/*
* Copyright (c) 2009-2012 Clark & Parsia, LLC. <http://www.clarkparsia.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.clarkparsia.empire;
import com.complexible.common.openrdf.model.Graphs;
import com.google.common.collect.Sets;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.openrdf.model.impl.GraphImpl;
import org.openrdf.model.impl.ValueFactoryImpl;
import org.openrdf.model.Graph;
import org.openrdf.model.util.GraphUtil;
import org.openrdf.model.vocabulary.RDFS;
import com.clarkparsia.empire.annotation.InvalidRdfException;
import com.clarkparsia.empire.annotation.RdfGenerator;
import com.clarkparsia.empire.annotation.Namespaces;
import com.clarkparsia.empire.annotation.RdfsClass;
import com.clarkparsia.empire.annotation.SupportsRdfIdImpl;
import com.clarkparsia.empire.annotation.RdfId;
import com.clarkparsia.empire.annotation.RdfProperty;
import com.clarkparsia.empire.api.BaseTestClass;
import com.clarkparsia.empire.api.TestPerson;
import com.clarkparsia.empire.ds.DataSourceException;
import static com.clarkparsia.empire.util.EmpireUtil.asPrimaryKey;
import com.clarkparsia.empire.api.TestDataSource;
import com.clarkparsia.empire.api.TestVocab;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import com.complexible.common.openrdf.vocabulary.FOAF;
import com.complexible.common.openrdf.vocabulary.DC;
import com.complexible.common.openrdf.model.ExtGraph;
import com.complexible.common.base.Dates;
import com.complexible.common.util.PrefixMapping;
import com.google.common.collect.Lists;
import javax.persistence.Entity;
import javax.persistence.Transient;
/**
* <p>Test cases for classes in the com.clarkparsia.empire.annotation package.</p>
*
* @author Michael Grove
* @since 0.1
*/
public class TestRdfConvert {
// TODO: tests for properties whose values are serialized as rdf:List's
// TODO: tests for stuff where an @RdfsClass extends another @RdfsClass
// TODO: tests for construct queries
// TODO: tests for named graph stuff
@BeforeClass
public static void beforeClass() {
EmpireOptions.STRONG_TYPING = true;
}
@Test(expected=InvalidRdfException.class)
public void testNoEntity() throws InvalidRdfException {
RdfGenerator.asRdf(new NoEntity());
}
@Test(expected=InvalidRdfException.class)
public void testNoRdfClass() throws InvalidRdfException {
RdfGenerator.asRdf(new NoRdfsClass());
}
@Test(expected=InvalidRdfException.class)
public void testNoSupports() throws InvalidRdfException {
RdfGenerator.asRdf(new NoSupports());
}
@Test(expected=InvalidRdfException.class)
public void testInvalidId() throws InvalidRdfException {
// this should not succeed, null values for the RdfId annotation are not allowed
RdfGenerator.asRdf(new TestPerson());
}
@Test(expected=InvalidRdfException.class)
public void testNoDefaultConstructor() throws InvalidRdfException, DataSourceException {
RdfGenerator.fromRdf(NoDefaultConstructor.class, URI.create("urn:foo"), new TestDataSource());
}
@Test(expected=InvalidRdfException.class)
public void testUnreachableConstructor() throws InvalidRdfException, DataSourceException {
RdfGenerator.fromRdf(UnreachableConstructor.class, URI.create("urn:foo"), new TestDataSource());
}
@Test(expected=InvalidRdfException.class)
public void testMultiInvalidId() throws InvalidRdfException {
RdfGenerator.asRdf(new MultipleRdfIds());
}
@Test
public void testNoStatements() throws InvalidRdfException, DataSourceException {
// we should at least return an object in these cases.
assertFalse(RdfGenerator.fromRdf(TestPerson.class, URI.create("urn:foo"), new TestDataSource()) == null);
}
@Test
public void testUnbalancedNamespaces() {
try {
RdfGenerator.asRdf(new UnbalancedNamespaces());
// the qname should not get expanded because the prefix was never asserted
assertEquals(PrefixMapping.GLOBAL.uri("notvalid:test"), "notvalid:test");
}
catch (InvalidRdfException e) {
e.printStackTrace();
fail(e.getMessage());
}
}
@Test
public void testConvert() throws Exception {
TestPerson aJoe = new TestPerson();
aJoe.setMBox("mailto:joe@example.org");
aJoe.setFirstName("Joe");
TestPerson aJane = new TestPerson();
aJane.setMBox("mailto:jane@example.org");
aJane.setFirstName("Jane");
TestPerson aPerson = new TestPerson();
aPerson.setMBox("mailto:bob@example.org");
try {
ExtGraph aGraph = Graphs.extend(RdfGenerator.asRdf(aPerson));
org.openrdf.model.URI aPersonURI = aGraph.getValueFactory().createURI(aPerson.getRdfId().toString());
assertEquals(aGraph.size(), 2);
// the statements should assert that the person is of type foaf:TestPerson
assertTrue(Sets.newHashSet(aGraph.getTypes(aPersonURI)).contains(FOAF.ontology().Person));
// and that the mbox is correct
assertEquals(Graphs.getLiteral(aGraph, aPersonURI, FOAF.ontology().mbox).get().getLabel(), aPerson.getMBox());
// now lets try with some more properties
aPerson.setWeight(123.45f);
aPerson.setBirthday(new Date());
aPerson.setFirstName("John");
aPerson.setLastName("Doe");
aPerson.setLikesVideoGames(true);
aPerson.setTitle("Sir");
aPerson.getKnows().add(aJoe);
aPerson.getKnows().add(aJane);
aPerson.setWeblogURI(URI.create("http://example.org"));
aGraph = Graphs.extend(RdfGenerator.asRdf(aPerson));
assertEquals((Float) Float.parseFloat(aGraph.getLiteral(aPersonURI, TestVocab.ontology().weight).get().getLabel()),
aPerson.getWeight());
// this tests if "inferring" from an annotated getter works
assertEquals(Boolean.valueOf(aGraph.getLiteral(aPersonURI, TestVocab.ontology().likesVideoGames).get().getLabel()),
aPerson.isLikesVideoGames());
// and this tests if 'inferring" from an annotated setter works
// also checking that it properly used the other namespace
assertEquals(aGraph.getLiteral(aPersonURI, DC.ontology().title).get().getLabel(),
aPerson.getTitle());
assertEquals(URI.create(GraphUtil.getUniqueObject(aGraph, aPersonURI, DC.ontology().publisher).stringValue()),
aPerson.getWeblogURI());
assertEquals(aGraph.getLiteral(aPersonURI, FOAF.ontology().firstName).get().getLabel(),
aPerson.getFirstName());
assertEquals(aGraph.getLiteral(aPersonURI, FOAF.ontology().surname).get().getLabel(),
aPerson.getLastName());
assertEquals(aGraph.getLiteral(aPersonURI, RDFS.LABEL).get().getLabel(),
aPerson.getLabel());
List aKnows = Lists.newArrayList(GraphUtil.getObjects(aGraph, aPersonURI, FOAF.ontology().knows));
assertEquals(aKnows.size(), 2);
assertTrue(aKnows.contains(aGraph.getValueFactory().createURI(aJane.getRdfId().toString())));
assertTrue(aKnows.contains(aGraph.getValueFactory().createURI(aJoe.getRdfId().toString())));
}
catch (InvalidRdfException e) {
e.printStackTrace();
fail(e.getMessage());
}
}
@Test
public void testRoundTrip() {
TestPerson aJoe = new TestPerson();
aJoe.setMBox("mailto:joe@example.org");
aJoe.setFirstName("Joe");
TestPerson aJane = new TestPerson();
aJane.setMBox("mailto:jane@example.org");
aJane.setFirstName("Jane");
TestPerson aBob = new TestPerson();
aBob.setBirthday(Dates.asDate("1980-01-01"));
aBob.setFirstName("Bob");
aBob.setLastName("Smith");
aBob.setLikesVideoGames(false);
aBob.setWeight(200.1f);
aBob.setWeblogURI(URI.create("http://someblog.example.org"));
aBob.setTitle("Mr");
aBob.setMBox("mailto:bob@example.org");
aBob.getKnows().add(aJoe);
aBob.getKnows().add(aJane);
try {
Graph aGraph = RdfGenerator.asRdf(aBob);
// this is the set of data that would normally be in the database
Graph aSourceGraph = new GraphImpl();
aSourceGraph.addAll(aGraph);
aSourceGraph.addAll(RdfGenerator.asRdf(aJoe));
aSourceGraph.addAll(RdfGenerator.asRdf(aJane));
TestPerson aPerson = RdfGenerator.fromRdf(TestPerson.class, aBob.getRdfId(), new TestDataSource(aSourceGraph));
assertEquals(aBob, aPerson);
// now lets test the round trip w/ the added trick of a circular dependency
aBob.setSpouse(aJane);
aGraph = RdfGenerator.asRdf(aBob);
// this is the set of data that would normally be in the database
aSourceGraph = new GraphImpl();
aSourceGraph.addAll(aGraph);
aSourceGraph.addAll(RdfGenerator.asRdf(aJoe));
aSourceGraph.addAll(RdfGenerator.asRdf(aJane));
aPerson = RdfGenerator.fromRdf(TestPerson.class, aBob.getRdfId(), new TestDataSource(aSourceGraph));
// should still be equal, should have re-used Jane
assertEquals(aBob, aPerson);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
@Test
public void testSupportsRdfIdImpl() {
SupportsRdfId aImpl = new SupportsRdfIdImpl();
URI aTestURI = URI.create("urn:some:identifier");
assertNull(aImpl.getRdfId());
aImpl.setRdfId(asPrimaryKey(aTestURI));
assertEquals(aImpl.getRdfId(), asPrimaryKey(aTestURI));
try {
aImpl.setRdfId(asPrimaryKey(URI.create("urn:new:id")));
fail("IllegalStateException expected");
}
catch (IllegalStateException e) {
// this is expected
}
assertEquals(aImpl, aImpl);
assertFalse(aImpl.equals(null));
assertFalse(aImpl.equals(""));
assertEquals(aImpl, new SupportsRdfIdImpl(aTestURI));
assertEquals(aImpl.hashCode(), new SupportsRdfIdImpl(aTestURI).hashCode());
assertFalse(aImpl.equals(new SupportsRdfIdImpl()));
assertFalse(aImpl.equals(new SupportsRdfIdImpl(URI.create("urn:new:id"))));
}
@Test
public void testThawByInterfacesAndCustomClasses() {
MyInterfaceImpl aImpl = new MyInterfaceImpl();
MyRootClass root = new MyRootClass();
root.addFoo( aImpl );
try {
Collection<Class<?>> klasses = new ArrayList<Class<?>>( );
klasses.add( MyInterfaceImpl.class );
klasses.add( MyRootClass.class );
RdfGenerator.init( klasses );
Graph aGraph = RdfGenerator.asRdf( root );
Graph aSourceGraph = new GraphImpl();
aSourceGraph.addAll(aGraph);
MyRootClass aRoot = RdfGenerator.fromRdf(MyRootClass.class, "urn:id:00", new TestDataSource(aSourceGraph));
assertSame( aRoot.getFoo().get( 0 ).getClass(), aImpl.getClass() );
} catch ( Exception e ) {
e.printStackTrace();
fail( e.getMessage() );
}
}
@Test
public void testTransience() {
TransientTest aObj = new TransientTest();
aObj.foo = "foo";
aObj.bar = "bar";
aObj.baz = "baz";
try {
ExtGraph aGraph = Graphs.extend(RdfGenerator.asRdf(aObj));
// we should have the normal field
assertTrue(aGraph.contains(null, ValueFactoryImpl.getInstance().createURI("urn:foo"), null));
// but neither of the transient ones
assertFalse(aGraph.contains(null, ValueFactoryImpl.getInstance().createURI("urn:bar"), null));
assertFalse(aGraph.contains(null, ValueFactoryImpl.getInstance().createURI("urn:baz"), null));
}
catch (InvalidRdfException e) {
e.printStackTrace();
fail(e.getMessage());
}
}
@RdfsClass("urn:TestClass")
@Entity
private static class NoDefaultConstructor extends BaseTestClass {
NoDefaultConstructor(String foo) {
setRdfId(asPrimaryKey(URI.create("urn:test:no:default")));
}
}
@Namespaces({"", "http://xmlns.com/foaf/0.1/",
"foaf", "http://xmlns.com/foaf/0.1/",
"dc", "http://purl.org/dc/elements/1.1/",
"notvalid"})
@RdfsClass("foaf:Person")
@Entity
private static class UnbalancedNamespaces extends BaseTestClass {
UnbalancedNamespaces() {
setRdfId(asPrimaryKey(URI.create("urn:test:unbalanced")));
}
}
@RdfsClass("urn:TestClass")
@Entity
private static class MultipleRdfIds {
@RdfId
private String one = "one";
@RdfId
private String two = "two";
}
@RdfsClass("urn:NoEntity")
public static class NoEntity extends BaseTestClass {
}
@Entity
@RdfsClass("urn:NoSupports")
public static class NoSupports {
@RdfId
private String one = "one";
}
@Namespaces({"foaf", "http://xmlns.com/foaf/0.1/"})
@Entity
public static class NoRdfsClass extends BaseTestClass {
@RdfId
@RdfProperty("foaf:name")
public String name;
}
@RdfsClass("urn:TestClass")
@Entity
public static class TransientTest extends BaseTestClass {
@RdfProperty("urn:foo")
private String foo;
@RdfProperty("urn:bar")
private transient String bar;
@Transient
@RdfProperty("urn:baz")
private String baz;
}
public abstract static class EmpireImpl implements SupportsRdfId, EmpireGenerated {
private RdfKey key;
private Graph allTriples;
private Graph instanceTriples;
public EmpireImpl( URIKey uriKey ) {
this.key = uriKey;
}
@Override
public RdfKey getRdfId() {
return key;
}
@Override
public void setRdfId( RdfKey theId ) {
this.key = theId;
}
@Override
public Graph getAllTriples() {
return allTriples;
}
@Override
public void setAllTriples( Graph aGraph ) {
this.allTriples = aGraph;
}
@Override
public Graph getInstanceTriples() {
return instanceTriples;
}
@Override
public void setInstanceTriples( Graph aGraph ) {
this.instanceTriples = aGraph;
}
}
@RdfsClass("urn:MyInterface")
@Entity
public static interface MyInterface extends SupportsRdfId {
}
@RdfsClass("urn:MyInterface")
@Entity
public static class MyInterfaceImpl extends EmpireImpl implements MyInterface, EmpireGenerated {
public MyInterfaceImpl() {
super( new URIKey( URI.create( "urn:sub:01" ) ) );
}
@Override
public Class getInterfaceClass() {
return MyInterface.class;
}
}
@RdfsClass("urn:MyClassOfSort")
@Entity
public static class MyRootClass extends EmpireImpl implements SupportsRdfId, EmpireGenerated {
List<MyInterface> foo;
public MyRootClass() {
super( new URIKey( URI.create( "urn:id:00" ) ) );
foo = new ArrayList<MyInterface>( );
}
@RdfProperty("urn:foo")
public List<MyInterface> getFoo() {
return foo;
}
public void setFoo( List<MyInterface> foo ) {
this.foo = foo;
}
@Override
public Class getInterfaceClass() {
return EmpireGenerated.class;
}
public void addFoo( MyInterface aImpl ) {
this.foo.add( aImpl );
}
}
}