1   /*
2    * RDFpro - An extensible tool for building stream-oriented RDF processing libraries.
3    * 
4    * Written in 2014 by Francesco Corcoglioniti with support by Marco Amadori, Michele Mostarda,
5    * Alessio Palmero Aprosio and Marco Rospocher. Contact info on http://rdfpro.fbk.eu/
6    * 
7    * To the extent possible under law, the authors have dedicated all copyright and related and
8    * neighboring rights to this software to the public domain worldwide. This software is
9    * distributed without any warranty.
10   * 
11   * You should have received a copy of the CC0 Public Domain Dedication along with this software.
12   * If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13   */
14  package eu.fbk.rdfpro.jsonld;
15  
16  import java.io.IOException;
17  import java.io.InputStream;
18  import java.io.InputStreamReader;
19  import java.io.Reader;
20  import java.nio.charset.Charset;
21  
22  import org.openrdf.model.Resource;
23  import org.openrdf.model.Statement;
24  import org.openrdf.model.URI;
25  import org.openrdf.model.Value;
26  import org.openrdf.model.ValueFactory;
27  import org.openrdf.model.impl.ValueFactoryImpl;
28  import org.openrdf.rio.RDFFormat;
29  import org.openrdf.rio.RDFHandler;
30  import org.openrdf.rio.RDFHandlerException;
31  import org.openrdf.rio.RDFParseException;
32  import org.openrdf.rio.helpers.RDFParserBase;
33  import org.semarglproject.jsonld.JsonLdParser;
34  import org.semarglproject.rdf.ParseException;
35  import org.semarglproject.sink.CharSink;
36  import org.semarglproject.sink.QuadSink;
37  
38  /**
39   * A parser that can parse RDF documents that are in the JSON-LD format.
40   * <p>
41   * JSON-LD is a JSON-based format for serializing data in (a superset of) RDF as JSON and
42   * interpreting JSON contents as RDF. See http://www.w3.org/TR/json-ld/ for the format
43   * specification.
44   * </p>
45   * <p>
46   * This implementation wraps the parser provided by the SEMARGL project -
47   * http://semarglproject.org/, adapting it to the Sesame RIO API.
48   * </p>
49   */
50  public class JSONLDParser extends RDFParserBase {
51  
52      /**
53       * Creates a new JSONLDParser that will use a {@link ValueFactoryImpl} to create RDF model
54       * objects.
55       */
56      public JSONLDParser() {
57          super();
58      }
59  
60      /**
61       * Creates a new JSONLDParser that will use the supplied ValueFactory to create RDF model
62       * objects.
63       * 
64       * @param valueFactory
65       *            the ValueFactory to use
66       */
67      public JSONLDParser(final ValueFactory valueFactory) {
68          super(valueFactory);
69      }
70  
71      @Override
72      public RDFFormat getRDFFormat() {
73          return RDFFormat.JSONLD;
74      }
75  
76      @Override
77      public void parse(final InputStream in, final String baseURI) throws IOException,
78              RDFParseException, RDFHandlerException {
79          parse(new InputStreamReader(in, Charset.forName("UTF-8")), baseURI);
80      }
81  
82      @Override
83      public void parse(final Reader reader, final String baseURI) throws IOException,
84              RDFParseException, RDFHandlerException {
85  
86          final QuadSink sink = new SesameSink(this.rdfHandler, this.valueFactory);
87          try {
88              final CharSink parser = JsonLdParser.connect(sink);
89              parser.startStream();
90              final char[] buffer = new char[4096];
91              while (true) {
92                  final int length = reader.read(buffer);
93                  if (length < 0) {
94                      break;
95                  }
96                  parser.process(buffer, 0, length);
97              }
98              parser.endStream();
99          } catch (final ParseException ex) {
100             throw new RDFParseException(ex.getMessage(), ex);
101         }
102     }
103 
104     private static final class SesameSink implements QuadSink {
105 
106         private final RDFHandler handler;
107 
108         private final ValueFactory factory;
109 
110         SesameSink(final RDFHandler handler, final ValueFactory factory) {
111             this.handler = handler;
112             this.factory = factory;
113         }
114 
115         @Override
116         public void setBaseUri(final String baseUri) {
117         }
118 
119         @Override
120         public boolean setProperty(final String key, final Object value) {
121             return false;
122         }
123 
124         @Override
125         public void startStream() throws ParseException {
126             try {
127                 this.handler.startRDF();
128             } catch (final RDFHandlerException e) {
129                 throw new ParseException(e);
130             }
131         }
132 
133         @Override
134         public void addNonLiteral(final String subj, final String pred, final String obj) {
135             emit(subj, pred, obj, false, null, null, null);
136         }
137 
138         @Override
139         public void addPlainLiteral(final String subj, final String pred, final String obj,
140                 final String lang) {
141             emit(subj, pred, obj, true, lang, null, null);
142         }
143 
144         @Override
145         public void addTypedLiteral(final String subj, final String pred, final String obj,
146                 final String dt) {
147             emit(subj, pred, obj, true, null, dt, null);
148         }
149 
150         @Override
151         public void addNonLiteral(final String subj, final String pred, final String obj,
152                 final String ctx) {
153             emit(subj, pred, obj, false, null, null, ctx);
154         }
155 
156         @Override
157         public void addPlainLiteral(final String subj, final String pred, final String obj,
158                 final String lang, final String ctx) {
159             emit(subj, pred, obj, true, lang, null, ctx);
160         }
161 
162         @Override
163         public void addTypedLiteral(final String subj, final String pred, final String obj,
164                 final String dt, final String ctx) {
165             emit(subj, pred, obj, true, null, dt, ctx);
166         }
167 
168         @Override
169         public void endStream() throws ParseException {
170             try {
171                 this.handler.endRDF();
172             } catch (final RDFHandlerException e) {
173                 throw new ParseException(e);
174             }
175         }
176 
177         private void emit(final String subj, final String pred, final String obj,
178                 final boolean literal, final String lang, final String dt, final String ctx) {
179 
180             final Resource s = subj.startsWith("_:") ? this.factory.createBNode(subj.substring(2))
181                     : this.factory.createURI(subj);
182 
183             final URI p = this.factory.createURI(pred);
184 
185             final Value o;
186             if (!literal) {
187                 o = obj.startsWith("_:") ? this.factory.createBNode(obj.substring(2))
188                         : this.factory.createURI(obj);
189             } else if (lang != null) {
190                 o = this.factory.createLiteral(obj, lang);
191             } else if (dt != null) {
192                 o = this.factory.createLiteral(obj, this.factory.createURI(dt));
193             } else {
194                 o = this.factory.createLiteral(obj);
195             }
196 
197             Statement stmt = null;
198             if (ctx == null) {
199                 stmt = this.factory.createStatement(s, p, o);
200             } else {
201                 final Resource c = ctx.startsWith("_:") ? this.factory.createBNode(ctx
202                         .substring(2)) : this.factory.createURI(ctx);
203                 stmt = this.factory.createStatement(s, p, o, c);
204             }
205 
206             try {
207                 this.handler.handleStatement(stmt);
208             } catch (final RDFHandlerException ex) {
209                 throw new RuntimeException(ex);
210             }
211         }
212 
213     }
214 
215 }