001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 2007.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.model.util;
007:
008: import java.util.Iterator;
009: import java.util.LinkedHashSet;
010: import java.util.Set;
011:
012: import info.aduna.collections.iterators.ConvertingIterator;
013: import info.aduna.collections.iterators.Iterators;
014:
015: import org.openrdf.OpenRDFUtil;
016: import org.openrdf.model.Graph;
017: import org.openrdf.model.Literal;
018: import org.openrdf.model.Resource;
019: import org.openrdf.model.Statement;
020: import org.openrdf.model.URI;
021: import org.openrdf.model.Value;
022:
023: /**
024: * Utility methods for working with {@link Graph} objects.
025: *
026: * @author Arjohn Kampman
027: */
028: public class GraphUtil {
029:
030: /**
031: * Gets the subject of the statements with the specified predicate, object
032: * and (optionally) contexts from the supplied graph. Calling this method is
033: * equivalent to calling <tt>graph.match(null, pred, obj, contexts)</tt>
034: * and extracting the subjects of the matching statements from the returned
035: * iterator. See {@link Graph#match(Resource, URI, Value, Resource[])} for a
036: * description of the parameter values.
037: */
038: public static Iterator<Resource> getSubjectIterator(Graph graph,
039: URI pred, Value obj, Resource... contexts) {
040: Iterator<Statement> iter = graph.match(null, pred, obj,
041: contexts);
042:
043: return new ConvertingIterator<Statement, Resource>(iter) {
044:
045: @Override
046: protected Resource convert(Statement st)
047: throws RuntimeException {
048: return st.getSubject();
049: }
050:
051: };
052: }
053:
054: /**
055: * Gets the subject of the statements with the specified predicate, object
056: * and (optionally) contexts from the supplied graph. Calling this method is
057: * equivalent to calling <tt>graph.match(null, pred, obj, contexts)</tt>
058: * and adding the subjects of the matching statements to a set. See
059: * {@link Graph#match(Resource, URI, Value, Resource[])} for a description of
060: * the parameter values.
061: */
062: public static Set<Resource> getSubjects(Graph graph, URI pred,
063: Value obj, Resource... contexts) {
064: Iterator<Resource> iter = getSubjectIterator(graph, pred, obj,
065: contexts);
066: return Iterators.addAll(iter, new LinkedHashSet<Resource>());
067: }
068:
069: /**
070: * Gets the subject of the statement(s) with the specified predicate and
071: * object from the specified contexts in the supplied graph. The combination
072: * of predicate, object and contexts must match at least one statement. In
073: * case more than one statement matches -- for example statements from
074: * multiple contexts -- all these statements should have the same subject. A
075: * {@link GraphUtilException} is thrown if these conditions are not met. See
076: * {@link Graph#match(Resource, URI, Value, Resource[])} for a description of
077: * the parameter values.
078: *
079: * @return The subject of the matched statement(s).
080: * @throws GraphUtilException
081: * If the statements matched by the specified parameters do not have
082: * exactly one unique subject.
083: */
084: public static Resource getUniqueSubject(Graph graph, URI pred,
085: Value obj, Resource... contexts) throws GraphUtilException {
086: Set<Resource> subjects = getSubjects(graph, pred, obj, contexts);
087:
088: if (subjects.size() == 1) {
089: return subjects.iterator().next();
090: } else if (subjects.isEmpty()) {
091: throw new GraphUtilException("Missing property: " + pred);
092: } else {
093: throw new GraphUtilException("Multiple " + pred
094: + " properties found");
095: }
096: }
097:
098: /**
099: * Utility method that casts the return value of
100: * {@link #getUniqueSubject(Graph, URI, Value, Resource[])} to a URI, or
101: * throws a GraphUtilException if that value is not a URI.
102: *
103: * @return The subject of the matched statement(s).
104: * @throws GraphUtilException
105: * If such an exception is thrown by
106: * {@link #getUniqueSubject(Graph, URI, Value, Resource[])} or if its
107: * return value is not a URI.
108: */
109: public static URI getUniqueSubjectURI(Graph graph, URI pred,
110: Value obj, Resource... contexts) throws GraphUtilException {
111: Resource subject = getUniqueSubject(graph, pred, obj, contexts);
112:
113: if (subject instanceof URI) {
114: return (URI) subject;
115: } else {
116: throw new GraphUtilException("Expected URI for subject "
117: + subject);
118: }
119: }
120:
121: /**
122: * Gets the subject of the statement(s) with the specified predicate and
123: * object from the specified contexts in the supplied graph. If the
124: * combination of predicate, object and contexts matches one or more
125: * statements, all these statements should have the same subject. A
126: * {@link RepositoryConfigException} is thrown if this is not the case. See
127: * {@link Graph#match(Resource, URI, Value, Resource[])} for a description of
128: * the parameter values.
129: *
130: * @return The subject of the matched statement(s), or <tt>null</tt> if no
131: * matching statements were found.
132: * @throws GraphUtilException
133: * If the statements matched by the specified parameters have more
134: * than one unique subject.
135: */
136: public static Resource getOptionalSubject(Graph graph, URI pred,
137: Value obj, Resource... contexts) throws GraphUtilException {
138: Set<Resource> subjects = getSubjects(graph, pred, obj, contexts);
139:
140: if (subjects.isEmpty()) {
141: return null;
142: } else if (subjects.size() == 1) {
143: return subjects.iterator().next();
144: } else {
145: throw new GraphUtilException("Multiple " + pred
146: + " properties found");
147: }
148: }
149:
150: /**
151: * Utility method that casts the return value of
152: * {@link #getOptionalSubject(Graph, URI, Value, Resource[])} to a URI, or
153: * throws a GraphUtilException if that value is not a URI.
154: *
155: * @return The subject of the matched statement(s), or <tt>null</tt> if no
156: * matching statements were found.
157: * @throws GraphUtilException
158: * If such an exception is thrown by
159: * {@link #getOptionalSubject(Graph, URI, Value, Resource[])} or if
160: * its return value is not a URI.
161: */
162: public static URI getOptionalSubjectURI(Graph graph, URI pred,
163: Value obj, Resource... contexts) throws GraphUtilException {
164: Resource subject = getOptionalSubject(graph, pred, obj,
165: contexts);
166:
167: if (subject instanceof URI) {
168: return (URI) subject;
169: } else {
170: throw new GraphUtilException("Expected URI for subject "
171: + subject);
172: }
173: }
174:
175: /**
176: * Gets the objects of the statements with the specified subject, predicate
177: * and (optionally) contexts from the supplied graph. Calling this method is
178: * equivalent to calling <tt>graph.match(subj, pred, null, contexts)</tt>
179: * and extracting the objects of the matching statements from the returned
180: * iterator. See {@link Graph#match(Resource, URI, Value, Resource[])} for a
181: * description of the parameter values.
182: */
183: public static Iterator<Value> getObjectIterator(Graph graph,
184: Resource subj, URI pred, Resource... contexts) {
185: Iterator<Statement> iter = graph.match(subj, pred, null,
186: contexts);
187:
188: return new ConvertingIterator<Statement, Value>(iter) {
189:
190: @Override
191: protected Value convert(Statement st)
192: throws RuntimeException {
193: return st.getObject();
194: }
195:
196: };
197: }
198:
199: /**
200: * Gets the objects of the statements with the specified subject, predicate
201: * and (optionally) contexts from the supplied graph. Calling this method is
202: * equivalent to calling <tt>graph.match(subj, pred, null, contexts)</tt>
203: * and adding the objects of the matching statements to a set. See
204: * {@link Graph#match(Resource, URI, Value, Resource[])} for a description of
205: * the parameter values.
206: */
207: public static Set<Value> getObjects(Graph graph, Resource subj,
208: URI pred, Resource... contexts) {
209: Iterator<Value> iter = getObjectIterator(graph, subj, pred,
210: contexts);
211: return Iterators.addAll(iter, new LinkedHashSet<Value>());
212: }
213:
214: /**
215: * Gets the object of the statement(s) with the specified subject and
216: * predicate from the specified contexts in the supplied graph. The
217: * combination of subject, predicate and contexts must match at least one
218: * statement. In case more than one statement matches -- for example
219: * statements from multiple contexts -- all these statements should have the
220: * same object. A {@link GraphUtilException} is thrown if these conditions
221: * are not met. See {@link Graph#match(Resource, URI, Value, Resource[])} for
222: * a description of the parameter values.
223: *
224: * @return The object of the matched statement(s).
225: * @throws GraphUtilException
226: * If the statements matched by the specified parameters do not have
227: * exactly one unique object.
228: */
229: public static Value getUniqueObject(Graph graph, Resource subj,
230: URI pred, Resource... contexts) throws GraphUtilException {
231: Set<Value> objects = getObjects(graph, subj, pred, contexts);
232:
233: if (objects.size() == 1) {
234: return objects.iterator().next();
235: } else if (objects.isEmpty()) {
236: throw new GraphUtilException("Missing property: " + pred);
237: } else {
238: throw new GraphUtilException("Multiple " + pred
239: + " properties found");
240: }
241: }
242:
243: /**
244: * Adds the specified statement and makes sure that no other statements are
245: * present in the Graph with the same subject and predicate. When contexts
246: * are specified, the (subj, pred) pair will occur exactly once in each
247: * context, else the (subj, pred) pair will occur exactly once in the entire
248: * Graph.
249: */
250: public static void setUniqueObject(Graph graph, Resource subj,
251: URI pred, Value obj, Resource... contexts) {
252: Iterator<Statement> iter = graph.match(subj, pred, null,
253: contexts);
254:
255: while (iter.hasNext()) {
256: iter.next();
257: iter.remove();
258: }
259:
260: graph.add(subj, pred, obj, contexts);
261: }
262:
263: /**
264: * Utility method that casts the return value of
265: * {@link #getUniqueObject(Graph, Resource, URI, Resource[])} to a Resource,
266: * or throws a GraphUtilException if that value is not a Resource.
267: *
268: * @return The object of the matched statement(s).
269: * @throws GraphUtilException
270: * If such an exception is thrown by
271: * {@link #getUniqueObject(Graph, Resource, URI, Resource[])} or if
272: * its return value is not a Resource.
273: */
274: public static Resource getUniqueObjectResource(Graph graph,
275: Resource subj, URI pred) throws GraphUtilException {
276: Value obj = getUniqueObject(graph, subj, pred);
277:
278: if (obj instanceof Resource) {
279: return (Resource) obj;
280: } else {
281: throw new GraphUtilException(
282: "Expected URI or blank node for property " + pred);
283: }
284: }
285:
286: /**
287: * Utility method that casts the return value of
288: * {@link #getUniqueObject(Graph, Resource, URI, Resource[])} to a URI, or
289: * throws a GraphUtilException if that value is not a URI.
290: *
291: * @return The object of the matched statement(s).
292: * @throws GraphUtilException
293: * If such an exception is thrown by
294: * {@link #getUniqueObject(Graph, Resource, URI, Resource[])} or if
295: * its return value is not a URI.
296: */
297: public static URI getUniqueObjectURI(Graph graph, Resource subj,
298: URI pred) throws GraphUtilException {
299: Value obj = getUniqueObject(graph, subj, pred);
300:
301: if (obj instanceof URI) {
302: return (URI) obj;
303: } else {
304: throw new GraphUtilException("Expected URI for property "
305: + pred);
306: }
307: }
308:
309: /**
310: * Utility method that casts the return value of
311: * {@link #getUniqueObject(Graph, Resource, URI, Resource[])} to a Literal,
312: * or throws a GraphUtilException if that value is not a Literal.
313: *
314: * @return The object of the matched statement(s).
315: * @throws GraphUtilException
316: * If such an exception is thrown by
317: * {@link #getUniqueObject(Graph, Resource, URI, Resource[])} or if
318: * its return value is not a Literal.
319: */
320: public static Literal getUniqueObjectLiteral(Graph graph,
321: Resource subj, URI pred) throws GraphUtilException {
322: Value obj = getUniqueObject(graph, subj, pred);
323:
324: if (obj instanceof Literal) {
325: return (Literal) obj;
326: } else {
327: throw new GraphUtilException(
328: "Expected literal for property " + pred);
329: }
330: }
331:
332: /**
333: * Gets the object of the statement(s) with the specified subject and
334: * predicate from the specified contexts in the supplied graph. If the
335: * combination of subject, predicate and contexts matches one or more
336: * statements, all these statements should have the same object. A
337: * {@link RepositoryConfigException} is thrown if this is not the case. See
338: * {@link Graph#match(Resource, URI, Value, Resource[])} for a description of
339: * the parameter values.
340: *
341: * @return The object of the matched statement(s), or <tt>null</tt> if no
342: * matching statements were found.
343: * @throws GraphUtilException
344: * If the statements matched by the specified parameters have more
345: * than one unique object.
346: */
347: public static Value getOptionalObject(Graph graph, Resource subj,
348: URI pred, Resource... contexts) throws GraphUtilException {
349: Set<Value> objects = getObjects(graph, subj, pred, contexts);
350:
351: if (objects.isEmpty()) {
352: return null;
353: } else if (objects.size() == 1) {
354: return objects.iterator().next();
355: } else {
356: throw new GraphUtilException("Multiple " + pred
357: + " properties found");
358: }
359: }
360:
361: /**
362: * Utility method that casts the return value of
363: * {@link #getOptionalObject(Graph, Resource, URI, Resource[])} to a
364: * Resource, or throws a GraphUtilException if that value is not a Resource.
365: *
366: * @return The object of the matched statement(s), or <tt>null</tt> if no
367: * matching statements were found.
368: * @throws GraphUtilException
369: * If such an exception is thrown by
370: * {@link #getOptionalObject(Graph, Resource, URI, Resource[])} or if
371: * its return value is not a Resource.
372: */
373: public static Resource getOptionalObjectResource(Graph graph,
374: Resource subj, URI pred) throws GraphUtilException {
375: Value obj = getOptionalObject(graph, subj, pred);
376:
377: if (obj == null || obj instanceof Resource) {
378: return (Resource) obj;
379: } else {
380: throw new GraphUtilException(
381: "Expected URI or blank node for property " + pred);
382: }
383: }
384:
385: /**
386: * Utility method that casts the return value of
387: * {@link #getOptionalObject(Graph, Resource, URI, Resource[])} to a URI, or
388: * throws a GraphUtilException if that value is not a URI.
389: *
390: * @return The object of the matched statement(s), or <tt>null</tt> if no
391: * matching statements were found.
392: * @throws GraphUtilException
393: * If such an exception is thrown by
394: * {@link #getOptionalObject(Graph, Resource, URI, Resource[])} or if
395: * its return value is not a URI.
396: */
397: public static URI getOptionalObjectURI(Graph graph, Resource subj,
398: URI pred) throws GraphUtilException {
399: Value obj = getOptionalObject(graph, subj, pred);
400:
401: if (obj == null || obj instanceof URI) {
402: return (URI) obj;
403: } else {
404: throw new GraphUtilException("Expected URI for property "
405: + pred);
406: }
407: }
408:
409: /**
410: * Utility method that casts the return value of
411: * {@link #getOptionalObject(Graph, Resource, URI, Resource[])} to a Literal,
412: * or throws a GraphUtilException if that value is not a Literal.
413: *
414: * @return The object of the matched statement(s), or <tt>null</tt> if no
415: * matching statements were found.
416: * @throws GraphUtilException
417: * If such an exception is thrown by
418: * {@link #getOptionalObject(Graph, Resource, URI, Resource[])} or if
419: * its return value is not a Literal.
420: */
421: public static Literal getOptionalObjectLiteral(Graph graph,
422: Resource subj, URI pred) throws GraphUtilException {
423: Value obj = getOptionalObject(graph, subj, pred);
424:
425: if (obj == null || obj instanceof Literal) {
426: return (Literal) obj;
427: } else {
428: throw new GraphUtilException(
429: "Expected literal for property " + pred);
430: }
431: }
432:
433: /**
434: * Utility method that removes all statements matching the specified criteria
435: * from a graph.
436: *
437: * @param graph
438: * The graph to remove the statements from.
439: * @param subj
440: * The subject of the statements to match, <tt>null</tt> to match
441: * statements with any subject.
442: * @param pred
443: * The predicate of the statements to match, <tt>null</tt> to match
444: * statements with any predicate.
445: * @param obj
446: * The object of the statements to match, <tt>null</tt> to match
447: * statements with any object.
448: * @param contexts
449: * The contexts of the statements to match. If no contexts are
450: * specified, statements will match disregarding their context. If one
451: * or more contexts are specified, statements with a context matching
452: * one of these will match.
453: * @throws IllegalArgumentException
454: * If a <tt>null</tt>-array is specified as the value for
455: * <tt>contexts</tt>. See
456: * {@link OpenRDFUtil#verifyContextNotNull(Resource[])} for more
457: * info.
458: */
459: public static void remove(Graph graph, Resource subj, URI pred,
460: Value obj, Resource... contexts) {
461: Iterator<Statement> statements = graph.match(subj, pred, obj,
462: contexts);
463: while (statements.hasNext()) {
464: statements.next();
465: statements.remove();
466: }
467: }
468: }
|