1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.any23.rdf;
19
20 import org.apache.any23.util.MathUtils;
21 import org.apache.any23.util.StringUtils;
22 import org.eclipse.rdf4j.model.BNode;
23 import org.eclipse.rdf4j.model.IRI;
24 import org.eclipse.rdf4j.model.Literal;
25 import org.eclipse.rdf4j.model.Resource;
26 import org.eclipse.rdf4j.model.Statement;
27 import org.eclipse.rdf4j.model.Value;
28 import org.eclipse.rdf4j.model.ValueFactory;
29 import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
30 import org.eclipse.rdf4j.model.vocabulary.RDF;
31 import org.eclipse.rdf4j.rio.RDFFormat;
32 import org.eclipse.rdf4j.rio.RDFParser;
33 import org.eclipse.rdf4j.rio.RDFParserRegistry;
34 import org.eclipse.rdf4j.rio.RDFWriter;
35 import org.eclipse.rdf4j.rio.Rio;
36 import org.eclipse.rdf4j.rio.helpers.BasicParserSettings;
37 import org.eclipse.rdf4j.rio.helpers.StatementCollector;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 import javax.xml.datatype.DatatypeConfigurationException;
42 import javax.xml.datatype.DatatypeFactory;
43 import javax.xml.datatype.XMLGregorianCalendar;
44 import java.io.ByteArrayInputStream;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.io.OutputStream;
48 import java.io.Writer;
49 import java.net.URISyntaxException;
50 import java.nio.charset.StandardCharsets;
51 import java.text.ParseException;
52 import java.text.SimpleDateFormat;
53 import java.util.Collection;
54 import java.util.Date;
55 import java.util.GregorianCalendar;
56 import java.util.Locale;
57 import java.util.Optional;
58 import java.util.TimeZone;
59
60 /**
61 * Basic class providing a set of utility methods when dealing with <i>RDF</i>.
62 *
63 * @author Michele Mostarda (mostarda@fbk.eu)
64 * @author Davide Palmisano (dpalmisano@gmail.com)
65 * @author Jacek Grzebyta (jgrzebyta@apache.org)
66 */
67 public class RDFUtils {
68
69 private static int nodeId = 0;
70
71 private static final ValueFactory valueFactory = SimpleValueFactory.getInstance();
72
73 private static final Logger LOG = LoggerFactory.getLogger(RDFUtils.class);
74
75 private static final Statement[] EMPTY_STATEMENTS = new Statement[0];
76
77 private RDFUtils() {
78 }
79
80 /**
81 * Fixes typical errors in an absolute org.eclipse.rdf4j.model.IRI, such as unescaped spaces.
82 *
83 * @param uri
84 * An absolute org.eclipse.rdf4j.model.IRI, can have typical syntax errors
85 *
86 * @return An absolute org.eclipse.rdf4j.model.IRI that is valid against the org.eclipse.rdf4j.model.IRI syntax
87 *
88 * @throws IllegalArgumentException
89 * if org.eclipse.rdf4j.model.IRI is not fixable
90 */
91 public static String fixAbsoluteIRI(String uri) {
92 String fixed = fixIRIWithException(uri);
93 if (!fixed.matches("[a-zA-Z0-9]+:/.*"))
94 throw new IllegalArgumentException("not a absolute org.eclipse.rdf4j.model.IRI: " + uri);
95 // Add trailing slash if org.eclipse.rdf4j.model.IRI has only authority but no path.
96 if (fixed.matches("https?://[a-zA-Z0-9.-]+(:[0-9+])?")) {
97 fixed = fixed + "/";
98 }
99 return fixed;
100 }
101
102 /**
103 * This method allows to obtain an <a href="http://www.w3.org/TR/xmlschema-2/#date">XML Schema</a> compliant date
104 * providing a textual representation of a date and textual a pattern for parsing it.
105 *
106 * @param dateToBeParsed
107 * the String containing the date.
108 * @param format
109 * the pattern as descibed in {@link java.text.SimpleDateFormat}
110 *
111 * @return a {@link String} representing the date
112 *
113 * @throws java.text.ParseException
114 * if there is an error parsing the given date.
115 * @throws javax.xml.datatype.DatatypeConfigurationException
116 * if there is a serious configuration error.
117 */
118 public static String getXSDDate(String dateToBeParsed, String format)
119 throws ParseException, DatatypeConfigurationException {
120 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format, Locale.ROOT);
121 Date date = simpleDateFormat.parse(dateToBeParsed);
122 GregorianCalendar gc = new GregorianCalendar(TimeZone.getDefault(), Locale.ROOT);
123 gc.setTime(date);
124 XMLGregorianCalendar xml = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
125 xml.setTimezone(0);
126 return xml.toString();
127 }
128
129 /**
130 * Prints a <code>date</code> to the XSD datetime format.
131 *
132 * @param date
133 * date to be printed.
134 *
135 * @return the string representation of the input date.
136 */
137 public static String toXSDDateTime(Date date) {
138 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ROOT);
139 String s = simpleDateFormat.format(date);
140 StringBuilder sb = new StringBuilder(s);
141 sb.insert(22, ':');
142 return sb.toString();
143 }
144
145 /**
146 * <p>
147 * Tries to fix a potentially broken relative or absolute URI.
148 * </p>
149 * These appear to be good rules: Remove whitespace or '\' or '"' in beginning and end Replace space with %20 Drop
150 * the triple if it matches this regex (only protocol): ^[a-zA-Z0-9]+:(//)?$ Drop the triple if it matches this
151 * regex: ^javascript: Truncate ">.*$ from end of lines (Neko didn't quite manage to fix broken markup) Drop the
152 * triple if any of these appear in the URL: <>[]|*{}"<>\
153 *
154 * @param unescapedIRI
155 * uri string to be unescaped.
156 *
157 * @return the unescaped string.
158 */
159 public static String fixIRIWithException(String unescapedIRI) {
160 if (unescapedIRI == null)
161 throw new IllegalArgumentException("org.eclipse.rdf4j.model.IRI was null");
162
163 // Remove starting and ending whitespace
164 String escapedIRI = unescapedIRI.trim();
165
166 // Replace space with %20
167 escapedIRI = escapedIRI.replaceAll(" ", "%20");
168
169 // strip linebreaks
170 escapedIRI = escapedIRI.replaceAll("\n", "");
171
172 // 'Remove starting "\" or '"'
173 if (escapedIRI.startsWith("\\") || escapedIRI.startsWith("\""))
174 escapedIRI = escapedIRI.substring(1);
175 // Remove ending "\" or '"'
176 if (escapedIRI.endsWith("\\") || escapedIRI.endsWith("\""))
177 escapedIRI = escapedIRI.substring(0, escapedIRI.length() - 1);
178
179 // Drop the triple if it matches this regex (only protocol): ^[a-zA-Z0-9]+:/?/?$
180 if (escapedIRI.matches("^[a-zA-Z0-9]+:/?/?$"))
181 throw new IllegalArgumentException("no authority in org.eclipse.rdf4j.model.IRI: " + unescapedIRI);
182
183 // Drop the triple if it matches this regex: ^javascript:
184 if (escapedIRI.matches("^javascript:"))
185 throw new IllegalArgumentException("org.eclipse.rdf4j.model.IRI starts with javascript: " + unescapedIRI);
186
187 // stripHTML
188 // escapedIRI = escapedIRI.replaceAll("\\<.*?\\>", "");
189
190 // >.*$ from end of lines (Neko didn't quite manage to fix broken markup)
191 escapedIRI = escapedIRI.replaceAll(">.*$", "");
192
193 // Drop the triple if any of these appear in the URL: <>[]|*{}"<>\
194 if (escapedIRI.matches("[<>\\[\\]|\\*\\{\\}\"\\\\]"))
195 throw new IllegalArgumentException("Invalid character in org.eclipse.rdf4j.model.IRI: " + unescapedIRI);
196
197 return escapedIRI;
198 }
199
200 /**
201 * Creates a {@link org.eclipse.rdf4j.model.IRI}.
202 *
203 * @param iri
204 * a base string for the {@link org.eclipse.rdf4j.model.IRI}
205 *
206 * @return a valid {@link org.eclipse.rdf4j.model.IRI}
207 */
208 public static org.eclipse.rdf4j.model.IRI iri(String iri) {
209 return valueFactory.createIRI(iri);
210 }
211
212 /**
213 * Creates a {@link org.eclipse.rdf4j.model.IRI}.
214 *
215 * @param namespace
216 * a base namespace for the {@link org.eclipse.rdf4j.model.IRI}
217 * @param localName
218 * a local name to associate with the namespace
219 *
220 * @return a valid {@link org.eclipse.rdf4j.model.IRI}
221 */
222 public static org.eclipse.rdf4j.model.IRI iri(String namespace, String localName) {
223 return valueFactory.createIRI(namespace, localName);
224 }
225
226 /**
227 * Creates a {@link Literal}.
228 *
229 * @param s
230 * string representation of the {@link org.eclipse.rdf4j.model.Literal}
231 *
232 * @return valid {@link org.eclipse.rdf4j.model.Literal}
233 */
234 public static Literal literal(String s) {
235 return valueFactory.createLiteral(s);
236 }
237
238 /**
239 * Creates a {@link Literal}.
240 *
241 * @param b
242 * boolean representation of the {@link org.eclipse.rdf4j.model.Literal}
243 *
244 * @return valid {@link org.eclipse.rdf4j.model.Literal}
245 */
246 public static Literal literal(boolean b) {
247 return valueFactory.createLiteral(b);
248 }
249
250 /**
251 * Creates a {@link Literal}.
252 *
253 * @param b
254 * byte representation of the {@link org.eclipse.rdf4j.model.Literal}
255 *
256 * @return valid {@link org.eclipse.rdf4j.model.Literal}
257 */
258 public static Literal literal(byte b) {
259 return valueFactory.createLiteral(b);
260 }
261
262 /**
263 * Creates a {@link Literal}.
264 *
265 * @param s
266 * short representation of the {@link org.eclipse.rdf4j.model.Literal}
267 *
268 * @return valid {@link org.eclipse.rdf4j.model.Literal}
269 */
270 public static Literal literal(short s) {
271 return valueFactory.createLiteral(s);
272 }
273
274 /**
275 * Creates a {@link Literal}.
276 *
277 * @param i
278 * int representation of the {@link org.eclipse.rdf4j.model.Literal}
279 *
280 * @return valid {@link org.eclipse.rdf4j.model.Literal}
281 */
282 public static Literal literal(int i) {
283 return valueFactory.createLiteral(i);
284 }
285
286 /**
287 * Creates a {@link Literal}.
288 *
289 * @param l
290 * long representation of the {@link org.eclipse.rdf4j.model.Literal}
291 *
292 * @return valid {@link org.eclipse.rdf4j.model.Literal}
293 */
294 public static Literal literal(long l) {
295 return valueFactory.createLiteral(l);
296 }
297
298 /**
299 * Creates a {@link Literal}.
300 *
301 * @param f
302 * float representation of the {@link org.eclipse.rdf4j.model.Literal}
303 *
304 * @return valid {@link org.eclipse.rdf4j.model.Literal}
305 */
306 public static Literal literal(float f) {
307 return valueFactory.createLiteral(f);
308 }
309
310 /**
311 * Creates a {@link Literal}.
312 *
313 * @param d
314 * double representation of the {@link org.eclipse.rdf4j.model.Literal}
315 *
316 * @return valid {@link org.eclipse.rdf4j.model.Literal}
317 */
318 public static Literal literal(double d) {
319 return valueFactory.createLiteral(d);
320 }
321
322 /**
323 * Creates a {@link Literal}.
324 *
325 * @param s
326 * the literal's label
327 * @param l
328 * the literal's language
329 *
330 * @return valid {@link org.eclipse.rdf4j.model.Literal}
331 */
332 public static Literal literal(String s, String l) {
333 if (l == null) {
334 // HACK: Workaround for ANY23 code that passes null in for language tag
335 return valueFactory.createLiteral(s);
336 } else {
337 return valueFactory.createLiteral(s, l);
338 }
339 }
340
341 /**
342 * Creates a {@link Literal}.
343 *
344 * @param s
345 * the literal's label
346 * @param datatype
347 * the literal's datatype
348 *
349 * @return valid {@link org.eclipse.rdf4j.model.Literal}
350 */
351 public static Literal literal(String s, org.eclipse.rdf4j.model.IRI datatype) {
352 return valueFactory.createLiteral(s, datatype);
353 }
354
355 /**
356 * Creates a {@link BNode}.
357 *
358 * @param id
359 * string representation of the {@link org.eclipse.rdf4j.model.BNode}
360 *
361 * @return the valid {@link org.eclipse.rdf4j.model.BNode}
362 */
363 // TODO: replace this with all occurrences of #getBNode()
364 public static BNode bnode(String id) {
365 return valueFactory.createBNode(id);
366 }
367
368 /**
369 * @return a <code>bnode</code> with unique id.
370 */
371 public static BNode bnode() {
372 return valueFactory.createBNode();
373 }
374
375 /**
376 * Creates a {@link BNode} with an MD5 digest as part of the ID.
377 *
378 * @param id
379 * string representation of the {@link org.eclipse.rdf4j.model.BNode} name for which we will create a md5
380 * hash.
381 *
382 * @return the valid {@link org.eclipse.rdf4j.model.BNode}
383 */
384 public static BNode getBNode(String id) {
385 return valueFactory.createBNode("node" + MathUtils.md5(id));
386 }
387
388 /**
389 * Creates a {@link Statement}.
390 *
391 * @param s
392 * subject {@link org.eclipse.rdf4j.model.Resource}
393 * @param p
394 * predicate {@link org.eclipse.rdf4j.model.URI}
395 * @param o
396 * object {@link org.eclipse.rdf4j.model.Value}
397 *
398 * @return valid {@link org.eclipse.rdf4j.model.Statement}
399 */
400 public static Statement triple(Resource s, org.eclipse.rdf4j.model.IRI p, Value o) {
401 return valueFactory.createStatement(s, p, o);
402 }
403
404 /**
405 * Creates a statement of type: <code>toValue(s), toValue(p), toValue(o)</code>
406 *
407 * @param s
408 * subject.
409 * @param p
410 * predicate.
411 * @param o
412 * object.
413 *
414 * @return a statement instance.
415 */
416 public static Statement triple(String s, String p, String o) {
417 return valueFactory.createStatement((Resource) toValue(s), (org.eclipse.rdf4j.model.IRI) toValue(p),
418 toValue(o));
419 }
420
421 /**
422 * Creates a {@link Statement}.
423 *
424 * @param s
425 * subject.
426 * @param p
427 * predicate.
428 * @param o
429 * object.
430 * @param g
431 * quad resource
432 *
433 * @return a statement instance.
434 */
435 public static Statement quad(Resource s, org.eclipse.rdf4j.model.IRI p, Value o, Resource g) {
436 return valueFactory.createStatement(s, p, o, g);
437 }
438
439 /**
440 * Creates a statement of type: <code>toValue(s), toValue(p), toValue(o), toValue(g)</code>
441 *
442 * @param s
443 * subject.
444 * @param p
445 * predicate.
446 * @param o
447 * object.
448 * @param g
449 * quad resource
450 *
451 * @return a statement instance.
452 */
453 public static Statement quad(String s, String p, String o, String g) {
454 return valueFactory.createStatement((Resource) toValue(s), (org.eclipse.rdf4j.model.IRI) toValue(p), toValue(o),
455 (Resource) toValue(g));
456 }
457
458 /**
459 * Creates a {@link Value}. If <code>s == 'a'</code> returns an {@link RDF#TYPE}. If
460 * <code> s.matches('[a-z0-9]+:.*')</code> expands the corresponding prefix using {@link PopularPrefixes}.
461 *
462 * @param s
463 * string representation of value.
464 *
465 * @return a value instance.
466 */
467 public static Value toValue(String s) {
468 if ("a".equals(s))
469 return RDF.TYPE;
470 if (s.matches("[a-z0-9]+:.*")) {
471 return PopularPrefixes.get().expand(s);
472 }
473 return valueFactory.createLiteral(s);
474 }
475
476 /**
477 *
478 * Returns all the available {@link RDFFormat}s.
479 *
480 * @return an unmodifiable collection of formats.
481 */
482 public static Collection<RDFFormat> getFormats() {
483 return RDFParserRegistry.getInstance().getKeys();
484 }
485
486 /**
487 * Creates a new {@link RDFParser} instance.
488 *
489 * @param format
490 * parser format.
491 *
492 * @return parser instance.
493 *
494 * @throws IllegalArgumentException
495 * if format is not supported.
496 */
497 public static RDFParser getParser(RDFFormat format) {
498 return Rio.createParser(format);
499 }
500
501 /**
502 * Creates a new {@link RDFWriter} instance.
503 *
504 * @param format
505 * output format.
506 * @param writer
507 * data output writer.
508 *
509 * @return writer instance.
510 *
511 * @throws IllegalArgumentException
512 * if format is not supported.
513 */
514 public static RDFWriter getWriter(RDFFormat format, Writer writer) {
515 return Rio.createWriter(format, writer);
516 }
517
518 /**
519 * Creates a new {@link RDFWriter} instance.
520 *
521 * @param format
522 * output format.
523 * @param os
524 * output stream.
525 *
526 * @return writer instance.
527 *
528 * @throws IllegalArgumentException
529 * if format is not supported.
530 */
531 public static RDFWriter getWriter(RDFFormat format, OutputStream os) {
532 return Rio.createWriter(format, os);
533 }
534
535 /**
536 * Returns a parser type from the given extension.
537 *
538 * @param ext
539 * input extension.
540 *
541 * @return parser matching the extension.
542 *
543 * @throws IllegalArgumentException
544 * if no extension matches.
545 */
546 public static Optional<RDFFormat> getFormatByExtension(String ext) {
547 if (!ext.startsWith("."))
548 ext = "." + ext;
549 return Rio.getParserFormatForFileName(ext);
550 }
551
552 /**
553 * Parses the content of <code>is</code> input stream with the specified parser <code>p</code> using
554 * <code>baseIRI</code>.
555 *
556 * @param format
557 * input format type.
558 * @param is
559 * input stream containing <code>RDF</code>.
560 * @param baseIRI
561 * base uri.
562 *
563 * @return list of statements detected within the input stream.
564 *
565 * @throws IOException
566 * if there is an error reading the {@link java.io.InputStream}
567 */
568 public static Statement[] parseRDF(RDFFormat format, InputStream is, String baseIRI) throws IOException {
569 final StatementCollector handler = new StatementCollector();
570 final RDFParser parser = getParser(format);
571 parser.getParserConfig().set(BasicParserSettings.VERIFY_DATATYPE_VALUES, true);
572 parser.setPreserveBNodeIDs(true);
573 parser.setRDFHandler(handler);
574 parser.parse(is, baseIRI);
575 return handler.getStatements().toArray(EMPTY_STATEMENTS);
576 }
577
578 /**
579 * Parses the content of <code>is</code> input stream with the specified parser <code>p</code> using <code>''</code>
580 * as base org.eclipse.rdf4j.model.IRI.
581 *
582 * @param format
583 * input format type.
584 * @param is
585 * input stream containing <code>RDF</code>.
586 *
587 * @return list of statements detected within the input stream.
588 *
589 * @throws IOException
590 * if there is an error reading the {@link java.io.InputStream}
591 */
592 public static Statement[] parseRDF(RDFFormat format, InputStream is) throws IOException {
593 return parseRDF(format, is, "");
594 }
595
596 /**
597 * Parses the content of <code>in</code> string with the specified parser <code>p</code> using <code>''</code> as
598 * base org.eclipse.rdf4j.model.IRI.
599 *
600 * @param format
601 * input format type.
602 * @param in
603 * input string containing <code>RDF</code>.
604 *
605 * @return list of statements detected within the input string.
606 *
607 * @throws IOException
608 * if there is an error reading the {@link java.io.InputStream}
609 */
610 public static Statement[] parseRDF(RDFFormat format, String in) throws IOException {
611 return parseRDF(format, new ByteArrayInputStream(in.getBytes(StandardCharsets.UTF_8)));
612 }
613
614 /**
615 * Parses the content of the <code>resource</code> file guessing the content format from the extension.
616 *
617 * @param resource
618 * resource name.
619 *
620 * @return the statements declared within the resource file.
621 *
622 * @throws java.io.IOException
623 * if an error occurs while reading file.
624 */
625 public static Statement[] parseRDF(String resource) throws IOException {
626 final int extIndex = resource.lastIndexOf('.');
627 if (extIndex == -1)
628 throw new IllegalArgumentException("Error while detecting the extension in resource name " + resource);
629 final String extension = resource.substring(extIndex + 1);
630 return parseRDF(getFormatByExtension(extension).orElseThrow(Rio.unsupportedFormat(extension)),
631 RDFUtils.class.getResourceAsStream(resource));
632 }
633
634 /**
635 * Checks if <code>href</code> is absolute or not.
636 *
637 * @param href
638 * candidate org.eclipse.rdf4j.model.IRI.
639 *
640 * @return <code>true</code> if <code>href</code> is absolute, <code>false</code> otherwise.
641 */
642 public static boolean isAbsoluteIRI(String href) {
643 try {
644 SimpleValueFactory.getInstance().createIRI(href.trim());
645 new java.net.URI(href.trim());
646 return true;
647 } catch (IllegalArgumentException e) {
648 LOG.trace("Error processing href: {}", href, e);
649 return false;
650 } catch (URISyntaxException e) {
651 LOG.trace("Error interpreting href: {} as URI.", href, e);
652 return false;
653 }
654 }
655
656 /**
657 * {@link #makeIRI(java.lang.String, org.eclipse.rdf4j.model.IRI, boolean) }.
658 *
659 * @param docUri
660 * It is a namespace. If it ends with '/' character than stays unchanged otherwise the hash character '#'
661 * is added to the end.
662 *
663 * @return instance of {@link Resource}.
664 */
665 public static Resource makeIRI(IRI docUri) {
666 return makeIRI("node", docUri);
667 }
668
669 /**
670 * {@link #makeIRI(java.lang.String, org.eclipse.rdf4j.model.IRI, boolean) }.
671 *
672 * @param type
673 * This argument is converted following Java naming conventions with
674 * {@link StringUtils#implementJavaNaming(java.lang.String) }.
675 * @param docIRI
676 * It is a namespace. If it ends with '/' character than stays unchanged otherwise the hash character '#'
677 * is added to the end.
678 *
679 * @return instance of {@link Resource}.
680 */
681 public static Resource makeIRI(String type, IRI docIRI) {
682 return makeIRI(type, docIRI, false);
683 }
684
685 /**
686 * Creates implementation of {@link Resource} from given arguments: <i>type</i> and <i>docIRI</i>.
687 *
688 * <b>NB:</b> The Java Naming Conventions is described by
689 * <a href='http://www.geeksforgeeks.org/java-naming-conventions/'>GeeksForGeeks</a>.
690 *
691 * @param type
692 * This argument is converted following Java naming conventions with
693 * {@link StringUtils#implementJavaNaming(java.lang.String) }.
694 * @param docIRI
695 * It is a namespace. If it ends with '/' character than stays unchanged otherwise the hash character '#'
696 * is added to the end.
697 * @param addId
698 * If argument is <b>TRUE</b> than the node identifier is added to the end formated
699 * <code>'_{int}'</code>.
700 *
701 * @return instance of {@link Resource}.
702 */
703 public static Resource makeIRI(String type, IRI docIRI, boolean addId) {
704
705 // preprocess string: converts - -> _
706 // converts <space>: word1 word2 -> word1Word2
707 String newType = StringUtils.implementJavaNaming(type);
708
709 String iriString;
710 if (docIRI.toString().endsWith("/") || docIRI.toString().endsWith("#")) {
711 iriString = docIRI.toString() + newType;
712 } else {
713 iriString = docIRI.toString() + "#" + newType;
714 }
715
716 if (addId) {
717 iriString = iriString + "_" + Integer.toString(nodeId);
718 }
719
720 Resource node = RDFUtils.iri(iriString);
721 if (addId) {
722 nodeId++;
723 }
724 return node;
725 }
726
727 /**
728 * Convert string to either IRI or Literal.
729 *
730 * If string value expresses valid IRI than {@link IRI} is created. Otherwise method creates simple {@link Literal}
731 * xsd:string.
732 *
733 * @param inString
734 * an input string to manifest as {@link org.eclipse.rdf4j.model.Value}
735 *
736 * @return either {@link IRI} or {@link Literal}.
737 */
738 public static Value makeIRI(String inString) {
739 if (RDFUtils.isAbsoluteIRI(inString)) {
740 return RDFUtils.iri(inString);
741 } else {
742 return RDFUtils.literal(inString);
743 }
744 }
745
746 public static Value makeIRI() {
747 BNode bnode = bnode(Integer.toString(nodeId));
748 nodeId++;
749 return bnode;
750 }
751
752 }