001: package org.ontoware.rdfreactor.runtime;
002:
003: import java.lang.reflect.Array;
004: import java.util.HashSet;
005: import java.util.Map;
006: import java.util.Set;
007:
008: import org.ontoware.aifbcommons.collection.ClosableIterator;
009: import org.ontoware.rdf2go.exception.ModelRuntimeException;
010: import org.ontoware.rdf2go.model.Model;
011: import org.ontoware.rdf2go.model.Statement;
012: import org.ontoware.rdf2go.model.impl.ModelAddRemoveMemoryImpl;
013: import org.ontoware.rdf2go.model.impl.TriplePatternImpl;
014: import org.ontoware.rdf2go.model.node.Node;
015: import org.ontoware.rdf2go.model.node.Resource;
016: import org.ontoware.rdf2go.model.node.URI;
017: import org.ontoware.rdf2go.model.node.Variable;
018: import org.ontoware.rdf2go.vocabulary.RDF;
019: import org.slf4j.Logger;
020: import org.slf4j.LoggerFactory;
021:
022: /**
023: * <b>BridgeBase</b> provides methods for adding, querying and deleting
024: * statements from the underlying RDF2Go model.
025: *
026: * TODO: be type-safe
027: *
028: *
029: *
030: * <br>
031: * RDF Reactor uses the following naming:
032: *
033: * <b>resource</b> - instance of an RDF schema class, identified by the
034: * resource ID (an URI or BlankNode), allmost all statements about the resource
035: * use the resource ID as the object
036: *
037: * <b>property</b> - a property belongs to a resource, represented by the
038: * predicate of a statement about a resource
039: *
040: * <b>value</b> - value of a property of a resource, represented by the object
041: * of the statement with the property as predicate and the resource ID as the
042: * subject
043: *
044: * @author mvo
045: */
046:
047: public class BridgeBase {
048:
049: private static Logger log = LoggerFactory
050: .getLogger(BridgeBase.class);
051:
052: // /////////////////////////
053: // true implementations
054:
055: /**
056: * Check if the resource identified by resourceID, has a property
057: * identidified by propertyURI which has the given value among its values.
058: *
059: * @param model -
060: * the underlying RDF2Go model
061: * @param resource -
062: * must be an URI or a BlankNode
063: * @param propertyURI -
064: * URI of the property
065: * @param value -
066: * look for this value of the property
067: * @return true if value is among values for the property
068: * @throws Exception
069: */
070: @Patrolled
071: public static boolean containsGivenValue(Model model,
072: Resource resource, URI propertyURI, Object value)
073: throws ModelRuntimeException {
074:
075: Node objectNode = RDFReactorRuntime.java2node(model, value);
076: return model.contains(resource, propertyURI, objectNode);
077: }
078:
079: /**
080: * Return the first x with matching statement (resourceObject, propertyURI,
081: * x) from the given model. If severeal matching statements exist, only the
082: * first is returned.
083: *
084: * @param model -
085: * the underlying RDF2Go model
086: * @param resourceSubject -
087: * the URI or BlankNode of the resource
088: * @param propertyURI -
089: * the URI of the property
090: * @param returnType -
091: * return the value from the statement as the given Java Type
092: * @return the first x matching (resourceObject, propertyURI, x)
093: * @throws RDFDataException
094: * if more then one value was found
095: * @throws ModelRuntimeException
096: */
097: @Patrolled
098: public static Object getValue(Model model,
099: Resource resourceSubject, URI propertyURI,
100: java.lang.Class<?> returnType) throws RDFDataException,
101: ModelRuntimeException {
102: Node node = ResourceUtils.getSingleValue(model,
103: resourceSubject, propertyURI);
104: return RDFReactorRuntime.node2javatype(model, node, returnType);
105: }
106:
107: /**
108: * Return a Set with all x matching (resourceObject, propertyURI, x) in the
109: * given model.
110: *
111: * @param model -
112: * the underlying model
113: * @param resource -
114: * URI or BlankNode of the resource
115: * @param propertyURI -
116: * URI of the property
117: * @param returnType -
118: * return the found values as the given Java Type
119: * @return Set<Object> with each object of type 'returnType'
120: * @throws Exception
121: */
122: @Patrolled
123: public static Set<Object> getAllValues_asSet(Model model,
124: Resource resource, URI propertyURI,
125: java.lang.Class<?> returnType) {
126: synchronized (model) {
127: ClosableIterator<? extends Statement> it = model
128: .findStatements(resource, propertyURI, Variable.ANY);
129: Set<Object> result = new HashSet<Object>();
130: while (it.hasNext()) {
131: Node rdfnode = it.next().getObject();
132: result.add(RDFReactorRuntime.node2javatype(model,
133: rdfnode, returnType));
134: }
135: it.close();
136: return result;
137: }
138: }
139:
140: /**
141: * Get all values for the given resource and property.
142: *
143: * @param model -
144: * the underlying RDF2Go model
145: * @param resource -
146: * URI or BlankNode of the resource
147: * @param propertyURI -
148: * URI of the property
149: * @param returnType -
150: * return the values typed as returnType
151: * @return array of type 'returnType' with 0..n values. Never null.
152: * @throws Exception
153: */
154: @Patrolled
155: public static Object[] getAllValues(Model model, Resource resource,
156: URI propertyURI, java.lang.Class<?> returnType) {
157: return triplepattern2reactor(model, TriplePatternImpl
158: .createObjectPattern(resource, propertyURI), returnType);
159: }
160:
161: /**
162: * Get all resources having the given property and value.
163: *
164: * @param model -
165: * the underlying RDF2Go model
166: * @param propertyURI -
167: * URI of the property
168: * @param objectNode -
169: * predicate/value of the property
170: * @param returnType -
171: * return the values typed as returnType
172: * @return array of type 'returnType' with 0..n values. Never null.
173: * @throws Exception
174: */
175: @Patrolled
176: public static Object[] getAllValues_Inverse(Model model,
177: URI propertyURI, Node objectNode,
178: java.lang.Class<?> returnType) throws ModelRuntimeException {
179: return triplepattern2reactor(model, TriplePatternImpl
180: .createSubjectPattern(propertyURI, objectNode),
181: returnType);
182: }
183:
184: /**
185: * Query the model with a SPARQL query.
186: *
187: * @param model -
188: * the underlying RDF2GO model
189: * @param returnTypes -
190: * cast the values to the types in returnTypes[]
191: * @param sparqlSelectQuery -
192: * the SPARQL query
193: * @return java objects, typed as desired by 'returnTypes'
194: * @throws Exception
195: */
196: @Patrolled
197: public static OOQueryResultTable getSparqlSelect(Model model,
198: Map<String, Class<?>> returnTypes, String sparqlSelectQuery)
199: throws ModelRuntimeException {
200: return new OOQueryResultTableImpl(model, returnTypes,
201: sparqlSelectQuery);
202: }
203:
204: /**
205: * Add a value to a property of a resource.
206: *
207: * @param model -
208: * the underlying RDF2Go model
209: * @param subject -
210: * URI or BlankNode representing the resource
211: * @param property -
212: * URI of the property
213: * @param object -
214: * value of the property which is to be added to the resource
215: * @throws Exception
216: */
217: @Patrolled
218: public static void add(Model model, Resource subject, URI property,
219: Object object) throws ModelRuntimeException {
220: addStatementGeneric(model, subject, property, RDFReactorRuntime
221: .java2node(model, object));
222: }
223:
224: /**
225: * Get all instances of the class in the model.
226: *
227: * @param model -
228: * the underlying RDF2Go class
229: * @param javaClass -
230: * the requested Java class
231: * @return Array of the given javaClass type instances in the model
232: */
233: @Patrolled
234: public static Object[] getAllInstances(Model model,
235: java.lang.Class<?> javaClass) {
236: URI rdfsClass;
237: try {
238: rdfsClass = (URI) javaClass.getDeclaredField("RDFS_CLASS")
239: .get(null);
240: return triplepattern2reactor(model, TriplePatternImpl
241: .createObjectPattern(RDF.type, rdfsClass),
242: javaClass);
243: } catch (IllegalArgumentException e) {
244: throw new RuntimeException(e);
245: } catch (SecurityException e) {
246: throw new RuntimeException(e);
247: } catch (IllegalAccessException e) {
248: throw new RuntimeException(e);
249: } catch (NoSuchFieldException e) {
250: throw new RuntimeException(e);
251: } catch (Exception e) {
252: throw new RuntimeException(e);
253: }
254: }
255:
256: /**
257: * Remove all values of a property from a resource. (types don't matter
258: * here)
259: *
260: * @param model -
261: * the underlying RDF2Go model
262: * @param resourceObject -
263: * URI or BlankNode of the resource
264: * @param propertyURI -
265: * URI of the property
266: * @return true if any value was present
267: * @throws Exception
268: */
269: public static boolean removeAllValues(Model model, Resource r, URI p)
270: throws ModelRuntimeException {
271: assert model != null;
272: assert r != null;
273: assert p != null;
274:
275: synchronized (model) {
276: ModelAddRemoveMemoryImpl toBeDeleted = new ModelAddRemoveMemoryImpl();
277: toBeDeleted
278: .addAll(model.findStatements(r, p, Variable.ANY));
279: ClosableIterator<Statement> it = toBeDeleted.iterator();
280: model.removeAll(it);
281: it.close();
282: return toBeDeleted.size() > 0;
283: }
284: }
285:
286: /**
287: * Remove a value of a property from a resource.
288: *
289: * @param model -
290: * the underlying RDF2Go model
291: * @param resource -
292: * URI or BlankNode of the resource
293: * @param propertyURI -
294: * URI of the property
295: * @param value -
296: * value of the property which is removed
297: * @return true if value could be removed
298: * @throws Exception
299: */
300: static boolean removeValue(Model model, Resource resource,
301: URI propertyURI, Object value) throws ModelRuntimeException {
302: Node objectNode = RDFReactorRuntime.java2node(model, value);
303: boolean found = model.contains(resource, propertyURI,
304: objectNode);
305:
306: if (found) {
307: model.removeStatement(resource, propertyURI, objectNode);
308: }
309: return found;
310: }
311:
312: // /////////////////////////////
313: // type conversion utilities
314:
315: /**
316: * Take a TripplePattern, apply it to the model, and return an array with
317: * the found instances typed as returnType.
318: *
319: * @param model -
320: * the underlying RDF2Go model
321: * @param triplePattern -
322: * TriplePattern for which matches are searched
323: * @param returnType -
324: * convert all found instances to this type
325: * @return array with 0..n instances (never null) typed as 'returnType'
326: * @throws Exception
327: */
328: @Patrolled
329: private static Object[] triplepattern2reactor(
330: Model model,
331: org.ontoware.rdf2go.model.impl.TriplePatternImpl triplePattern,
332: Class<?> returnType) {
333: log.debug("looking for " + triplePattern);
334: Set<Object> result = new HashSet<Object>();
335: synchronized (model) {
336: ClosableIterator<? extends Statement> it = model
337: .findStatements(triplePattern);
338: // eliminates duplicates
339: while (it.hasNext()) {
340: log.debug("got a result");
341: Statement statement = it.next();
342: Node rdfnode = triplePattern.getExtract(statement);
343: result.add(RDFReactorRuntime.node2javatype(model,
344: rdfnode, returnType));
345: }
346: it.close();
347: }
348: log.debug("Found " + result.size() + " results");
349: // IMPROVE: quite complicated array creation
350: Object[] resultValues = result.toArray();
351: Object[] resultAsArray = (Object[]) Array.newInstance(
352: returnType, result.size());
353:
354: for (int i = 0; i < resultAsArray.length; i++) {
355: log.debug("casting " + resultValues[i] + " to "
356: + returnType);
357: resultAsArray[i] = returnType.cast(resultValues[i]);
358: }
359: return resultAsArray;
360: }
361:
362: // @Patrolled
363: // private static Object triplepattern2reactor_singleValue(Model model,
364: // org.ontoware.rdf2go.model.impl.TriplePatternImpl triplePattern,
365: // Class<?> returnType) {
366: // log.debug("looking for *the* single value of " + triplePattern);
367: // Object result = null;
368: // synchronized (model) {
369: // ClosableIterator<? extends Statement> it = model
370: // .findStatements(triplePattern);
371: // while (it.hasNext()) {
372: // Statement statement = it.next();
373: // Node rdfnode = triplePattern.getExtract(statement);
374: // result = uriBlankLiteral2reactor(model, rdfnode, returnType);
375: // }
376: // it.close();
377: // }
378: //
379: // return result;
380: // }
381:
382: /**
383: * Add a statement (subject, property, o) to the given model. ("generic"
384: * because a lot of type casting is necessary if generic objects are allowed
385: * as arguments).
386: *
387: * @param model -
388: * the underlying RDF2Go model
389: * @param subject -
390: * subject of the statement has to be an URI or BlankNode
391: * @param property -
392: * predicate of the statement has to be an URI
393: * @param object -
394: * object of the statement can be an URI, URI[], String,
395: * DatatypeLiteral, LanguageTagLiteral or generic Object[]
396: * @throws Exception
397: */
398: @Patrolled
399: private static void addStatementGeneric(Model model,
400: Resource subject, URI property, Object object)
401: throws ModelRuntimeException {
402: assert subject != null;
403: assert property != null;
404: assert object != null;
405: log.debug("add (" + subject + "," + property + "," + object
406: + ")");
407:
408: if (object.getClass().isArray()) {
409: // handle each component
410: log.debug("object is an instanceof some Array");
411: Object[] values = (Object[]) object;
412: for (int i = 0; i < values.length; i++) {
413: addStatementGeneric_singleValue(model, subject,
414: property, values[i]);
415: }
416: } else {
417: // handle once
418: addStatementGeneric_singleValue(model, subject, property,
419: object);
420: }
421: }
422:
423: /**
424: * Add a single statement.
425: *
426: * @param model
427: * @param subject
428: * @param property
429: * @param object -
430: * handles rdf2go nodes and strings
431: */
432: @Patrolled
433: private static void addStatementGeneric_singleValue(Model model,
434: Resource subject, URI property, Object object) {
435: if (object instanceof Node) {
436: log
437: .debug("object is an instance of an Rdf2go Node (URI, Literal, ...) , so will add as single resource");
438: model.addStatement(subject, property, (Node) object);
439: } else if (object instanceof String) {
440: model.addStatement(subject, property, (String) object);
441: } else
442: throw new RuntimeException("unknown object type "
443: + object.getClass());
444: }
445:
446: // /**
447: // * convert a ReactorBase or primitive Java object to a type used in the
448: // * RDF2GO model
449: // *
450: // * @param reactorValue
451: // * @return URI, String and Blank nodes are passed through, ReactorBase
452: // * instances have their identifier returned.
453: // * @throws ModelRuntimeException
454: // */
455: // private static Object toRDF2GoType(Object reactorValue) {
456: // if (reactorValue == null) {
457: // throw new IllegalArgumentException("Argument may not be null");
458: // }
459: //
460: // // array in, array out
461: //
462: // if (reactorValue.getClass().isArray()) {
463: // log.debug("object is an array");
464: // Object[] reactorValues = (Object[]) reactorValue;
465: // Node[] nodes = new Node[reactorValues.length];
466: // for (int i = 0; i < reactorValues.length; i++) {
467: // nodes[i] = toRDF2GoNode(reactorValues[i]);
468: // }
469: // return nodes;
470: // }
471: //
472: // // if (reactorValue instanceof ResourceEntity[]) {
473: // // log
474: // // .debug("object is an instanceof ReactorBase[], so will add as
475: // // multiple resources");
476: // // ResourceEntity[] values = (ResourceEntity[]) reactorValue;
477: // // Resource[] javatype = new Resource[values.length];
478: // // for (int i = 0; i < values.length; i++) {
479: // // javatype[i] = ((ResourceEntity) values[i]).getResource();
480: // // }
481: // // return javatype;
482: // // }
483: // else {
484: // log.debug("object is simple, converting to rdf2go node...");
485: // // value in, value out
486: // return toRDF2GoNode(reactorValue);
487: // }
488: // }
489:
490: }
|