001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.rdf.serialize; 031 032import java.io.IOException; 033import java.io.StringReader; 034import java.lang.reflect.Array; 035import java.lang.reflect.Field; 036import java.lang.reflect.InvocationTargetException; 037import java.lang.reflect.Method; 038import java.lang.reflect.Type; 039import java.net.MalformedURLException; 040import java.net.URISyntaxException; 041import java.net.URL; 042import java.util.ArrayList; 043import java.util.Arrays; 044import java.util.Collection; 045import java.util.HashMap; 046import java.util.HashSet; 047import java.util.List; 048 049import org.openimaj.util.pair.IndependentPair; 050import org.openrdf.model.Statement; 051import org.openrdf.model.URI; 052import org.openrdf.model.Value; 053import org.openrdf.model.impl.LiteralImpl; 054import org.openrdf.model.impl.StatementImpl; 055import org.openrdf.model.impl.URIImpl; 056import org.openrdf.model.impl.ValueFactoryImpl; 057import org.openrdf.model.vocabulary.RDF; 058import org.openrdf.query.BindingSet; 059import org.openrdf.query.BooleanQuery; 060import org.openrdf.query.MalformedQueryException; 061import org.openrdf.query.QueryEvaluationException; 062import org.openrdf.query.QueryLanguage; 063import org.openrdf.query.TupleQuery; 064import org.openrdf.query.TupleQueryResult; 065import org.openrdf.repository.Repository; 066import org.openrdf.repository.RepositoryConnection; 067import org.openrdf.repository.RepositoryException; 068import org.openrdf.repository.sail.SailRepository; 069import org.openrdf.rio.RDFFormat; 070import org.openrdf.rio.RDFParseException; 071import org.openrdf.sail.memory.MemoryStore; 072import org.springframework.core.ResolvableType; 073 074import javassist.Modifier; 075 076/** 077 * The RDFSerializer is used to serialise an object to RDF. It will serialise 078 * the object deeply. This class itself does not output any specific RDF 079 * representation but generates triples which is gives to the method 080 * {@link #addTriple(Statement)}. This method must be overridden in a subclass 081 * to provide the actual representation output. 082 * <p> 083 * For example, to output to Turtle, you might use the OpenRDF TurtleWriter to 084 * form a representation of the RDF graph: 085 * <p> 086 * <code><pre> 087 * StringWriter sw = new StringWriter(); 088 * final TurtleWriter tw = new TurtleWriter( sw ); 089 * RDFSerializer rs = new RDFSerializer() 090 * { 091 * public void addTriple( Statement s ) 092 * { 093 * tw.handleStatement( s ); 094 * } 095 * }; 096 * rs.serialize( myObject ); 097 * System.out.println( sw.toString() ); 098 * </pre></code> 099 * <p> 100 * By default the class will only produce triples for fields which have been 101 * annotated with the {@link Predicate} annotation. The annotation gives the URI 102 * used to link the object to its field in the triple. If you wish to attempt to 103 * serialise unannotated fields, then you should use the constructor that takes 104 * a boolean, passing true: {@link #RDFSerializer(boolean)}. This will then 105 * create predicates based on the field name, so field {@code member} will 106 * become predicate {@code hasMember}. Complex fields are serialised into 107 * subgraphs and those graphs have URIs automatically generated for them based 108 * on the URI of the object in which they exist and their name. For example: 109 * <code><pre> 110 * http://example.com/MyObject 111 * http://example.com/MyObject_hasMember 112 * http://example.com/MyObject_member. 113 * </pre></code> 114 * <p> 115 * The {@link #serialize(Object, String)} method requires the URI of the object 116 * to be serialised. This must be decided by the caller and passed in. It is 117 * used to construct URIs for complex fields and predicates (if not given). So, 118 * an object with URI <code>http://example.com/object</code> and a complex field 119 * called <code>field</code> will end up with a triple that links the object to 120 * a subgraph representing the complex object as so: <code><pre> 121 * http://example.com/object :hasField http://example.com/object_field 122 * </pre></code> The name of the subgraph is based on the URI of the object and 123 * the name of the field. The predicate is also automatically generated from the 124 * name of the field. Note that this means you may need to be careful about the 125 * names of the fields. For example, if an object had a complex field 126 * <code>name_first</code> but also had a complex field <code>name</code> that 127 * itself had a complex field <code>first</code> it's possible the same URI may 128 * be generated for both subgraphs. 129 * <p> 130 * Primitive types will be typed with XSD datatypes. For example: <code><pre> 131 * example:field example:hasNumber "20"^^xsd:integer 132 * </pre></code> 133 * <p> 134 * Lists and collections are output in one of two ways. By default they are 135 * output as separate triples in an unordered way: <code><pre> 136 * @Predicate("http://example.com/hasString") 137 * String[] strings = new String[] { "one", "two" }; 138 * </pre></code> ...will be output, by default, as: <code><pre> 139 * http://example.com/object 140 * http://example.com/hasString "one"; 141 * http://example.com/hasString "two". 142 * </pre></code> 143 * <p> 144 * Alternatively, the {@link RDFCollection} annotation can be used. When this 145 * annotation is used, they are encoded using RDF sequences; that is as 146 * subgraphs where the items have the predicates 147 * <code>rdf:_1, rdf:_2..., rdf:_n</code> and the type <code>rdf:Seq</code>. 148 * This retains the same order for the collection as when serialised. So the 149 * above example would be output as: <code><pre> 150 * http://example.com/object 151 * http://example.com/hasString http://example.com/strings . 152 * http://example.com/strings 153 * rdf:type rdf:Seq; 154 * rdf:_1 "one"; 155 * rdf:_2 "two". 156 * </pre></code> 157 * <p> 158 * By default the serialisation will also output a triple that gives the class 159 * name of the object which is being serialised. If you do not want this, use 160 * the {@link #setOutputClassNames(boolean)} to turn it off, although this may 161 * cause the deserialization to stop working, if the original fields in the 162 * object are defined as non-concrete classes (e.g. List or Collection rather 163 * than an ArrayList). In this case, the deserialiser attempts to look for this 164 * triple to find the actual class that was serialised. 165 * <p> 166 * The {@link RelationList} annotation allows collections of independent pairs 167 * of URI and Object can be sent to the RDF graph directly without validation. 168 * This is not deserialisable as there's no way for the derserialiser to know 169 * which triples belong in this collection. 170 * <p> 171 * The {@link TripleList} annotation allows members that are collections of 172 * OpenRDF {@link Statement} objects to be sent to the RDF graph directly. 173 * Again, this is not deserialisable as there's no way for the derserialiser to 174 * know which triples belong in this collection. 175 * <p> 176 * This class also provides an unserialisation routine for converting an RDF 177 * graph back into an object graph. Given a string that is an RDF representation 178 * of a graph (serialised with the serialiser), it will return the object. 179 * 180 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 181 * @created 11 Sep 2012 182 * @version $Author$, $Revision$, $Date$ 183 */ 184public class RDFSerializer { 185 /** URI used for temporary graph when loading into a store */ 186 public static final String RDF_OPENIMAJ_TMP_GRAPH = "http://rdf.openimaj.org/tmp/"; 187 188 /** Predicate for giving the class name */ 189 public static final String RDF_OPENIMAJ_P_CLASSNAME = "http://rdf.openimaj.org/hasClassName/"; 190 191 /** Predicate for unnamed collections */ 192 public static final String RDF_OPENIMAJ_P_COLLECTIONITEM = "http://rdf.openimaj.org/hasCollectionItem/"; 193 194 /** Whether to try to create predicates for unannotated fields */ 195 protected boolean autoPredicate = false; 196 197 /** Whether to output class names */ 198 protected boolean outputClassNames = true; 199 200 /** 201 * During a serialization, this field contains all the graphs which have already 202 * been written, avoiding duplicate entries in the output as well as avoiding 203 * infinite loops when cycles occur 204 */ 205 private HashSet<URI> knownGraphs = null; 206 207 /** 208 * Default constructor 209 */ 210 public RDFSerializer() { 211 this(false); 212 } 213 214 /** 215 * Constructor that determines whether to create predicates automatically when 216 * the {@link Predicate} annotation does not exist. 217 * 218 * @param autoPredicate 219 * Whether to automatically create predicates 220 */ 221 public RDFSerializer(final boolean autoPredicate) { 222 this.autoPredicate = autoPredicate; 223 } 224 225 /** 226 * Serialize the given object as RDF. 227 * 228 * @param objectToSerialize 229 * The object to serialize. 230 * @param uri 231 * The URI of the object to serialize. 232 * @return Returns the URI of the object (this may be different to the one 233 * that's passed in) 234 */ 235 public URI serialize(final Object objectToSerialize, final String uri) { 236 this.knownGraphs = new HashSet<URI>(); 237 final URI i = this.serializeAux(objectToSerialize, uri); 238 return i; 239 } 240 241 /** 242 * Serialize the given object as RDF. 243 * 244 * @param objectToSerialize 245 * The object to serialize. 246 * @param uri 247 * The URI of the object to serialize. 248 * @return Returns the URI of the object (this may be different to the one 249 * that's passed in) 250 */ 251 public URI serializeAux(final Object objectToSerialize, final String uri) { 252 return this.serializeAux(objectToSerialize, uri, true); 253 } 254 255 /** 256 * Serialize the given object as RDF. This is specifically designed for calling 257 * by the process outputting collections. 258 * 259 * @param objectToSerialize 260 * The object to serialize. 261 * @param uri 262 * The URI of the object to serialize. 263 * @param outputCollectionObjects 264 * Whether to output items in a collection 265 * @return Returns the URI of the object (this may be different to the one 266 * that's passed in) 267 */ 268 public URI serializeAux(final Object objectToSerialize, final String uri, 269 final boolean outputCollectionObjects) 270 { 271 // The subject (the object to serialize) won't change, so 272 // we'll just create the URI node once. 273 URIImpl subject = new URIImpl(uri); 274 275 // Find the object URI 276 subject = this.getObjectURI(objectToSerialize, subject); 277 278 // Check whether we've already serialized this object. If we have 279 // we just return, otherwise we add it to our memory of serialized 280 // objects so that we won't try again. 281 if (this.knownGraphs.contains(subject)) 282 return subject; 283 284 this.knownGraphs.add(subject); 285 286 // Output the class name of the object to serialise 287 if (this.outputClassNames) 288 this.addTriple(new StatementImpl(subject, new URIImpl( 289 RDFSerializer.RDF_OPENIMAJ_P_CLASSNAME), 290 this 291 .checkPrimitive(objectToSerialize.getClass().getName()))); 292 293 // Check whether there's a semantic type for this object 294 final RDFType typeAnnotation = objectToSerialize.getClass() 295 .getAnnotation(RDFType.class); 296 297 // If there is a type anotation, add it as a triple in the graph. 298 if (typeAnnotation != null) 299 this.addTriple(new StatementImpl(subject, RDF.TYPE, new URIImpl( 300 typeAnnotation.value()))); 301 302 // If this top-level object is a collection, we obviously 303 // have no predicate for all the items in the collection, 304 // so we will output them all linked to the subject URI by 305 // way of the OpenIMAJ hasCollectionItem predicate. This functionality 306 // can be disabled by passing in outputCollectionObjects as false, 307 // which the processCollection() method does when serializing collections. 308 // It does this because it will have already output the list items for 309 // a collection object, but will have recursed here to allow the other 310 // fields in the collection object to be serialized. 311 if (objectToSerialize instanceof Collection && outputCollectionObjects) { 312 this.processCollection(subject, new URIImpl(RDFSerializer.RDF_OPENIMAJ_P_COLLECTIONITEM), 313 subject, "", objectToSerialize, false); 314 315 // We will still carry on and serialize the other parts 316 // of this object... 317 } 318 319 // Get all the fields 320 final List<Field> fields = this.getAllFields(objectToSerialize); 321 322 // Loop through the fields and output them one at a time 323 for (final Field field : fields) { 324 // We won't output static members 325 if (Modifier.isStatic(field.getModifiers())) 326 continue; 327 328 // System.out.println( "====== Field "+field+" ============"); 329 330 try { 331 // Get the value of the field 332 field.setAccessible(true); 333 final Object oo = field.get(objectToSerialize); 334 335 // Special fields have annotations which mean they will be 336 // output in some other way, as defined in the outputSpecial() 337 // method. If this field is not one of those, we'll output 338 // in the normal way. 339 if (!this.outputSpecial(oo, field, subject)) { 340 // Get the predicate name (may be null if if cannot be 341 // created either due to a lack of the @Predicate 342 // annotation or because autoPredicate is false 343 final URIImpl predicate = this.getPredicateName(field, uri); 344 345 // If the predicate is null, we can't output this object. 346 // Otherwise, we'll go ahead and output it. 347 if (predicate != null) 348 this.processObject(subject, predicate, field.getName(), oo, 349 field.getAnnotation(RDFCollection.class) != null); 350 } 351 } catch (final Exception e) { 352 System.out.println("Error reflecting " + field); 353 e.printStackTrace(); 354 } 355 } 356 357 return subject; 358 } 359 360 /** 361 * Serialises a single object into the graph using the given subject and 362 * predicate in the triple. 363 * 364 * @param subject 365 * The URI of the subject being serialised 366 * @param predicate 367 * The URI of the predicate for this member 368 * @param field 369 * The name of the field being serialised 370 * @param oo 371 * The object being serialised 372 * @param asCollection 373 * Whether the field should be output as an {@link RDFCollection} 374 */ 375 private void processObject(final URIImpl subject, final URIImpl predicate, 376 final String fieldName, final Object oo, final boolean asCollection) 377 { 378 // Get the URI of the subject (the object we're serialising) 379 final String uri = subject.stringValue(); 380 381 // If the value of the object to output is not null, we go ahead 382 // and serialise the object. 383 if (oo != null && predicate != null) { 384 // This will be the value of the object we're outputting. 385 // It'll be a URI for complex objects. 386 Value object; 387 388 // This value will give whether we're outputting a collection object. 389 // That also includes Arrays. 390 boolean isCollective = false; 391 392 // Check if we should output a primitive value. If so, we're done. 393 // Otherwise, we'll need to do some more complex analysis... 394 if ((object = this.checkPrimitive(oo)) == null) { 395 // Get a URI for this object 396 final URIImpl objectURI = this.getObjectURI( 397 oo, new URIImpl( 398 subject.stringValue() + "_" + fieldName)); 399 400 // If oo is an array, we'll call the processArray() method 401 // to output it. The object becomes a URI to the array subgraph. 402 if (oo.getClass().isArray()) { 403 isCollective = true; 404 object = this.processArray(subject, predicate, objectURI, 405 fieldName, oo, asCollection); 406 } else 407 // If we have a collection of things, we'll output 408 // them as an RDF linked-list. The object becomes a URI 409 // to the collection's subgraph. 410 if (oo instanceof Collection<?>) { 411 isCollective = true; 412 object = this.processCollection(subject, predicate, objectURI, 413 fieldName, oo, asCollection); 414 } 415 // Not a primitive, array or collection? Must be a 416 // regular object, so we'll recurse this process with 417 // the value of the field. 418 else { 419 // The URI is the uri of the subject concatenated 420 // with the name of the field from which this value 421 // was taken. 422 object = new URIImpl(uri + "_" 423 + fieldName); 424 425 // Here's the recursive call to the process 426 object = this.serializeAux(oo, object.stringValue()); 427 } 428 } 429 430 // We don't need to add this triple if the triples are a 431 // are collection that's been output separately 432 if (!isCollective || (isCollective && asCollection)) { 433 // Create a triple and send it to the serializer 434 final Statement t = new StatementImpl(subject, 435 predicate, object); 436 this.addTriple(t); 437 } 438 } 439 } 440 441 /** 442 * Processes an array object outputting triples for the entire array. Returns 443 * the Value linking to this array. 444 * 445 * @param subject 446 * The URI of the object to serialise 447 * @param predicate 448 * The predicate between the subject and this array 449 * @param collectionURI 450 * The URI of the collection subgraph 451 * @param field 452 * The field in the object that's the array 453 * @param arrayObject 454 * The array object 455 * @param asCollection 456 * Whether to output as an RDF Collection 457 * @return The object linking to this array 458 */ 459 private Value processArray(final URIImpl subject, final URIImpl predicate, 460 final URIImpl collectionURI, final String fieldName, 461 final Object arrayObject, final boolean asCollection) 462 { 463 // Loop through all the array elements and output them separately. 464 for (int count = 0; count < Array.getLength(arrayObject);) { 465 // Get the array element value.. 466 final Object o = Array.get(arrayObject, count); 467 468 // Call the processListitemObject to output the actual value. 469 // We call this method rather than the serializeAux() method because 470 // we need to deal with the various methods for outputting collections 471 // in one single place (e.g. as a collection, or an RDF sequence, etc.) 472 count = this.processListItemObject(subject, predicate, collectionURI, 473 count, o, asCollection); 474 } 475 476 return collectionURI; 477 } 478 479 /** 480 * Processes a collection object. 481 * 482 * @param subject 483 * The URI of the object we're serializing 484 * @param predicate 485 * The predicate for this collection 486 * @param collectionURI 487 * The URI of the collection subgraph 488 * @param field 489 * The field in the object that is the collection 490 * @param collectionObject 491 * The collection object 492 * @param asCollection 493 * Whether to output as an RDF collection 494 * @return The object created for this collection 495 */ 496 private Value processCollection(final URIImpl subject, final URIImpl predicate, 497 final URIImpl collectionURI, final String fieldName, 498 final Object collectionObject, final boolean asCollection) 499 { 500 // Loop through all the collection items outputting them one at a time. 501 int count = 1; 502 for (final Object o : (Collection<?>) collectionObject) { 503 // We call this method rather than the serializeAux() method because 504 // we need to deal with the various methods for outputting collections 505 // in one single place (e.g. as a collection, or an RDF sequence, etc.) 506 count = this.processListItemObject(subject, predicate, collectionURI, 507 count, o, asCollection); 508 } 509 510 // We also need to serialize the object itself because if 511 // The collection is actually a subclass that contains 512 // @Predicate annotations we need to go ahead and 513 // serialise those too; so we recurse here. 514 final Value object = this.serializeAux(collectionObject, 515 collectionURI.stringValue(), false); 516 517 return object; 518 } 519 520 /** 521 * A method that's called during the processing of a list of items to write a 522 * single item. 523 * 524 * @param subject 525 * The URI of the object in which this collection exists 526 * @param predicate 527 * The predicate of the collection in the original graph 528 * @param collectionURI 529 * The URI of the collection in the graph 530 * @param listCounter 531 * The current counter in the list (1-based index) 532 * @param listItemObject 533 * The object to be serialised 534 * @param asSequence 535 * Whether to output as an RDF Sequence or as individual triples 536 * @return the next counter in the list 537 */ 538 private int processListItemObject( 539 URIImpl subject, URIImpl predicate, 540 final URIImpl collectionURI, 541 final int listCounter, final Object listItemObject, 542 final boolean asSequence) 543 { 544 // If we're outputting as a sequence, then we must 545 // alter the predicate and therefore the subject 546 if (asSequence) { 547 // If we're outputting as an RDF sequence the predicate 548 // becomes the rdf:_n counter predicate and the subject 549 // will be the URI of the collection 550 predicate = new URIImpl(RDF.NAMESPACE + "_" + listCounter); 551 subject = collectionURI; 552 } 553 554 // Check whether the list item is a primitive. If it is, its 555 // value will be output directly into the collection, otherwise 556 // we need to output a subgraph URI instead. 557 Value oo = null; 558 oo = this.checkPrimitive(listItemObject); 559 560 // If list item isn't a primitive, get a URI for it. 561 if (oo == null) { 562 // Get the URI for the list item object. 563 oo = this.getObjectURI(listItemObject, 564 new URIImpl(collectionURI.stringValue() 565 + "_listItem" + listCounter)); 566 567 // We're here because the list item is not a primitive - it's a 568 // complex object or a collection; in which case we must 569 // link to a subgraph and output that subgraph. 570 this.addTriple(new StatementImpl(subject, predicate, oo)); 571 572 // Now we serialize the object into the graph 573 this.serializeAux(listItemObject, oo.stringValue()); 574 } 575 // Output the primitive triple 576 else { 577 this.addTriple(new StatementImpl(subject, predicate, oo)); 578 } 579 580 return listCounter + 1; 581 } 582 583 /** 584 * Returns a predicate name for the given field. 585 * 586 * @param field 587 * The field 588 * @param uri 589 * The URI of the object 590 * @return A predicate URI, either generated from the @Predicate annotation or 591 * from the field name 592 */ 593 private URIImpl getPredicateName(final Field field, final String uri) { 594 // Get the predicate annotation, if there is one 595 final Predicate predicateAnnotation = field 596 .getAnnotation(Predicate.class); 597 598 URIImpl predicate = null; 599 if (predicateAnnotation != null) { 600 // Create a predicate URI for this predicate 601 predicate = new URIImpl(predicateAnnotation.value()); 602 } 603 // Null predicate annotation? 604 else { 605 // Try to create a predicate for the unannotated field 606 if (this.autoPredicate) 607 predicate = new URIImpl(uri + "_has" 608 + field.getName().substring(0, 1).toUpperCase() 609 + field.getName().substring(1)); 610 } 611 612 return predicate; 613 } 614 615 /** 616 * Checks whether the given object is a primitive type and, if so, will return a 617 * Node that encodes it. Otherwise NULL is returned. 618 * 619 * @param o 620 * The object to check 621 * @return a Node or NULL 622 */ 623 private Value checkPrimitive(final Object o) { 624 if (o instanceof String) 625 return new LiteralImpl(o.toString()); 626 627 if (o instanceof Integer) 628 return new ValueFactoryImpl().createLiteral((Integer) o); 629 630 if (o instanceof Float) 631 return new ValueFactoryImpl().createLiteral((Float) o); 632 633 if (o instanceof Double) 634 return new ValueFactoryImpl().createLiteral((Double) o); 635 636 if (o instanceof URI || o instanceof URL || o instanceof java.net.URI) 637 return new URIImpl(o.toString()); 638 639 return null; 640 } 641 642 /** 643 * Returns a list of declared fields from the whole object tree. 644 * 645 * @param o 646 * The object 647 * @return A list of fields 648 */ 649 private List<Field> getAllFields(final Object o) { 650 final ArrayList<Field> fields = new ArrayList<Field>(); 651 Class<?> objectToGetFieldsFrom = o.getClass(); 652 do { 653 fields.addAll(Arrays.asList(objectToGetFieldsFrom 654 .getDeclaredFields())); 655 objectToGetFieldsFrom = objectToGetFieldsFrom.getSuperclass(); 656 } while (!objectToGetFieldsFrom.getSimpleName().equals("Object")); 657 658 return fields; 659 } 660 661 /** 662 * Set whether to output class names as triples. 663 * 664 * @param tf 665 * TRUE to output class name triples. 666 */ 667 public void setOutputClassNames(final boolean tf) { 668 this.outputClassNames = tf; 669 } 670 671 /** 672 * Set whether to attempt to output all fields from the objects, not just those 673 * annotated with {@link Predicate}. 674 * 675 * @param tf 676 * TRUE to attempt to find predicates for all members. 677 */ 678 public void setAutoPredicate(final boolean tf) { 679 this.autoPredicate = tf; 680 } 681 682 /** 683 * Unserializes an object from the given RDF string (with the given format) into 684 * the given object. 685 * 686 * @param <T> 687 * Type of object being unserialised 688 * 689 * @param objectToUnserialize 690 * The object to populate 691 * @param objectRootURI 692 * The URI that gives the root of the object graph 693 * @param rdf 694 * The RDF string 695 * @param rdfFormat 696 * The format of the RDF in the string 697 * @return The populated object or NULL if an error occurs 698 */ 699 public <T> T unserialize(final T objectToUnserialize, 700 final String objectRootURI, final String rdf, 701 final RDFFormat rdfFormat) 702 { 703 try { 704 // We'll read the RDF into a memory store. So create that store 705 // here. 706 final Repository repo = new SailRepository(new MemoryStore()); 707 repo.initialize(); 708 709 // Read the RDF into the store 710 final RepositoryConnection connection = repo.getConnection(); 711 final StringReader sr = new StringReader(rdf); 712 final String graphURI = RDFSerializer.RDF_OPENIMAJ_TMP_GRAPH; 713 connection.add(sr, graphURI, rdfFormat); 714 715 // Now unserialize the object 716 return this.unserialize(objectToUnserialize, objectRootURI, repo); 717 } catch (final RepositoryException e) { 718 e.printStackTrace(); 719 return null; 720 } catch (final RDFParseException e) { 721 e.printStackTrace(); 722 return null; 723 } catch (final IOException e) { 724 e.printStackTrace(); 725 return null; 726 } 727 } 728 729 /** 730 * Unserializes an object from an RDF graph that is rooted at the given URI. 731 * 732 * @param <T> 733 * Type of object being unserialised 734 * 735 * @param objectToUnserialize 736 * The object to populate 737 * @param objectRootURI 738 * The URI that gives the root of the object graph 739 * @param repo 740 * The repository storing the RDF graph 741 * @return The populated object or NULL if an error occurs 742 */ 743 public <T> T unserialize(final T objectToUnserialize, 744 final String objectRootURI, final Repository repo) 745 { 746 // Can't do anything if the object is null 747 if (objectToUnserialize == null) { 748 System.err.println("Unserialize error: given object is null"); 749 return null; 750 } 751 752 // If our starting object is a collection, then there will be no 753 // predicate for us to work with. So we'll get the items from the 754 // collection using the statically defined predicate 755 // RDF_OPENIMAJ_P_COLLECTIONITEM. We will still need to deserialise 756 // the object after this, in case the collection has any 757 // predicated members that need to be deserialised. 758 if (objectToUnserialize instanceof Collection<?>) 759 this.extractCollectionDirect((Collection<?>) objectToUnserialize, 760 objectRootURI, repo); 761 762 try { 763 final RepositoryConnection connection = repo.getConnection(); 764 765 // Get the fields of the object's class 766 final Field[] fields = objectToUnserialize.getClass().getFields(); 767 768 // Loop through the fields 769 for (final Field field : fields) { 770 // System.out.println( "=========== Field "+field.getName()+" ============= "); 771 772 try { 773 // Get the name of the predicate for this field 774 final URIImpl predicateName = this.getPredicateName(field, 775 objectRootURI); 776 777 // If we can't determine a predicate, we don't unserialize it 778 if (predicateName != null) { 779 // If it's a collection, we'll do something special 780 if (Collection.class.isAssignableFrom(field.getType())) { 781 this.unserializeCollection(field, objectToUnserialize, 782 objectRootURI, repo, predicateName); 783 } else 784 // Same goes for if it's an array. 785 if (((Class<?>) field.getType()).isArray()) { 786 this.unserializeArray(objectToUnserialize, 787 objectRootURI, repo, field, predicateName); 788 } 789 // If we don't know what it is, we'll treat it as 790 // an unknown (RDF) serializable object and recurse 791 else { 792 // Query the RDF graph for the triples that represent 793 // this field in the graph. If there are more 794 // than one, the first will be used. 795 try { 796 final String queryString = "SELECT ?o WHERE {<" 797 + objectRootURI + "> <" + predicateName 798 + "> ?o.}"; 799 final TupleQuery tupleQuery = connection 800 .prepareTupleQuery(QueryLanguage.SPARQL, 801 queryString); 802 final TupleQueryResult result = tupleQuery 803 .evaluate(); 804 805 // We only want the first result because we know 806 // it's not a collection 807 if (result.hasNext()) { 808 try { 809 final BindingSet bindingSet = result.next(); 810 final Value objectValue = bindingSet 811 .getValue("o"); 812 813 // We have a value for the field. Now what 814 // we do with it depends on the field itself. 815 field.setAccessible(true); 816 field.set(objectToUnserialize, 817 this.getFieldValue( 818 field.getGenericType(), 819 objectValue, repo, 820 field.getName(), 821 objectRootURI)); 822 } catch (final IllegalArgumentException e) { 823 e.printStackTrace(); 824 } catch (final IllegalAccessException e) { 825 e.printStackTrace(); 826 } 827 } else { 828 // RDF Graph did not have a value for the field 829 } 830 } catch (final MalformedQueryException e) { 831 e.printStackTrace(); 832 } catch (final QueryEvaluationException e) { 833 e.printStackTrace(); 834 } 835 } 836 } 837 } catch (final IllegalArgumentException e) { 838 e.printStackTrace(); 839 } catch (final IllegalAccessException e) { 840 e.printStackTrace(); 841 } 842 } 843 844 connection.close(); 845 } catch (final RepositoryException e) { 846 e.printStackTrace(); 847 } catch (final SecurityException e) { 848 e.printStackTrace(); 849 } 850 851 return objectToUnserialize; 852 } 853 854 /** 855 * @param objectToUnserialize 856 * @param objectRootURI 857 * @param repo 858 */ 859 @SuppressWarnings("unchecked") 860 private <T extends Collection<?>> void extractCollectionDirect( 861 final T objectToUnserialize, 862 final String objectRootURI, final Repository repo) 863 { 864 final Class<?> collectionType = ResolvableType.forClass(objectToUnserialize.getClass()).asCollection() 865 .resolveGeneric(); 866 867 // TODO: This needs to be sorted out. We can't pass null. 868 // Unserialize the collection items. 869 final Object[] seq = this.extractCollectionObjects( 870 objectRootURI, repo, collectionType, 871 "", objectRootURI, 872 RDFSerializer.RDF_OPENIMAJ_P_COLLECTIONITEM); 873 874 ((Collection<Object>) objectToUnserialize).clear(); 875 for (int i = 0; i < seq.length; i++) 876 ((Collection<Object>) objectToUnserialize).add(seq[i]); 877 } 878 879 /** 880 * Unserializes an array object from the graph. 881 * 882 * @param objectToUnserialize 883 * The object in which there is an array field 884 * @param objectRootURI 885 * The URI of the object in which there is an array field 886 * @param repo 887 * The repository containing the RDF graph 888 * @param field 889 * The field that is the array 890 * @param predicateName 891 * The name of the predicate for the array items 892 * @throws IllegalAccessException 893 * If the field cannot be set 894 */ 895 private <T> void unserializeArray(final T objectToUnserialize, 896 final String objectRootURI, final Repository repo, final Field field, 897 final URIImpl predicateName) throws IllegalAccessException 898 { 899 final Class<?> componentType = field.getType().getComponentType(); 900 901 // Go get all the array objects 902 @SuppressWarnings("unchecked") 903 final T[] seq = (T[]) this 904 .extractCollectionObjects(objectRootURI, repo, 905 componentType, field.getName(), 906 objectRootURI, predicateName 907 .stringValue()); 908 909 // Set the field up 910 field.setAccessible(true); 911 field.set(objectToUnserialize, seq); 912 } 913 914 /** 915 * Unserializes a collection object from the graph. This method is mainly 916 * dealing with setting up the appropriate collection instance before calling 917 * {@link #extractCollectionObjects(String, Repository, Class, Field, String, String)} 918 * to actually get the objects. 919 * 920 * @param field 921 * The field which is the collection 922 * @param objectToUnserialize 923 * The object in which the field exists 924 * @param objectRootURI 925 * The URI of the object in which the field exists 926 * @param repo 927 * The repository containing the RDF graph 928 * @param predicateURI 929 * The name of the predicate for the collection items. 930 */ 931 private void unserializeCollection(final Field field, 932 final Object objectToUnserialize, final String objectRootURI, 933 final Repository repo, final URIImpl predicateURI) 934 { 935 // If we have a collection object, then we can do something 936 // a bit different here. We know it's a collection, so we 937 // simply iterate through the sequence getting each item in 938 // turn and deserialize it. 939 if (Collection.class.isAssignableFrom(field.getType())) { 940 try { 941 // We get the class from the object that we're populating 942 Class<?> cls = field.getType(); 943 944 // Attempt to instantiate the new object. 945 // This may fail if the object does not have a 946 // default or accessible constructor. In which case 947 // we'll ignore the object here. 948 Object newInstance; 949 try { 950 newInstance = cls.newInstance(); 951 } catch (final InstantiationException e) { 952 cls = (Class<?>) this.getObjectClass( 953 objectRootURI + "_" + field.getName(), repo); 954 955 // If we can't get a class to instantiate, 956 // we cannot do anything. Probably the field was null. 957 if (cls == null) 958 return; 959 960 newInstance = cls.newInstance(); 961 } 962 963 // Cast to a collection 964 @SuppressWarnings("unchecked") 965 final Collection<Object> collection = (Collection<Object>) newInstance; 966 967 // We must clear the collection here. We will populate 968 // it will everything that was in it when it was created, 969 // and if the constructor adds stuff to the collection, 970 // this will end up with a collection that is not the same 971 // as the original. 972 collection.clear(); 973 974 // Get the collection of the type 975 Class<?> collectionType = null; 976 collectionType = ResolvableType.forField(field).asCollection().resolveGeneric(); 977 978 // Now unserialise the collection. 979 final Object[] seq = this.extractCollectionObjects( 980 objectRootURI, repo, collectionType, 981 field.getName(), objectRootURI, predicateURI.stringValue()); 982 983 // If we have some stuff, then put it into the field. 984 if (seq != null) { 985 // Add all the extracted objects into the collection 986 for (int i = 0; i < seq.length; i++) 987 collection.add(seq[i]); 988 989 // Set the field value to the new collection 990 field.setAccessible(true); 991 field.set(objectToUnserialize, collection); 992 } 993 } catch (final SecurityException e) { 994 e.printStackTrace(); 995 } catch (final IllegalArgumentException e) { 996 e.printStackTrace(); 997 } catch (final InstantiationException e1) { 998 e1.printStackTrace(); 999 } catch (final IllegalAccessException e) { 1000 e.printStackTrace(); 1001 } 1002 } else { 1003 System.out.println("WARNING: Unserialize collection called for" + 1004 " something that's not a collection."); 1005 } 1006 } 1007 1008 /** 1009 * Returns an object extracted from the OpenRDF {@link Value} for this field. If 1010 * it's a primitive type, the object will be that primitive type. Otherwise, the 1011 * object will be deserialised and its reference passed back. 1012 * 1013 * @param type 1014 * The type of the field 1015 * @param value 1016 * The RDF value object for the field. 1017 * @param repo 1018 * The RDF Graph repository in use. 1019 * @param fieldName 1020 * The field name 1021 * @param subjectURI 1022 * The URI of the object 1023 * @return The deserialised object. 1024 */ 1025 private Object getFieldValue(final Type type, final Value value, 1026 final Repository repo, final String fieldName, 1027 final String subjectURI) 1028 { 1029 try { 1030 // ---- String value ---- 1031 if (type.equals(String.class)) { 1032 return value.stringValue(); 1033 } 1034 // ---- URI or URL values ---- 1035 else if (type.equals(java.net.URI.class)) { 1036 try { 1037 return new java.net.URI(value.toString()); 1038 } catch (final URISyntaxException e) { 1039 e.printStackTrace(); 1040 } 1041 } 1042 // ---- URI or URL values ---- 1043 else if (type.equals(URL.class)) { 1044 try { 1045 return new URL(value.toString()); 1046 } catch (final MalformedURLException e) { 1047 e.printStackTrace(); 1048 } 1049 } 1050 // ---- Integer values ---- 1051 else if (type.equals(Integer.class) 1052 || type.equals(int.class)) 1053 { 1054 return Integer.parseInt(value.stringValue()); 1055 } 1056 // ---- Double values ---- 1057 else if (type.equals(Double.class) 1058 || type.equals(double.class)) 1059 { 1060 return Double.parseDouble(value.stringValue()); 1061 } 1062 // ---- Float values ---- 1063 else if (type.equals(Float.class) 1064 || type.equals(float.class)) 1065 { 1066 return Float.parseFloat(value.stringValue()); 1067 } 1068 // ---- Other complex objects ---- 1069 else { 1070 // The object is not a default type that we understand. 1071 // So what we must do is try to instantiate the object, 1072 // then attempt to deserialize that object, then set the field 1073 // in this object. 1074 try { 1075 if (value instanceof URI) { 1076 String objectURI = value.stringValue(); 1077 1078 // Try and look up the className predicate of the 1079 // object. 1080 Type type2 = this.getObjectClass(objectURI, repo); 1081 if (type2 == null) 1082 type2 = this.getObjectClass(objectURI = subjectURI 1083 + "_" + fieldName, repo); 1084 1085 // Attempt to instantiate the new object. 1086 // This may fail if the object does not have a 1087 // default or accessible constructor. 1088 final Object newInstance = ((Class<?>) type2) 1089 .newInstance(); 1090 1091 // Now recurse the unserialization down the object tree, 1092 // by attempting to unserialize the given object. 1093 return this.unserialize(newInstance, objectURI, repo); 1094 } else { 1095 System.out 1096 .println("WARNING: I don't know what to do with " 1097 + value); 1098 } 1099 } catch (final InstantiationException e) { 1100 e.printStackTrace(); 1101 } 1102 } 1103 } catch (final IllegalArgumentException e) { 1104 e.printStackTrace(); 1105 } catch (final IllegalAccessException e) { 1106 e.printStackTrace(); 1107 } 1108 1109 return null; 1110 } 1111 1112 /** 1113 * Returns whether the collection given by collectionURI is an RDF sequence or 1114 * not. It does this by asking the SPARQL store whether the URI has the type 1115 * rdf:Seq. 1116 * 1117 * @param repo 1118 * The repository containing the graph 1119 * @param collectionURI 1120 * The URI of the collection to check 1121 * @return TRUE if the collection is of type rdf:Seq; FALSE otherwise or if an 1122 * error occurs. 1123 */ 1124 private boolean isRDFSequence(final Repository repo, final String collectionURI) { 1125 // Before we retrieve the objects from the sequence, we'll first 1126 // double check that it really is a sequence. If it's not (it's a 1127 // collection of unordered triples), then we'll treat it differently. 1128 try { 1129 final RepositoryConnection c = repo.getConnection(); 1130 final String queryString = "ASK {<" + collectionURI + "> <" 1131 + RDF.TYPE + "> <" + RDF.SEQ + ">}"; 1132 final BooleanQuery query = c.prepareBooleanQuery( 1133 QueryLanguage.SPARQL, queryString); 1134 return query.evaluate(); 1135 } catch (final RepositoryException e1) { 1136 e1.printStackTrace(); 1137 } catch (final MalformedQueryException e1) { 1138 e1.printStackTrace(); 1139 } catch (final QueryEvaluationException e1) { 1140 e1.printStackTrace(); 1141 } 1142 1143 return false; 1144 } 1145 1146 /** 1147 * Returns a list of objects that have been deserialised from an unordered 1148 * collection or an rdf:Seq in the graph. A test is made to determine which type 1149 * the collection is, and it will be dealt with in the appropriate way. The 1150 * method, if it succeeds, always returns an array of objects. 1151 * 1152 * @param collectionURI 1153 * The URI of the collection from which to get items 1154 * @param repo 1155 * The repository containing the RDF graph 1156 * @param componentType 1157 * The Java type of each component in the graph 1158 * @param field 1159 * The collection field to be set 1160 * @param subject 1161 * The URI of the object in which the collection is a member 1162 * @param predicate 1163 * The predicate that maps the collection to the object 1164 * @return An array of deserialised objects 1165 */ 1166 @SuppressWarnings("unchecked") 1167 private <T> T[] extractCollectionObjects(final String collectionURI, 1168 final Repository repo, final Class<T> componentType, 1169 final String fieldName, final String subject, final String predicate) 1170 { 1171 // This will be the output of the method - an array of 1172 // all the objects in order. May contain nulls. 1173 T[] sequence = null; 1174 1175 // Check whether the collection is a sequence. If it is, then we 1176 // can get the collection of objects and put them into an appropriate 1177 // array 1178 if (this.isRDFSequence(repo, collectionURI)) { 1179 // We'll get all the results into this map to start with. 1180 // It maps an index (in the sequence) to the binding set from the query 1181 final HashMap<Integer, BindingSet> tmpMap = new HashMap<Integer, BindingSet>(); 1182 1183 try { 1184 // Extract the objects from the RDF sequence 1185 final int max = this.extractRDFSequenceObjects( 1186 collectionURI, repo, tmpMap); 1187 1188 // If there was no sequence object, we'll return 1189 if (max < 0) 1190 return null; 1191 1192 // So we've processed and stored all the results. Now we need to 1193 // make sure our output array is big enough for all the results. 1194 sequence = (T[]) Array.newInstance(componentType, max); 1195 1196 // Now loop through all the values and poke them into the array. 1197 // Note that we convert the indices to 0-based (RDF.Seq are 1198 // 1-based indices). 1199 for (final int i : tmpMap.keySet()) 1200 sequence[i - 1] = (T) this.getFieldValue( 1201 componentType, 1202 tmpMap.get(i).getValue("o"), 1203 repo, fieldName, 1204 collectionURI); 1205 } catch (final RepositoryException e) { 1206 e.printStackTrace(); 1207 } catch (final MalformedQueryException e) { 1208 e.printStackTrace(); 1209 } catch (final QueryEvaluationException e) { 1210 e.printStackTrace(); 1211 } 1212 } else { 1213 sequence = this.getUnorderedObjects( 1214 collectionURI, repo, componentType, 1215 fieldName, subject, predicate); 1216 } 1217 1218 return sequence; 1219 } 1220 1221 /** 1222 * Extracts a set of objects from an RDF sequence and returns a map containing 1223 * the objects mapped to their index. 1224 * 1225 * @param collectionURI 1226 * The URI of the collection to extract 1227 * @param repo 1228 * The repository containing the RDF graph 1229 * @param objectMap 1230 * The map of the sequence objects 1231 * @return The maximum index of a sequence object extracted 1232 * @throws RepositoryException 1233 * @throws MalformedQueryException 1234 * @throws QueryEvaluationException 1235 */ 1236 private int extractRDFSequenceObjects(final String collectionURI, 1237 final Repository repo, final HashMap<Integer, BindingSet> objectMap) 1238 throws RepositoryException, MalformedQueryException, 1239 QueryEvaluationException 1240 { 1241 int max = -1; 1242 1243 // First we select all the links from the collectionURI 1244 // out using the SPARQL query: 1245 // SELECT <collectionURI> ?p ?o 1246 // ordered by the predicate name, as we're expecting those 1247 // to be rdf:_1, rdf:_2, etc. etc. 1248 final RepositoryConnection c = repo.getConnection(); 1249 final String queryString = "SELECT ?p ?o WHERE {<" + collectionURI 1250 + "> ?p ?o} ORDER BY DESC(?p)"; 1251 final TupleQuery tupleQuery = c.prepareTupleQuery( 1252 QueryLanguage.SPARQL, queryString); 1253 1254 // This actually does the query to the store... 1255 final TupleQueryResult result = tupleQuery.evaluate(); 1256 1257 // Loop through the results... 1258 while (result.hasNext()) { 1259 try { 1260 final BindingSet bs = result.next(); 1261 1262 // If the predicate is a sequence number (starts with rdf:_) 1263 // then we parse the integer into the index variable. 1264 // If it's not a NumberFormatException is thrown 1265 // (and caught and ignored because it's clearly not an 1266 // RDF sequence URI) 1267 final int index = Integer.parseInt( 1268 bs.getValue("p").stringValue() 1269 .substring("http://www.w3.org/1999/02/22-rdf-syntax-ns#_" 1270 .length())); 1271 1272 // Just be sure we're doing something sensible. 1273 if (index >= 0) { 1274 // Stick it in the map. 1275 objectMap.put(index, bs); 1276 1277 // Store the maximum index 1278 max = Math.max(index, max); 1279 } 1280 } catch (final NumberFormatException e) { 1281 // If we get a NFE then it's probably not a sequence number. 1282 } catch (final StringIndexOutOfBoundsException e) { 1283 // If we get a SOOBE then it's probably because the 1284 // predicate 1285 // is not a sequence number. 1286 } 1287 } 1288 return max; 1289 } 1290 1291 /** 1292 * Returns a list of unserialised objects that were unserialised from an 1293 * unordered list of triples in RDF 1294 * 1295 * @param sequenceURI 1296 * The URI of the triples 1297 * @param repo 1298 * The repository 1299 * @param fieldType 1300 * The field type 1301 * @param field 1302 * The field 1303 * @return An array of objects, in an arbitrary order. 1304 */ 1305 @SuppressWarnings("unchecked") 1306 private <T> T[] getUnorderedObjects(final String sequenceURI, 1307 final Repository repo, final Class<T> fieldType, 1308 final String fieldName, 1309 final String subjectURI, final String predicate) 1310 { 1311 try { 1312 // First select all the objects that link the main object 1313 // with the collection objects via the collection predicate. 1314 // We use the SPARQL query: 1315 // SELECT ?o WHERE { <subjectURI> <predicate> ?o . } 1316 // The results are in any order. 1317 final RepositoryConnection c = repo.getConnection(); 1318 final String queryString = "SELECT ?o WHERE {<" + subjectURI 1319 + "> <" + predicate + "> ?o}"; 1320 final TupleQuery tupleQuery = c.prepareTupleQuery( 1321 QueryLanguage.SPARQL, queryString); 1322 1323 // Evaluate the query, to get the results. 1324 final TupleQueryResult result = tupleQuery.evaluate(); 1325 1326 // We'll aggregate all the objects into this general list. 1327 final ArrayList<T> objs = new ArrayList<T>(); 1328 int n = 0; 1329 while (result.hasNext()) { 1330 final BindingSet bs = result.next(); 1331 final Value oo = bs.getBinding("o").getValue(); 1332 1333 // Get the value of the field if it's a primitive, or 1334 // it's URI if it's a complex object and add it to 1335 // the list. 1336 objs.add((T) this.getFieldValue(fieldType, 1337 oo, repo, fieldName, sequenceURI)); 1338 n++; 1339 } 1340 1341 // Copy the values into an array 1342 final T[] arr = (T[]) Array.newInstance(fieldType, n); 1343 for (int i = 0; i < arr.length; i++) 1344 arr[i] = objs.get(i); 1345 1346 return arr; 1347 } catch (final RepositoryException e) { 1348 e.printStackTrace(); 1349 } catch (final MalformedQueryException e) { 1350 e.printStackTrace(); 1351 } catch (final QueryEvaluationException e) { 1352 e.printStackTrace(); 1353 } 1354 1355 return null; 1356 } 1357 1358 /** 1359 * Attempts to find the correct class for the object URI given. If a class name 1360 * cannot be found in the repository, then the field is used to attempt to 1361 * instantiate a class. 1362 * 1363 * @param objectURI 1364 * The URI of the object in the repo 1365 * @param repo 1366 * The RDF repository 1367 * @return A class object. 1368 */ 1369 private Type getObjectClass(final String objectURI, final Repository repo) { 1370 String queryString = null; 1371 try { 1372 final RepositoryConnection c = repo.getConnection(); 1373 1374 queryString = "SELECT ?o WHERE {<" + objectURI + "> <" 1375 + RDFSerializer.RDF_OPENIMAJ_P_CLASSNAME + "> ?o.}"; 1376 final TupleQuery tupleQuery = c.prepareTupleQuery( 1377 QueryLanguage.SPARQL, queryString); 1378 final TupleQueryResult result = tupleQuery.evaluate(); 1379 1380 // System.out.println( queryString ); 1381 1382 // We'll look at all the results until we find a class we can 1383 // instantiate. Of course, we expect there to be only one in 1384 // reality. 1385 Class<?> clazz = null; 1386 boolean found = false; 1387 while (!found && result.hasNext()) { 1388 final Value value = result.next().getValue("o"); 1389 1390 try { 1391 // Try to find the class with the given name 1392 clazz = Class.forName(value.stringValue()); 1393 1394 // If the above succeeds, then we are done 1395 found = true; 1396 } catch (final ClassNotFoundException e) { 1397 e.printStackTrace(); 1398 } 1399 } 1400 1401 // Close the repo connection. 1402 c.close(); 1403 1404 // Return the class if we have one. 1405 if (clazz != null) { 1406 // System.out.println( clazz ); 1407 return clazz; 1408 } 1409 1410 } catch (final RepositoryException e) { 1411 System.out.println("Processing: " + queryString); 1412 e.printStackTrace(); 1413 } catch (final MalformedQueryException e) { 1414 System.out.println("Processing: " + queryString); 1415 e.printStackTrace(); 1416 } catch (final QueryEvaluationException e) { 1417 System.out.println("Processing: " + queryString); 1418 e.printStackTrace(); 1419 } 1420 1421 // Can't determine a class from the repository? Then we'll fall back 1422 // to the field's type. 1423 return null; 1424 } 1425 1426 /** 1427 * Returns a URI for the given object. If it cannot determine one, it will 1428 * return the default URI. It attempts to determine the object's URI by looking 1429 * for a getURI() method in the object. If it has one, it invokes it and uses 1430 * the return value as the object's URI, otherwise it will use the default URI 1431 * passed in via the method parameters. 1432 * 1433 * @param obj 1434 * The object 1435 * @param defaultURI 1436 * A default value for the URI 1437 * @return A URI for the object 1438 */ 1439 public URIImpl getObjectURI(final Object obj, final URIImpl defaultURI) { 1440 // Check whether the object has a getURI() method. If so, then 1441 // what we'll do is this: we'll call the getURI() method to retrieve the 1442 // URI of the object and use that as the subject URI instead of the 1443 // uri that's passed in via the method parameters. 1444 try { 1445 final Method method = obj.getClass().getMethod("getURI"); 1446 1447 // We'll call the method and use the toString() method to 1448 // get the URI as a string. We'll instantiate a new URIImpl with it. 1449 final URIImpl subject = new URIImpl(method.invoke(obj, 1450 (Object[]) null).toString()); 1451 1452 return subject; 1453 } catch (final NoSuchMethodException e1) { 1454 } catch (final SecurityException e1) { 1455 e1.printStackTrace(); 1456 } catch (final IllegalAccessException e) { 1457 e.printStackTrace(); 1458 } catch (final IllegalArgumentException e) { 1459 e.printStackTrace(); 1460 } catch (final InvocationTargetException e) { 1461 e.printStackTrace(); 1462 } 1463 1464 return defaultURI; 1465 } 1466 1467 /** 1468 * Checks whether the field value is a special field. If so, it will output it 1469 * using a separate device than the main serialization loop. Otherwise the 1470 * method returns FALSE and the main loop continues. 1471 * <p> 1472 * A special field is one which has some annotation that requires the output be 1473 * performed in a separate way. One example of this is the {@link TripleList} 1474 * annotation which forces the outputs of triples directly from the value of the 1475 * object. Similarly for the {@link RelationList} which forces the output of 1476 * triples from the pairs stored in the object's value. 1477 * 1478 * @param fieldValue 1479 * the value of the field 1480 * @param field 1481 * The field definition 1482 * @return 1483 */ 1484 private boolean outputSpecial(final Object fieldValue, final Field field, 1485 final URIImpl subjectURI) 1486 { 1487 // Check whether this field is a triple list. If it is, we'll take 1488 // the triples from the field (assuming it's the right type) and 1489 // bang them into the triple store. 1490 if (field.getAnnotation(TripleList.class) != null) { 1491 if (fieldValue instanceof Collection) { 1492 for (final Object o : (Collection<?>) fieldValue) { 1493 if (o instanceof Statement) 1494 this.addTriple((Statement) o); 1495 } 1496 } 1497 return true; // stop the main loop processing this field 1498 } else 1499 // If the field is a relation list, process each in turn 1500 if (field.getAnnotation(RelationList.class) != null) { 1501 if (fieldValue instanceof Collection) { 1502 int count = 0; 1503 for (final Object o : (Collection<?>) fieldValue) { 1504 if (o instanceof IndependentPair<?, ?>) { 1505 final IndependentPair<?, ?> ip = (IndependentPair<?, ?>) o; 1506 1507 Value ooo; 1508 if ((ooo = this.checkPrimitive(ip.getSecondObject())) != null) 1509 this.addTriple(new StatementImpl( 1510 subjectURI, 1511 new URIImpl(ip.getFirstObject().toString()), 1512 ooo)); 1513 else { 1514 final URI subjU = this.serializeAux( 1515 ip.getSecondObject(), subjectURI + "_" 1516 + field.getName() + "_" + count++); 1517 this.addTriple(new StatementImpl( 1518 subjectURI, 1519 new URIImpl(ip.getFirstObject().toString()), 1520 subjU)); 1521 } 1522 } else 1523 this.serializeAux(o, 1524 subjectURI + "_" + field.getName() + "_" 1525 + count++); 1526 } 1527 } 1528 return true; // stop the main loop processing this field 1529 } 1530 1531 return false; // continue on the main loop 1532 } 1533 1534 /** 1535 * Adds a single triple to some RDF serializer. 1536 * 1537 * @param t 1538 * The triple to add 1539 */ 1540 public void addTriple(final Statement t) { 1541 // Default implementation does nothing. Subclasses should override 1542 // this method and do something useful with created triples. 1543 // This method is not abstract just so users can create this object 1544 // for unserialization. 1545 } 1546}