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