1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.rdf.simple.experimental;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.net.URI;
23 import java.nio.file.Files;
24 import java.nio.file.Path;
25 import java.util.Optional;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.Executors;
28 import java.util.concurrent.Future;
29 import java.util.function.Consumer;
30
31 import org.apache.commons.rdf.api.Dataset;
32 import org.apache.commons.rdf.api.Graph;
33 import org.apache.commons.rdf.api.IRI;
34 import org.apache.commons.rdf.api.Quad;
35 import org.apache.commons.rdf.api.RDFSyntax;
36 import org.apache.commons.rdf.api.RDF;
37 import org.apache.commons.rdf.experimental.RDFParser;
38 import org.apache.commons.rdf.simple.SimpleRDF;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public abstract class AbstractRDFParser<T extends AbstractRDFParser<T>> implements RDFParser, Cloneable {
60
61 public static final ThreadGroup threadGroup = new ThreadGroup("Commons RDF parsers");
62 private static final ExecutorService threadpool = Executors.newCachedThreadPool(r -> new Thread(threadGroup, r));
63
64
65 private static RDF internalRdfTermFactory = new SimpleRDF();
66
67
68
69
70
71
72
73 public Optional<RDF> getRdfTermFactory() {
74 return rdfTermFactory;
75 }
76
77
78
79
80
81
82
83
84
85
86 public Optional<RDFSyntax> getContentTypeSyntax() {
87 return contentTypeSyntax;
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101 public final Optional<String> getContentType() {
102 return contentType;
103 }
104
105
106
107
108
109
110
111
112
113
114
115 public Consumer<Quad> getTarget() {
116 return target;
117 }
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public Optional<Dataset> getTargetDataset() {
134 return targetDataset;
135 }
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 public Optional<Graph> getTargetGraph() {
152 return targetGraph;
153 }
154
155
156
157
158
159
160
161 public Optional<IRI> getBase() {
162 return base;
163 }
164
165
166
167
168
169
170
171
172
173
174 public Optional<InputStream> getSourceInputStream() {
175 return sourceInputStream;
176 }
177
178
179
180
181
182
183
184
185
186
187
188 public Optional<Path> getSourceFile() {
189 return sourceFile;
190 }
191
192
193
194
195
196
197
198
199
200
201
202 public Optional<IRI> getSourceIri() {
203 return sourceIri;
204 }
205
206 private Optional<RDF> rdfTermFactory = Optional.empty();
207 private Optional<RDFSyntax> contentTypeSyntax = Optional.empty();
208 private Optional<String> contentType = Optional.empty();
209 private Optional<IRI> base = Optional.empty();
210 private Optional<InputStream> sourceInputStream = Optional.empty();
211 private Optional<Path> sourceFile = Optional.empty();
212 private Optional<IRI> sourceIri = Optional.empty();
213 private Consumer<Quad> target;
214 private Optional<Dataset> targetDataset;
215 private Optional<Graph> targetGraph;
216
217 @SuppressWarnings("unchecked")
218 @Override
219 public T clone() {
220 try {
221 return (T) super.clone();
222 } catch (final CloneNotSupportedException e) {
223 throw new RuntimeException(e);
224 }
225 }
226
227 @SuppressWarnings("unchecked")
228 protected T asT() {
229 return (T) this;
230 }
231
232 @Override
233 public T rdfTermFactory(final RDF rdfTermFactory) {
234 final AbstractRDFParser<T> c = clone();
235 c.rdfTermFactory = Optional.ofNullable(rdfTermFactory);
236 return c.asT();
237 }
238
239 @Override
240 public T contentType(final RDFSyntax rdfSyntax) throws IllegalArgumentException {
241 final AbstractRDFParser<T> c = clone();
242 c.contentTypeSyntax = Optional.ofNullable(rdfSyntax);
243 c.contentType = c.contentTypeSyntax.map(syntax -> syntax.mediaType);
244 return c.asT();
245 }
246
247 @Override
248 public T contentType(final String contentType) throws IllegalArgumentException {
249 final AbstractRDFParser<T> c = clone();
250 c.contentType = Optional.ofNullable(contentType);
251 c.contentTypeSyntax = c.contentType.flatMap(RDFSyntax::byMediaType);
252 return c.asT();
253 }
254
255 @Override
256 public T base(final IRI base) {
257 final AbstractRDFParser<T> c = clone();
258 c.base = Optional.ofNullable(base);
259 c.base.ifPresent(i -> checkIsAbsolute(i));
260 return c.asT();
261 }
262
263 @Override
264 public T base(final String base) throws IllegalArgumentException {
265 return base(internalRdfTermFactory.createIRI(base));
266 }
267
268 @Override
269 public T source(final InputStream inputStream) {
270 final AbstractRDFParser<T> c = clone();
271 c.resetSource();
272 c.sourceInputStream = Optional.ofNullable(inputStream);
273 return c.asT();
274 }
275
276 @Override
277 public T source(final Path file) {
278 final AbstractRDFParser<T> c = clone();
279 c.resetSource();
280 c.sourceFile = Optional.ofNullable(file);
281 return c.asT();
282 }
283
284 @Override
285 public T source(final IRI iri) {
286 final AbstractRDFParser<T> c = clone();
287 c.resetSource();
288 c.sourceIri = Optional.ofNullable(iri);
289 c.sourceIri.ifPresent(i -> checkIsAbsolute(i));
290 return c.asT();
291 }
292
293 @Override
294 public T source(final String iri) throws IllegalArgumentException {
295 final AbstractRDFParser<T> c = clone();
296 c.resetSource();
297 c.sourceIri = Optional.ofNullable(iri).map(internalRdfTermFactory::createIRI);
298 c.sourceIri.ifPresent(i -> checkIsAbsolute(i));
299 return source(internalRdfTermFactory.createIRI(iri));
300 }
301
302
303
304
305
306
307
308
309
310
311
312 protected void checkIsAbsolute(final IRI iri) throws IllegalArgumentException {
313 if (!URI.create(iri.getIRIString()).isAbsolute()) {
314 throw new IllegalArgumentException("IRI is not absolute: " + iri);
315 }
316 }
317
318
319
320
321
322
323
324
325
326
327
328
329 protected void checkSource() throws IOException {
330 if (!sourceFile.isPresent() && !sourceInputStream.isPresent() && !sourceIri.isPresent()) {
331 throw new IllegalStateException("No source has been set");
332 }
333 if (sourceIri.isPresent() && sourceInputStream.isPresent()) {
334 throw new IllegalStateException("Both sourceIri and sourceInputStream have been set");
335 }
336 if (sourceIri.isPresent() && sourceFile.isPresent()) {
337 throw new IllegalStateException("Both sourceIri and sourceFile have been set");
338 }
339 if (sourceInputStream.isPresent() && sourceFile.isPresent()) {
340 throw new IllegalStateException("Both sourceInputStream and sourceFile have been set");
341 }
342 if (sourceFile.isPresent() && !sourceFile.filter(Files::isReadable).isPresent()) {
343 throw new IOException("Can't read file: " + sourceFile);
344 }
345 }
346
347
348
349
350
351
352
353 protected void checkBaseRequired() throws IllegalStateException {
354 if (!base.isPresent() && sourceInputStream.isPresent()
355 && !contentTypeSyntax.filter(t -> t == RDFSyntax.NQUADS || t == RDFSyntax.NTRIPLES).isPresent()) {
356 throw new IllegalStateException("base iri required for inputstream source");
357 }
358 }
359
360
361
362
363
364
365
366
367 protected void resetSource() {
368 sourceInputStream = Optional.empty();
369 sourceIri = Optional.empty();
370 sourceFile = Optional.empty();
371 }
372
373
374
375
376
377
378
379
380
381
382
383 protected void resetTarget() {
384 targetDataset = Optional.empty();
385 targetGraph = Optional.empty();
386 }
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404 protected abstract void parseSynchronusly() throws IOException, RDFParseException;
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423 protected T prepareForParsing() throws IOException, IllegalStateException {
424 checkSource();
425 checkBaseRequired();
426 checkContentType();
427 checkTarget();
428
429
430
431 final AbstractRDFParser<T> c = clone();
432
433
434 if (!c.rdfTermFactory.isPresent()) {
435 c.rdfTermFactory = Optional.of(createRDFTermFactory());
436 }
437
438
439 if (c.sourceFile.isPresent() && !c.base.isPresent()) {
440 final URI baseUri = c.sourceFile.get().toRealPath().toUri();
441 c.base = Optional.of(internalRdfTermFactory.createIRI(baseUri.toString()));
442 }
443
444 return c.asT();
445 }
446
447
448
449
450
451
452
453 protected void checkTarget() {
454 if (target == null) {
455 throw new IllegalStateException("target has not been set");
456 }
457 if (targetGraph.isPresent() && targetDataset.isPresent()) {
458
459
460 throw new IllegalStateException("targetGraph and targetDataset can't both be set");
461 }
462 }
463
464
465
466
467
468
469
470
471
472 protected void checkContentType() throws IllegalStateException {
473 }
474
475
476
477
478
479
480
481
482
483
484
485
486
487 protected static Optional<RDFSyntax> guessRDFSyntax(final Path path) {
488 return fileExtension(path).flatMap(RDFSyntax::byFileExtension);
489 }
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504 private static Optional<String> fileExtension(final Path path) {
505 final Path fileName = path.getFileName();
506 if (fileName == null) {
507 return Optional.empty();
508 }
509 final String filenameStr = fileName.toString();
510 final int last = filenameStr.lastIndexOf(".");
511 if (last > -1) {
512 return Optional.of(filenameStr.substring(last));
513 }
514 return Optional.empty();
515 }
516
517
518
519
520
521
522
523
524
525
526
527
528
529 protected RDF createRDFTermFactory() {
530 return new SimpleRDF();
531 }
532
533 @Override
534 public Future<ParseResult> parse() throws IOException, IllegalStateException {
535 final AbstractRDFParser<T> c = prepareForParsing();
536 return threadpool.submit(() -> {
537 c.parseSynchronusly();
538 return null;
539 });
540 }
541
542 @Override
543 public T target(final Consumer<Quad> consumer) {
544 final AbstractRDFParser<T> c = clone();
545 c.resetTarget();
546 c.target = consumer;
547 return c.asT();
548 }
549
550 @Override
551 public T target(final Dataset dataset) {
552 @SuppressWarnings({ "rawtypes", "unchecked" })
553 final
554 AbstractRDFParser<T> c = (AbstractRDFParser) RDFParser.super.target(dataset);
555 c.resetTarget();
556 c.targetDataset = Optional.of(dataset);
557 return c.asT();
558 }
559
560 @Override
561 public T target(final Graph graph) {
562 @SuppressWarnings({ "rawtypes", "unchecked" })
563 final
564
565 AbstractRDFParser<T> c = (AbstractRDFParser) RDFParser.super.target(graph);
566 c.resetTarget();
567 c.targetGraph = Optional.of(graph);
568 return c.asT();
569 }
570
571 }