001: package org.ontoware.rdfreactor.runtime;
002:
003: import java.lang.reflect.Array;
004: import java.util.ArrayList;
005: import java.util.Iterator;
006: import java.util.List;
007: import java.util.Set;
008:
009: import org.ontoware.aifbcommons.collection.ClosableIterator;
010: import org.ontoware.rdf2go.exception.ModelRuntimeException;
011: import org.ontoware.rdf2go.model.Model;
012: import org.ontoware.rdf2go.model.QueryRow;
013: import org.ontoware.rdf2go.model.Statement;
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>ReactorBaseImpl</b> is the base class for instances of classes from the
024: * RDFS/OWL schema generated by the code generator. <br>
025: *
026: * If a class inheriting from ReactorBaseImpl is instantiated, it represents an
027: * instance of a class from an RDFS/OWL schema. The properties of this class
028: * instance are stored in an RDF model using RDF2Go (via the static Bridge
029: * object) as triples of the form: (this.instanceIdentifier, key, value). <br>
030: *
031: * Besides methods for putting, getting and removing properties, the Map<URI,
032: * Object> Interface is also supported for handling of all properties of this
033: * instance in one Map, backed by the actual RDF2Go model.
034: *
035: * <br>
036: * RDF Reactor uses the following naming:
037: *
038: * <b>resource</b> - instance of an RDF schema class, identified by the
039: * resource ID (an URI or BlankNode), almost all statements about the resource
040: * use the resource ID as the object
041: *
042: * <b>property</b> - a property belongs to a resource, represented by the
043: * predicate of a statement about a resource
044: *
045: * <b>value</b> - value of a property of a resource, represented by the object
046: * of the statement with the property as predicate and the resource ID as the
047: * subject
048: *
049: * @author $Author: xamde $
050: * @version $Id: ReactorBaseImpl.java,v 1.24 2006/12/05 19:47:28 xamde Exp $
051: *
052: *
053: */
054: public class ReactorBaseImpl implements ReactorBase {
055:
056: private static Logger log = LoggerFactory
057: .getLogger(ReactorBaseImpl.class);
058:
059: /**
060: * the underlying RDF2Go model in which the triples representing the
061: * properties of this object are saved
062: */
063: protected Model model;
064:
065: /**
066: * the URI of the RDFS class from which this object is an instance
067: */
068: protected URI classURI;
069:
070: /**
071: * the identifier of this instance is a URI or a BlankNode. It is used as
072: * the Subject of all triples representing this instance in the RDF model.
073: */
074: private Resource instanceIdentifier;
075:
076: /**
077: * Constructor: create a ReactorBaseImpl for the RDFS/OWL schema class
078: * identified by classURI, with instanceIdentifier as the identifing URL or
079: * BlankNode.
080: *
081: * @param model,
082: * the underlying RDF2Go model
083: * @param classURI,
084: * URI of the RDFS/OWL class from which this object is an
085: * instance
086: * @param instanceIdentifier,
087: * has to be an URI or URL or BlankNode
088: * @param write
089: * if true, the triple (this, rdf:type, classURI) is written to
090: * the model (in addition to any other triples denoting
091: * properties of the instance)
092: */
093: public ReactorBaseImpl(Model model, URI classURI,
094: Resource instanceIdentifier, boolean write) {
095: this .model = model;
096: this .classURI = classURI;
097: this .instanceIdentifier = (Resource) instanceIdentifier;
098:
099: // this can lead to concurrenty exceptions when used in
100: // iterators on the model
101: if (write) {
102: try {
103: // add type information only if not present
104: if (!model.contains(this .instanceIdentifier, RDF.type,
105: classURI)) {
106: log.debug("adding type information: "
107: + this .instanceIdentifier + " a "
108: + classURI);
109: add(RDF.type, classURI);
110:
111: }
112: } catch (Exception e) {
113: throw new RuntimeException(e);
114: }
115: }
116: }
117:
118: /**
119: * Constructor: create a ReactorBaseImpl for the RDFS/OWL schema class
120: * identified by classURI, with instanceIdentifier as the identifing URL or
121: * BlankNode. Dont write (this, rdf:type, classURI) for this instance to the
122: * model.
123: *
124: * @param model,
125: * the underlying RDF2Go model
126: * @param classURI,
127: * URI of the RDFS/OWL class from which this object is an
128: * instance
129: * @param instanceIdentifier,
130: * has to be an URI or URL or BlankNode
131: */
132: public ReactorBaseImpl(Model model, URI classURI,
133: Resource instanceIdentifier) {
134: // FIXME: default true or false?
135: this (model, classURI, instanceIdentifier, false);
136: }
137:
138: /**
139: * implements
140: *
141: * @see ReactorEntity
142: */
143: public Resource getResource() {
144: return this .instanceIdentifier;
145: }
146:
147: /**
148: * @return the URI of the RDFS schema class of which this object is an
149: * instance
150: */
151: public URI getRDFSClassURI() {
152: return this .classURI;
153: }
154:
155: /**
156: * Find the URI of the RDFS schema class through inspection, this is usefull
157: * if the model contains no (this, rdf:type, classURI) triple
158: *
159: * @param javaClass
160: * for which the RDFS schema class should be found
161: * @return the RDFs_CLASS URI of an instance by using reflection
162: */
163: private static URI getClassURI(Class<?> javaClass) {
164: // TODO experimental
165: try {
166: URI classURI;
167: classURI = (URI) javaClass.getDeclaredField("RDFS_CLASS")
168: .get(null);
169: return classURI;
170: } catch (Exception e) {
171: throw new IllegalArgumentException(
172: javaClass
173: + " seems not to have an RDFS_CLASS. Is it really a subclass of ReactorBaseImpl ?",
174: e);
175: }
176: }
177:
178: // //////////////////////////////////////
179: // override some java.lang.Object methods
180:
181: /**
182: * implement
183: *
184: * @see Object methods
185: */
186: public boolean equals(Object other) {
187:
188: if (other instanceof ReactorBase) {
189: return ((ReactorBase) other).getResource().equals(
190: this .getResource());
191: } else if (other instanceof URI) {
192: return this .getResource().equals(other);
193: } else
194: return false;
195: }
196:
197: /**
198: * implement
199: *
200: * @see Object methods
201: */
202: public int hashCode() {
203: return this .instanceIdentifier.hashCode();
204: }
205:
206: /**
207: * implement
208: *
209: * @see Object methods
210: * @return a string representation of the instance identifier (URI or blank
211: * node). Representations are dependant on the used RDF2Go adaptor.
212: */
213: public String toString() {
214: return this .instanceIdentifier.toString();
215: }
216:
217: // ////////////////////////
218: // queries
219:
220: public static boolean hasInstance(Model model, URI uri, URI classURI) {
221: try {
222: return (model.contains(uri, RDF.type, classURI));
223: } catch (ModelRuntimeException e) {
224: throw new RuntimeException(e);
225: }
226: }
227:
228: public static Object getInstance(Model model, URI uri,
229: Class<?> javaClass) throws Exception {
230: if (model.contains(uri, RDF.type, getClassURI(javaClass))) {
231: return RDFReactorRuntime.resource2reactorbase(model, uri,
232: javaClass);
233: } else
234: return null;
235: }
236:
237: /**
238: * Return all instances of the given class.
239: *
240: * @param model -
241: * underlying RDF2Go model
242: * @param javaClass -
243: * the java class representing the class the instances which
244: * should be returned
245: * @param classURI -
246: * URI of the (RDFS/OWL) class. currently not used
247: * @return array of all instances of the given java class in the model
248: */
249: private static Object[] getAllInstances(Model model,
250: Class<?> javaClass, URI classURI) {
251:
252: // FIXME
253: if (!model.isOpen())
254: model.open();
255:
256: // FIXME: classURI is not used ?
257: List<Object> result = new ArrayList<Object>();
258: try {
259:
260: ClosableIterator<? extends Statement> it = model
261: .findStatements(Variable.ANY, RDF.type,
262: getClassURI(javaClass));
263: while (it.hasNext()) {
264: Resource o = it.next().getSubject();
265: // log.debug("found instance " + o + " of class "
266: // + javaClass.getName());
267: result.add(RDFReactorRuntime.node2javatype(model, o,
268: javaClass));
269: }
270: it.close();
271:
272: Object[] typedResult = (Object[]) Array.newInstance(
273: javaClass, result.size());
274: for (int i = 0; i < typedResult.length; i++)
275: typedResult[i] = javaClass.cast(result.get(i));
276:
277: log.debug("returning " + typedResult.length + " typed "
278: + javaClass + " instances");
279: return typedResult;
280:
281: } catch (Exception e) {
282: throw new RuntimeException(e);
283: }
284: }
285:
286: /**
287: * Return all instances of the given class as a SubjectResultIterator
288: * object.
289: *
290: * @param model -
291: * underlying RDF2Go model
292: * @param javaClass -
293: * the java class representing the class the instances which
294: * should be returned
295: * @param classURI -
296: * URI of the (RDFS/OWL) class. currently not used
297: * @return SubjectResultIterator over the resulting instances
298: */
299: @SuppressWarnings("unchecked")
300: public static Iterator<?> getAllInstancesAsIterator(Model model,
301: Class javaClass, URI classURI) {
302: ClosableIterator<QueryRow> it;
303: try {
304: // it = model.findStatements(Variable.ANY, RDF.type,
305: // getClassURI(javaClass)).iterator();
306: it = model.sparqlSelect(
307: "SELECT ?x WHERE { ?x <" + RDF.type + "> <"
308: + classURI + ">}").iterator();
309: return new ObjectResultIterator(model,
310: new ExtractingIterator(model, it, "x"), javaClass);
311: } catch (Exception e) {
312: throw new RuntimeException(e);
313: }
314: }
315:
316: /**
317: * Return all instances of the given class.
318: *
319: * @param model -
320: * underlying RDF2Go model
321: * @param javaClass -
322: * the java class representing the class the instances which
323: * should be returned
324: * @return array of all instances of the given java class in the model
325: */
326: public static Object[] getAllInstances(Model model,
327: Class<?> javaClass) {
328: URI classURI = getClassURI(javaClass);
329: return getAllInstances(model, javaClass, classURI);
330: }
331:
332: /**
333: * Check if .this object is an instance of the given RDFS/OWL class URI.
334: *
335: * @param classURI -
336: * URI of the RDFS/OWL class
337: * @return true if .this is an instance of the given classURI
338: */
339: public boolean isInstanceof(URI classURI)
340: throws ModelRuntimeException {
341: return model.contains(this .getResource(), RDF.type, classURI);
342: }
343:
344: /**
345: * Check if .this object is an instance of the given Java class.
346: *
347: * @param javaClass -
348: * given Java class
349: * @return true if .this is an instance of the given Java Class in the model
350: */
351: public boolean isInstanceof(Class<?> javaClass)
352: throws ModelRuntimeException {
353: return (isInstanceof(getClassURI(javaClass)));
354: }
355:
356: /**
357: * Cast .this object to the given target Java type.
358: *
359: * @param targetType -
360: * Java type to which to cast this object
361: * @return converted object
362: */
363: public Object castTo(Class<?> targetType) {
364: return RDFReactorRuntime.node2javatype(model, this
365: .getResource(), targetType);
366: }
367:
368: // FIXME new
369:
370: /**
371: * Returns the first x in (this, prop, x) if such a statement is in the
372: * model. Null otherwise. If there are several x the first is returned
373: * (depending on the underlying RDF store). This method is useful for
374: * functional properties.
375: *
376: * @param prop -
377: * URI of the property
378: * @param returnType -
379: * desired Java return type
380: * @return null or object typed as returnType
381: * @throws RDFDataException
382: */
383: public Object get(URI prop, Class<?> returnType)
384: throws RDFDataException {
385: return Bridge.getValue(this .model, this .instanceIdentifier,
386: prop, returnType);
387: }
388:
389: /**
390: * Returns an array of x with (this, prop, x) if such statements are in the
391: * model. Null otherwise.
392: *
393: * @param prop -
394: * URI of the property
395: * @param returnType -
396: * desired Java return type
397: * @return all values, array can be empty, never null
398: */
399: public Object[] getAll(URI prop, Class<?> returnType) {
400:
401: try {
402: return Bridge.getAllValues(this .model,
403: this .instanceIdentifier, prop, returnType);
404: } catch (Exception e) {
405: throw new RuntimeException("return type "
406: + returnType.getName() + ". " + e, e);
407: }
408: }
409:
410: /**
411: * Get all resources having the given property and value. Return x matching
412: * (x, property, o).
413: *
414: * @param property -
415: * URI of the property
416: * @param o -
417: * value of the property
418: * @param returnType -
419: * desired Java return type
420: * @return array of desired returnType representing the found resources
421: */
422: public Object[] getAll_Inverse(URI property, Node o,
423: Class<?> returnType) {
424: try {
425: return Bridge.getAllValues_Inverse(this .model, property, o,
426: returnType);
427: } catch (Exception e) {
428: throw new RuntimeException(e);
429: }
430: }
431:
432: /**
433: * Get all values for the given property of this resource instance as a Set.
434: *
435: * @param prop -
436: * URI of the property
437: * @param returnType -
438: * desired Java return type
439: * @return Set of all values for the given property
440: */
441: public Set<Object> getAll_AsSet(URI prop, Class<?> returnType) {
442:
443: try {
444: return Bridge.getAllValues_asSet(this .model,
445: this .instanceIdentifier, prop, returnType);
446: } catch (Exception e) {
447: throw new RuntimeException("type " + returnType.getName(),
448: e);
449: }
450: }
451:
452: /**
453: * Removes all statements (this, prop, x) and set one anew: (this, prop, o).
454: *
455: * @param prop -
456: * URI of the property
457: * @param o -
458: * new value for the property
459: */
460: public void set(URI prop, Object o) {
461: try {
462: Bridge.setValue(this .model, this .instanceIdentifier, prop,
463: o);
464: } catch (Exception e) {
465: throw new RuntimeException(e);
466: }
467: }
468:
469: /**
470: * Removes all statements (this, prop, x) and sets anew: (this, prop, o[0]),
471: * (this, prop, o[1]), ...
472: *
473: * @param prop -
474: * URI of the property
475: * @param o -
476: * array of new values for the property
477: */
478: public void setAll(URI prop, Object[] o)
479: throws ModelRuntimeException {
480: Bridge
481: .setAllValue(this .model, this .instanceIdentifier, prop,
482: o);
483: }
484:
485: /**
486: * Removes all statements (this, prop, x) and sets anew: (this, prop, o[0]),
487: * (this, prop, o[1]), ... But only if the number of objects in o[] is less
488: * than or equal to maxCard.
489: *
490: * @param prop -
491: * URI of the property
492: * @param o -
493: * array of new values of the property
494: * @param maxCard -
495: * the maximum number of triples allowed to match (this, prop, x)
496: * @throws CardinalityException
497: * @throws CardinalityException
498: * if the size of o[] is larger then maxCard
499: */
500: public void setAll(URI prop, Object[] o, int maxCard)
501: throws ModelRuntimeException, CardinalityException {
502: if (o.length <= maxCard)
503: setAll(prop, o);
504: else
505: throw new CardinalityException("Only " + maxCard
506: + " values allowed for property " + prop
507: + " in class " + classURI + ". You tried to add "
508: + o.length);
509: }
510:
511: /**
512: * Looks for a statement (this, prop, oldValue) and replaces it by a new
513: * statement (this, prop, newValue). If the first cannot be found, false is
514: * returned, true otherwise.
515: *
516: * @param prop -
517: * URI of the property
518: * @param oldValue -
519: * old value of the property
520: * @param newValue -
521: * new value of the property
522: * @return true if old value was found
523: */
524: public boolean update(URI prop, Object oldValue, Object newValue) {
525: try {
526: return Bridge.updateValue(this .model,
527: this .instanceIdentifier, prop, oldValue, newValue);
528: } catch (Exception e) {
529: throw new RuntimeException(e);
530: }
531:
532: }
533:
534: /**
535: *
536: * @param prop
537: * @param value
538: * @return true if the model contains the statement (this, prop, value)
539: */
540: public boolean hasValue(URI prop, Object value) {
541: try {
542: return Bridge.containsGivenValue(model, instanceIdentifier,
543: prop, value);
544: } catch (ModelRuntimeException e) {
545: throw new RuntimeException(e);
546: }
547: }
548:
549: /**
550: * @param prop
551: * @return true if the model contains any statement (this, prop, *)
552: */
553: public boolean hasValue(URI prop) {
554: try {
555: return ResourceUtils.containsAnyValue(model,
556: instanceIdentifier, prop);
557: } catch (ModelRuntimeException e) {
558: throw new RuntimeException(e);
559: }
560: }
561:
562: /**
563: * Adds a statement (this, prop, o). Returns false if this statement has
564: * already been in model. True otherwise.
565: *
566: * @param property -
567: * URI of the property
568: * @param object -
569: * value of the property
570: * @return true if value was already in the model
571: */
572: public boolean add(URI property, Object object) {
573: try {
574: return Bridge.addValue(this .model, this .instanceIdentifier,
575: property, object);
576: } catch (Exception e) {
577: log.error("", e);
578: throw new RuntimeException(e);
579: }
580: }
581:
582: /**
583: * Adds a statement (this, prop, o) if the number of statements matching
584: * (this, prop, x) is less then maxCard
585: *
586: * @param property -
587: * URI of the property
588: * @param object -
589: * value of the property
590: * @param maxCard -
591: * number of occurences of (this, prop, x) allowed in the model
592: * @return true if value was already preset
593: * @throws CardinalityException
594: * if the resource already has more then maxCard values for the
595: * property
596: */
597: public boolean add(URI property, Object object, int maxCard)
598: throws CardinalityException {
599: if (getAll(property, Resource.class).length < maxCard)
600: return add(property, object);
601: else
602: throw new CardinalityException("Only " + maxCard
603: + " values allowed for property " + property
604: + " in class " + classURI);
605: }
606:
607: /**
608: * Tries to remove a statement (this, prop, o).
609: *
610: * @param prop -
611: * URI of the property
612: * @param o -
613: * value of the property
614: * @return true, if value was present
615: */
616: public boolean remove(URI prop, Object o) {
617: try {
618: return Bridge.removeValue(this .model,
619: this .instanceIdentifier, prop, o);
620: } catch (Exception e) {
621: throw new RuntimeException(e);
622: }
623: }
624:
625: /**
626: * Tries to remove a statement (this, prop, o) if the number of statements
627: * matching (this, prop, x) in the model is less then minCard
628: *
629: * @param prop -
630: * URI of the property
631: * @param o -
632: * value of the property
633: * @param minCard -
634: * number of occurences of (this, prop, x) needed in the model
635: * @return true if value was found
636: * @throws CardinalityException
637: * if resource has less then minCard values for the property
638: */
639: public boolean remove(URI prop, Object o, int minCard)
640: throws CardinalityException {
641: if (getAll(prop, Object.class).length > minCard)
642: return remove(prop, o);
643: else
644: throw new CardinalityException("Must have at least "
645: + minCard + " values for property " + prop
646: + " in class " + classURI);
647: }
648:
649: /**
650: * remove all (this, rdf:type, prop) statements
651: *
652: * @param prop,
653: * Object of a Triple
654: */
655: public boolean removeAll(URI prop) {
656: try {
657: return Bridge.removeAllValues(this .model,
658: this .instanceIdentifier, prop);
659: } catch (Exception e) {
660: throw new RuntimeException(e);
661: }
662: }
663:
664: public Model getModel() {
665: return this .model;
666: }
667:
668: /**
669: * Delete all statements of the form (this, *,*)
670: */
671: public void delete() {
672: try {
673: this .model.removeStatements(model
674: .createTriplePattern(this .instanceIdentifier,
675: Variable.ANY, Variable.ANY));
676: } catch (ModelRuntimeException e) {
677: throw new RuntimeException(e);
678: }
679:
680: }
681:
682: public boolean in(Model model) {
683:
684: // TODO implement boolean in(Model)
685: return false;
686: }
687:
688: // TODO re-enable after cleanup is complete
689: //
690: // public Map<URI, Object> map() {
691: // return new ReactorMap(model, instanceIdentifier);
692: // }
693:
694: }
|