com.clarkparsia.empire.annotation.RdfGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.clarkparsia.empire.annotation.RdfGenerator.java

Source

/*
 * 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.annotation;

import com.complexible.common.openrdf.model.Graphs;
import com.google.common.base.Optional;
import com.google.common.collect.ObjectArrays;

import org.apache.commons.lang3.StringUtils;
import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.Resource;
import org.openrdf.model.Value;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.Statement;
import org.openrdf.model.Graph;
import org.openrdf.model.util.GraphUtil;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.model.vocabulary.XMLSchema;
import org.openrdf.model.vocabulary.RDFS;

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Set;
import java.util.Iterator;
import java.util.Locale;
import java.util.ArrayList;
import java.net.URISyntaxException;

import org.openrdf.model.impl.URIImpl;
import org.openrdf.model.impl.ValueFactoryImpl;

import com.clarkparsia.empire.ds.DataSource;
import com.clarkparsia.empire.ds.DataSourceException;
import com.clarkparsia.empire.ds.QueryException;
import com.clarkparsia.empire.ds.DataSourceUtil;
import com.clarkparsia.empire.EmpireOptions;
import com.clarkparsia.empire.EmpireGenerated;
import com.clarkparsia.empire.SupportsRdfId;
import com.clarkparsia.empire.Empire;
import com.clarkparsia.empire.Dialect;
import com.clarkparsia.empire.annotation.runtime.Proxy;
import com.clarkparsia.empire.impl.serql.SerqlDialect;

import static com.clarkparsia.empire.util.BeanReflectUtil.set;
import static com.clarkparsia.empire.util.BeanReflectUtil.setAccessible;
import static com.clarkparsia.empire.util.BeanReflectUtil.getAnnotatedFields;
import static com.clarkparsia.empire.util.BeanReflectUtil.getAnnotatedGetters;
import static com.clarkparsia.empire.util.BeanReflectUtil.getAnnotatedSetters;
import static com.clarkparsia.empire.util.BeanReflectUtil.get;

import com.clarkparsia.empire.util.BeanReflectUtil;
import com.clarkparsia.empire.util.EmpireUtil;

import static com.clarkparsia.empire.util.EmpireUtil.asPrimaryKey;

import com.complexible.common.openrdf.util.ResourceBuilder;
import com.complexible.common.openrdf.util.GraphBuilder;
import com.complexible.common.util.PrefixMapping;
import com.complexible.common.base.Strings2;
import com.complexible.common.base.Dates;
import com.complexible.common.net.NetUtils;
import com.complexible.common.collect.Iterables2;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.inject.ProvisionException;
import com.google.inject.ConfigurationException;

import javax.persistence.Entity;
import javax.persistence.Transient;

import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyObject;
import javassist.util.proxy.MethodFilter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl;

/**
 * <p>Description: Utility for creating RDF from a compliant Java Bean, and for turning RDF (the results of a describe
 * on a given rdf:ID into a KB) into a Java bean.</p>
 * <p>Usage:<br/>
 * <code><pre>
 *   MyClass aObj = new MyClass();
 *
 *   // set some data on the object
 *   KB.add(RdfGenerator.toRdf(aObj));
 *
 *   MyClass aObjCopy = RdfGenerator.fromRdf(MyClass.class, aObj.getRdfId(), KB);
 *
 *   // this will print true
 *   System.out.println(aObj.equals(aObjCopy));
 * </pre>
 * </code>
 * </p>
 * <p>
 * Compliant classes must be annotated with the {@link Entity} JPA annotation, the {@link RdfsClass} annotation,
 * and must implement the {@link SupportsRdfId} interface.</p>
 *
 * @author   Michael Grove
 * @since   0.1
 * @version 0.7.3
 */
public final class RdfGenerator {

    /**
     * Global ValueFactory to use for converting Java values into sesame objects for serialization to RDF
     */
    private static final ValueFactory FACTORY = new ValueFactoryImpl();

    private static final ContainsResourceValues CONTAINS_RESOURCES = new ContainsResourceValues();

    private static final LanguageFilter LANG_FILTER = new LanguageFilter(getLanguageForLocale());

    /**
     * The logger
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(RdfGenerator.class.getName());

    /**
     * Map from rdf:type URI's to the Java class which corresponds to that resource.
     */
    private final static Multimap<URI, Class> TYPE_TO_CLASS = HashMultimap.create();

    /**
     * Map to keep a record of what instances are currently being created in order to prevent cycles.  Keys are the
     * identifiers of the instances and the values are the instances
     */
    public final static Map<Object, Object> OBJECT_M = new HashMap<Object, Object>();

    private final static Set<Class<?>> REGISTERED_FOR_NS = new HashSet<Class<?>>();

    /**
     * Cache the AccessibleObjects to avoid repeated inspections
     */
    private final static Map<Class<?>, Map<URI, AccessibleObject>> ACCESSORS_BY_CLASS = new HashMap<Class<?>, Map<URI, AccessibleObject>>();

    /**
     * Initialize some parameters in the RdfGenerator.  This caches namespace and type mapping information locally
     * which will be used in subsequent rdf generation requests.
     * @param theClasses the list of classes to be handled by the RdfGenerator
     */
    public static synchronized void init(Collection<Class<?>> theClasses) {
        for (Class<?> aClass : theClasses) {
            RdfsClass aAnnotation = aClass.getAnnotation(RdfsClass.class);

            if (aAnnotation != null) {
                addNamespaces(aClass);

                TYPE_TO_CLASS.put(FACTORY.createURI(PrefixMapping.GLOBAL.uri(aAnnotation.value())), aClass);
            }
        }
    }

    /**
     * Create an instance of the specified class and instantiate it's data from the given data source using the RDF
     * instance specified by the given URI
     * @param theClass the class to create
     * @param theKey the id of the RDF individual containing the data for the new instance
     * @param theSource the KB to get the RDF data from
     * @param <T> the type of the instance to create
     * @return a new instance
     * @throws InvalidRdfException thrown if the class does not support RDF JPA operations, or does not provide sufficient access to its fields/data.
     * @throws DataSourceException thrown if there is an error while retrieving data from the graph
     */
    public static <T> T fromRdf(Class<T> theClass, String theKey, DataSource theSource)
            throws InvalidRdfException, DataSourceException {
        return fromRdf(theClass, EmpireUtil.asPrimaryKey(theKey), theSource);
    }

    /**
     * Create an instance of the specified class and instantiate it's data from the given data source using the RDF
     * instance specified by the given URI
     * @param theClass the class to create
     * @param theURI the id of the RDF individual containing the data for the new instance
     * @param theSource the KB to get the RDF data from
     * @param <T> the type of the instance to create
     * @return a new instance
     * @throws InvalidRdfException thrown if the class does not support RDF JPA operations, or does not provide sufficient access to its fields/data.
     * @throws DataSourceException thrown if there is an error while retrieving data from the graph
     */
    public static <T> T fromRdf(Class<T> theClass, java.net.URI theURI, DataSource theSource)
            throws InvalidRdfException, DataSourceException {
        return fromRdf(theClass, new SupportsRdfId.URIKey(theURI), theSource);
    }

    /**
     * Create an instance of the specified class and instantiate it's data from the given data source using the RDF
     * instance specified by the given URI
     * @param theClass the class to create
     * @param theId the id of the RDF individual containing the data for the new instance
     * @param theSource the KB to get the RDF data from
     * @param <T> the type of the instance to create
     * @return a new instance
     * @throws InvalidRdfException thrown if the class does not support RDF JPA operations, or does not provide sufficient access to its fields/data.
     * @throws DataSourceException thrown if there is an error while retrieving data from the graph
     */
    public static <T> T fromRdf(Class<T> theClass, SupportsRdfId.RdfKey theId, DataSource theSource)
            throws InvalidRdfException, DataSourceException {
        T aObj;
        long start = System.currentTimeMillis(), start1 = System.currentTimeMillis();
        try {
            aObj = Empire.get().instance(theClass);
        } catch (ConfigurationException ex) {
            aObj = null;
        } catch (ProvisionException ex) {
            aObj = null;
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Tried to get instance of class in {} ms ", (System.currentTimeMillis() - start));
        }
        start = System.currentTimeMillis();

        if (aObj == null) {
            // this means Guice construction failed, which is not surprising since that's not going to be the default.
            // so we'll try our own reflect based creation or create bytecode for an interface.

            try {
                long istart = System.currentTimeMillis();
                if (theClass.isInterface() || Modifier.isAbstract(theClass.getModifiers())) {
                    aObj = com.clarkparsia.empire.codegen.InstanceGenerator.generateInstanceClass(theClass)
                            .newInstance();

                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("CodeGenerated instance in {} ms. ", (System.currentTimeMillis() - istart));
                    }
                } else {
                    aObj = theClass.newInstance();

                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("CodeGenerated instance in {} ms. ", (System.currentTimeMillis() - istart));
                    }
                }
            } catch (InstantiationException e) {
                throw new InvalidRdfException("Cannot create instance of bean, should have a default constructor.",
                        e);
            } catch (IllegalAccessException e) {
                throw new InvalidRdfException("Could not access default constructor for class: " + theClass, e);
            } catch (Exception e) {
                throw new InvalidRdfException("Cannot create an instance of bean", e);
            }

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Got reflect instance of class {} ms ", (System.currentTimeMillis() - start1));
            }

            start = System.currentTimeMillis();
        }

        asSupportsRdfId(aObj).setRdfId(theId);

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Has rdfId {} ms", (System.currentTimeMillis() - start1));
        }

        start = System.currentTimeMillis();

        Class<T> aNewClass = determineClass(theClass, aObj, theSource);

        if (!aNewClass.equals(aObj.getClass())) {
            try {
                aObj = aNewClass.newInstance();
            } catch (InstantiationException e) {
                throw new InvalidRdfException("Cannot create instance of bean, should have a default constructor.",
                        e);
            } catch (IllegalAccessException e) {
                throw new InvalidRdfException("Could not access default constructor for class: " + theClass, e);
            } catch (Exception e) {
                throw new InvalidRdfException("Cannot create an instance of bean", e);
            }
            asSupportsRdfId(aObj).setRdfId(theId);
        }

        T fromRdf = fromRdf(aObj, theSource);
        return fromRdf;
    }

    private static <T> Class<T> determineClass(Class<T> theOrigClass, T theObj, DataSource theSource)
            throws InvalidRdfException, DataSourceException {
        return determineClass(theOrigClass, theObj, theSource, true);
    }

    @SuppressWarnings("unchecked")
    private static <T> Class<T> determineClass(Class<T> theOrigClass, T theObj, DataSource theSource,
            boolean instanceClass) throws InvalidRdfException, DataSourceException {
        Class aResult = theOrigClass;
        final SupportsRdfId aTmpSupportsRdfId = asSupportsRdfId(theObj);

        //      ExtGraph aGraph = new ExtGraph(DataSourceUtil.describe(theSource, theObj));

        // ======================================== start speedup PATCH ================================================== 
        // get class from uri without quering db

        //      final Collection<Value> aTypes = DataSourceUtil.getValues(theSource, EmpireUtil.asResource(EmpireUtil.asSupportsRdfId(theObj)), RDF.TYPE);

        String rdfId = EmpireUtil.asSupportsRdfId(theObj).getRdfId().toString();
        final Collection<Value> aTypes = Collections
                .singletonList(new URIImpl(StringUtils.substringBeforeLast(rdfId, ":")));
        // ======================================== end speedup PATCH ========================================

        // right now, our best match is the original class (we will refine later)

        //      final Resource aTmpRes = EmpireUtil.asResource(aTmpSupportsRdfId);

        // iterate for all rdf:type triples in the data
        // There may be multiple rdf:type triples, which can then translate onto multiple candidate Java classes
        // some of the Java classes may belong to the same class hierarchy, whereas others can have no common
        // super class (other than java.lang.Object)
        for (Value aValue : aTypes) {
            if (!(aValue instanceof URI)) {
                // there is no URI in the object position of rdf:type
                // ignore that data
                continue;
            }

            URI aType = (URI) aValue;

            for (Class aCandidateClass : TYPE_TO_CLASS.get(aType)) {
                if (aCandidateClass.equals(aResult)) {
                    // it is mapped to the same Java class, that we have; ignore
                    continue;
                }

                // at this point we found an rdf:type triple that resolves to a different Java class than we have
                // we are only going to accept this candidate class if it is a subclass of the current Java class
                // (doing otherwise, may cause class cast exceptions)

                if (aResult.isAssignableFrom(aCandidateClass)) {
                    aResult = aCandidateClass;
                }
            }
        }

        try {
            if (instanceClass) {
                if (aResult.isInterface() || Modifier.isAbstract(aResult.getModifiers())
                        || !EmpireGenerated.class.isAssignableFrom(aResult)) {
                    aResult = com.clarkparsia.empire.codegen.InstanceGenerator.generateInstanceClass(aResult);
                }
            }
        } catch (Exception e) {
            throw new InvalidRdfException("Cannot generate a class for a bean", e);
        }

        return aResult;
    }

    /**
     * Populate the fields of the current instance from the RDF indiviual with the given URI
     * @param theObj the Java object to populate
     * @param theSource the KB to get the RDF data from
     * @param <T> the type of the class being populated
     * @return theObj, populated from the specified DataSource
     * @throws InvalidRdfException thrown if the object does not support the RDF JPA API.
     * @throws DataSourceException thrown if there is an error retrieving data from the database
     */
    @SuppressWarnings("unchecked")
    private synchronized static <T> T fromRdf(T theObj, DataSource theSource)
            throws InvalidRdfException, DataSourceException {
        final SupportsRdfId aTmpSupportsRdfId = asSupportsRdfId(theObj);
        final SupportsRdfId.RdfKey theKeyObj = aTmpSupportsRdfId.getRdfId();
        long start = System.currentTimeMillis(), start1 = System.currentTimeMillis();
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Converting {} to RDF.", theObj);
        }

        if (OBJECT_M.containsKey(theKeyObj)) {
            // TODO: this is probably a safe cast, i dont see how something w/ the same URI, which should be the same
            // object would change types
            return (T) OBJECT_M.get(theKeyObj);
        }
        try {

            OBJECT_M.put(theKeyObj, theObj);

            Graph aGraph = DataSourceUtil.describe(theSource, theObj);
            if (aGraph.size() == 0) {
                return theObj;
            }

            final Resource aTmpRes = EmpireUtil.asResource(aTmpSupportsRdfId);
            Set<URI> aProps = new HashSet<URI>();
            Iterator<Statement> sIter = aGraph.match(aTmpRes, null, null);

            while (sIter.hasNext()) {
                Statement aStmt = sIter.next();
                aProps.add(aStmt.getPredicate());
            }

            final SupportsRdfId aSupportsRdfId = asSupportsRdfId(theObj);

            final EmpireGenerated aEmpireGenerated = asEmpireGenerated(theObj);
            aEmpireGenerated.setAllTriples(aGraph);

            OBJECT_M.put(theKeyObj, theObj);
            final Resource aRes = EmpireUtil.asResource(aSupportsRdfId);

            addNamespaces(theObj.getClass());
            Map<URI, AccessibleObject> theCachedMap = ACCESSORS_BY_CLASS.get(theObj.getClass());

            if (theCachedMap == null) {
                theCachedMap = cacheAccessibles(theObj.getClass(), aRes);
            }
            Set<URI> aUsedProps = new HashSet<URI>();
            for (URI aProp : aProps) {
                AccessibleObject aAccess = theCachedMap.get(aProp);

                if (aAccess == null && RDF.TYPE.equals(aProp)) {
                    // TODO: the following block should be entirely removed (leaving continue only)
                    // right now, leaving it until the code review: code review before removing the following block

                    // my understanding is that the following block was only necessary when having a support for a single-typed objects,
                    // which is no longer the case                

                    // we can skip the rdf:type property.  it's basically assigned in the @RdfsClass annotation on the
                    // java class, so we can figure it out later if need be. TODO: of course, if something has multiple types
                    // that information is lost, which is not good.

                    /*
                    URI aType = (URI) aGraph.getValue(aRes, aProp);
                    if (!TYPE_TO_CLASS.containsKey(aType) ||
                       !TYPE_TO_CLASS.get(aType).isAssignableFrom(theObj.getClass())) {
                        
                       if (TYPE_TO_CLASS.containsKey(aType) && !TYPE_TO_CLASS.get(aType).getName().equals(theObj.getClass().getName())) {
                          // TODO: this might just be an error
                          LOGGER.warn("Asserted rdf:type of the individual does not match the rdf:type annotation on the object. " + aType + " " + TYPE_TO_CLASS.get(aType) + " " + theObj.getClass() + " " +TYPE_TO_CLASS.get(aType).isAssignableFrom(theObj.getClass())+ " " +TYPE_TO_CLASS.get(aType).equals(theObj.getClass()) + " " + TYPE_TO_CLASS.get(aType).getName().equals(theObj.getClass().getName()));
                       }
                       else {
                          // if they're not equals() or isAssignableFrom, but have the same name, this is usually
                          // means that the class loaders don't match.  so probably not an error, so no warning.
                       }
                    }
                    */

                    continue;
                } else if (aAccess == null) {
                    // this must be data that is not covered by the bean (perhaps accessible by a different view/bean for a differnent type of an individual)               
                    continue;
                }

                aUsedProps.add(aProp);

                ToObjectFunction aFunc = new ToObjectFunction(theSource, aRes, aAccess, aProp);
                Object aValue = aFunc.apply(GraphUtil.getObjects(aGraph, aRes, aProp));
                boolean aOldAccess = aAccess.isAccessible();

                try {
                    setAccessible(aAccess, true);
                    set(aAccess, theObj, aValue);
                } catch (InvocationTargetException e) {
                    // oh crap
                    throw new InvalidRdfException(e);
                } catch (IllegalAccessException e) {
                    // this should not happen since we toggle the accessibility of the field, but we'll re-throw regardless
                    throw new InvalidRdfException(e);
                } catch (IllegalArgumentException e) {
                    // this is "likely" to happen.  we'll get this exception if the rdf does not match the java.  for example
                    // if something is specified to be an int in the java class, but it typed as a float (though down conversion
                    // in that case might work) the set call will fail.
                    // TODO: shouldnt this be an error?

                    //               LOGGER.info("Probable type mismatch: {} {}", aValue, aAccess);
                } catch (RuntimeException e) {
                    // TODO: i dont like keying on a RuntimeException here to get the error condition, but since the
                    // Function interface does not throw anything, this is the best we can do.  maybe consider a
                    // version of the Function interface that has a throws clause, it would make this more clear.

                    // this was probably an error converting from a Value to an Object
                    throw new InvalidRdfException(e);
                } finally {
                    setAccessible(aAccess, aOldAccess);
                }
            }

            sIter = aGraph.match(aTmpRes, null, null);
            Graph aInstanceTriples = Graphs.newGraph();

            while (sIter.hasNext()) {
                Statement aStmt = sIter.next();

                if (aUsedProps.contains(aStmt.getPredicate())) {
                    aInstanceTriples.add(aStmt);
                }
            }

            aEmpireGenerated.setInstanceTriples(aInstanceTriples);

            return theObj;
        } finally {
            OBJECT_M.remove(theKeyObj);
        }
    }

    public static Map<URI, AccessibleObject> cacheAccessibles(Class theClass, final Resource aRes) {
        final Map<URI, AccessibleObject> aAccessMap = new HashMap<URI, AccessibleObject>();

        Collection<Field> aFields = getAnnotatedFields(theClass);
        Collection<Method> aMethods = getAnnotatedSetters(theClass, true);

        Iterables2.each(aFields, new Predicate<Field>() {
            public boolean apply(final Field theField) {
                if (theField.getAnnotation(RdfProperty.class) != null) {
                    URI theURI = FACTORY
                            .createURI(PrefixMapping.GLOBAL.uri(theField.getAnnotation(RdfProperty.class).value()));
                    aAccessMap.put(theURI, theField);
                } else {
                    String aBase = "urn:empire:clark-parsia:";
                    if (aRes instanceof URI) {
                        aBase = ((URI) aRes).getNamespace();
                    }

                    aAccessMap.put(FACTORY.createURI(aBase + theField.getName()), theField);
                }

                return true;
            }
        });

        Iterables2.each(aMethods, new Predicate<Method>() {
            public boolean apply(final Method theMethod) {
                RdfProperty aAnnotation = BeanReflectUtil.getAnnotation(theMethod, RdfProperty.class);
                if (aAnnotation != null) {
                    URI theURI = FACTORY.createURI(PrefixMapping.GLOBAL.uri(aAnnotation.value()));
                    aAccessMap.put(theURI, theMethod);
                }

                return true;
            }
        });

        ACCESSORS_BY_CLASS.put(theClass, aAccessMap);
        return aAccessMap;
    }

    /**
     * Return the RdfClass annotation on the object.
     * @param theObj the object to get that annotation from
     * @return the objects' RdfClass annotation
     * @throws InvalidRdfException thrown if the object does not have the required annotation, does not have an @Entity
     * annotation, or does not {@link SupportsRdfId support Rdf Id's}
     */
    private static RdfsClass asValidRdfClass(Object theObj) throws InvalidRdfException {
        if (!BeanReflectUtil.hasAnnotation(theObj.getClass(), RdfsClass.class)) {
            throw new InvalidRdfException("Specified value is not an RdfsClass object");
        }

        if (EmpireOptions.ENFORCE_ENTITY_ANNOTATION
                && !BeanReflectUtil.hasAnnotation(theObj.getClass(), Entity.class)) {
            throw new InvalidRdfException("Specified value is not a JPA Entity object");
        }

        // verify that it supports rdf id's
        asSupportsRdfId(theObj);

        return BeanReflectUtil.getAnnotation(theObj.getClass(), RdfsClass.class);
    }

    /**
     * Return the object casted to {@link SupportsRdfId}
     * @param theObj the object to cast
     * @return the object, casted to the interface
     * @throws InvalidRdfException thrown if the object does not implement the interface
     */
    private static SupportsRdfId asSupportsRdfId(Object theObj) throws InvalidRdfException {
        if (!(theObj instanceof SupportsRdfId)) {
            throw new InvalidRdfException(
                    "Object of type '" + (theObj.getClass().getName()) + "' does not implements SupportsRdfId.");
        } else {
            return (SupportsRdfId) theObj;
        }
    }

    private static EmpireGenerated asEmpireGenerated(Object theObj) throws InvalidRdfException {
        if (!(theObj instanceof EmpireGenerated)) {
            throw new InvalidRdfException(
                    "Object of type '" + (theObj.getClass().getName()) + "' does not implements EmpireGenerated.");
        } else {
            return (EmpireGenerated) theObj;
        }
    }

    /**
     * Given an object, return it's rdf:ID.  If it already has an id, that will be returned, otherwise the id
     * will either be generated from the data, using the {@link RdfId} annotation as a guide, or it will auto-generate one.
     * @param theObj the object
     * @return the object's rdf:Id
     * @throws InvalidRdfException thrown if the object does not support the minimum to create or retrieve an rdf:ID
     * @see SupportsRdfId
     */
    public static Resource id(Object theObj) throws InvalidRdfException {
        SupportsRdfId aSupport = asSupportsRdfId(theObj);

        if (aSupport.getRdfId() != null) {
            return EmpireUtil.asResource(aSupport);
        }

        Field aIdField = BeanReflectUtil.getIdField(theObj.getClass());

        String aValue = hash(Strings2.getRandomString(10));
        String aNS = RdfId.DEFAULT;

        URI aURI = FACTORY.createURI(aNS + aValue);

        if (aIdField != null && !aIdField.getAnnotation(RdfId.class).namespace().equals("")) {
            aNS = aIdField.getAnnotation(RdfId.class).namespace();
        }

        if (aIdField != null) {
            boolean aOldAccess = aIdField.isAccessible();
            aIdField.setAccessible(true);

            try {
                if (aIdField.get(theObj) == null) {
                    throw new InvalidRdfException("id field must have a value");
                }

                Object aValObj = aIdField.get(theObj);

                aValue = Strings2.urlEncode(aValObj.toString());

                if (aValObj instanceof java.net.URI || NetUtils.isURI(aValObj.toString())) {
                    try {
                        aURI = FACTORY.createURI(aValObj.toString());
                    } catch (IllegalArgumentException e) {
                        // sometimes sesame disagrees w/ Java about what a valid URI is.  so we'll have to try
                        // and construct a URI from the possible fragment
                        aURI = FACTORY.createURI(aNS + aValue);
                    }
                } else {
                    //aValue = hash(aValObj);
                    aURI = FACTORY.createURI(aNS + aValue);
                }
            } catch (IllegalAccessException ex) {
                throw new InvalidRdfException(ex);
            }

            aIdField.setAccessible(aOldAccess);
        }

        aSupport.setRdfId(new SupportsRdfId.URIKey(java.net.URI.create(aURI.toString())));

        return aURI;
    }

    /**
     * Scan the object for {@link Namespaces} annotations and add them to the current list of known namespaces
     * @param theObj the object to scan.
     */
    public static void addNamespaces(Class<?> theObj) {
        if (theObj == null || REGISTERED_FOR_NS.contains(theObj)) {
            return;
        }

        REGISTERED_FOR_NS.add(theObj);

        Namespaces aNS = BeanReflectUtil.getAnnotation(theObj, Namespaces.class);

        if (aNS == null) {
            return;
        }

        int aIndex = 0;
        while (aIndex + 1 < aNS.value().length) {
            String aPrefix = aNS.value()[aIndex];
            String aURI = aNS.value()[aIndex + 1];

            // TODO: maybe have a local version of this, this will add a global namespace, and could potentially
            // overwrite global things that use the same prefix but different uris, which would be bad
            PrefixMapping.GLOBAL.addMapping(aPrefix, aURI);
            aIndex += 2;
        }
    }

    /**
     * Return the given Java bean as a set of RDF triples
     * @param theObj the object
     * @return the object represented as RDF triples
     * @throws InvalidRdfException thrown if the object cannot be transformed into RDF.
     */
    public static Graph asRdf(final Object theObj) throws InvalidRdfException {
        if (theObj == null) {
            return null;
        }

        Object aObj = theObj;

        if (aObj instanceof ProxyHandler) {
            aObj = ((ProxyHandler) aObj).mProxy.value();
        } else {
            try {
                if (aObj.getClass().getDeclaredField("handler") != null) {
                    Field aProxy = aObj.getClass().getDeclaredField("handler");
                    aObj = ((ProxyHandler) BeanReflectUtil.safeGet(aProxy, aObj)).mProxy.value();
                }
            } catch (InvocationTargetException e) {
                // this is probably an error, we know its a proxy object, but can't get the proxied object
                throw new InvalidRdfException("Could not access proxy object", e);
            } catch (NoSuchFieldException e) {
                // this is probably ok.
            }
        }

        RdfsClass aClass = asValidRdfClass(aObj);

        Resource aSubj = id(aObj);

        addNamespaces(aObj.getClass());

        GraphBuilder aBuilder = new GraphBuilder();

        Collection<AccessibleObject> aAccessors = new HashSet<AccessibleObject>();
        aAccessors.addAll(getAnnotatedFields(aObj.getClass()));
        aAccessors.addAll(getAnnotatedGetters(aObj.getClass(), true));

        try {
            ResourceBuilder aRes = aBuilder.instance(
                    aBuilder.getValueFactory().createURI(PrefixMapping.GLOBAL.uri(aClass.value())), aSubj);

            for (AccessibleObject aAccess : aAccessors) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Getting rdf for : {}", aAccess);
                }

                AsValueFunction aFunc = new AsValueFunction(aAccess);

                if (aAccess.isAnnotationPresent(Transient.class)
                        || (aAccess instanceof Field && Modifier.isTransient(((Field) aAccess).getModifiers()))) {

                    // transient fields or accessors with the Transient annotation do not get converted.
                    continue;
                }

                RdfProperty aPropertyAnnotation = BeanReflectUtil.getAnnotation(aAccess, RdfProperty.class);
                String aBase = "urn:empire:clark-parsia:";
                if (aRes instanceof URI) {
                    aBase = ((URI) aRes).getNamespace();
                }

                URI aProperty = aPropertyAnnotation != null
                        ? aBuilder.getValueFactory()
                                .createURI(PrefixMapping.GLOBAL.uri(aPropertyAnnotation.value()))
                        : (aAccess instanceof Field
                                ? aBuilder.getValueFactory().createURI(aBase + ((Field) aAccess).getName())
                                : null);

                boolean aOldAccess = aAccess.isAccessible();
                setAccessible(aAccess, true);

                Object aValue = get(aAccess, aObj);

                setAccessible(aAccess, aOldAccess);

                if (aValue == null || aValue.toString().equals("")) {
                    continue;
                } else if (Collection.class.isAssignableFrom(aValue.getClass())) {
                    @SuppressWarnings("unchecked")
                    List<Value> aValueList = asList(aAccess, (Collection<?>) Collection.class.cast(aValue));

                    if (aValueList.isEmpty()) {
                        continue;
                    }

                    if (aPropertyAnnotation.isList()) {
                        aRes.addProperty(aProperty, aValueList);
                    } else {
                        for (Value aVal : aValueList) {
                            aRes.addProperty(aProperty, aVal);
                        }
                    }
                } else {
                    aRes.addProperty(aProperty, aFunc.apply(aValue));
                }
            }
        } catch (IllegalAccessException e) {
            throw new InvalidRdfException(e);
        } catch (RuntimeException e) {
            throw new InvalidRdfException(e);
        } catch (InvocationTargetException e) {
            throw new InvalidRdfException("Cannot invoke method", e);
        }

        return aBuilder.graph();
    }

    /**
     * Transform a list of Java Objects into the corresponding RDF values
     * @param theAccess the accessor for the value
     * @param theCollection the collection to transform
     * @return the collection as a list of RDF values
     * @throws InvalidRdfException thrown if any of the values cannot be transformed
     */
    private static List<Value> asList(AccessibleObject theAccess, Collection<?> theCollection)
            throws InvalidRdfException {
        try {
            return Lists.newArrayList(Collections2.transform(theCollection, new AsValueFunction(theAccess)));
        } catch (RuntimeException e) {
            throw new InvalidRdfException(e.getMessage());
        }
    }

    /**
     * Return a base64 encoded md5 hash of the given object
     * @param theObj the object to hash
     * @return the hashed version of the object.
     */
    private static String hash(Object theObj) {
        return Strings2.hex(Strings2.md5(theObj.toString()));
    }

    /**
     * Javassist {@link MethodHandler} implementation for method proxying.
     */
    private static class CollectionProxyHandler implements MethodHandler {

        /**
         * The proxy object which wraps the instance being proxied.
         */
        private CollectionProxy mProxy;

        /**
         * Create a new ProxyHandler
         * @param theProxy the proxy object
         */
        private CollectionProxyHandler(final CollectionProxy theProxy) {
            mProxy = theProxy;
        }

        /**
         * Delegates the methods to the Proxy
         * @inheritDoc
         */
        public Object invoke(final Object theThis, final Method theMethod, final Method theProxyMethod,
                final Object[] theArgs) throws Throwable {
            return theMethod.invoke(mProxy.value(), theArgs);
        }
    }

    private static class CollectionProxy {
        private Collection mCollection;
        private AccessibleObject mField;
        private Collection<Value> theList;
        private ValueToObject valueToObject;

        public CollectionProxy(final AccessibleObject theField, final Collection<Value> theTheList,
                final ValueToObject theValueToObject) {
            mField = theField;
            theList = theTheList;
            valueToObject = theValueToObject;
        }

        private void init() {
            Collection<Object> aValues = BeanReflectUtil
                    .instantiateCollectionFromField(BeanReflectUtil.classFrom(mField));

            for (Value aValue : theList) {
                Object aListValue = valueToObject.apply(aValue);

                if (aListValue == null) {
                    throw new RuntimeException("Error converting a list value.");
                }

                aValues.add(aListValue);
            }

            mCollection = aValues;
        }

        public Collection value() {
            if (mCollection == null) {
                init();
                theList.clear();

                theList = null;
                mField = null;
                valueToObject = null;
            }

            return mCollection;
        }
    }

    /**
     * Enabling this seems to use more memory than per-object proxying (or none at all).  Is javassist leaking memory?
     * Experimental option, not currently used.
     */
    @Deprecated
    public static final boolean PROXY_COLLECTIONS = false;

    /**
     * Implementation of the function interface to turn a Collection of RDF values into Java bean(s).
     */
    private static class ToObjectFunction implements Function<Collection<Value>, Object> {
        /**
         * Function to turn a single value into an object
         */
        private ValueToObject valueToObject;

        /**
         * Reference to the Type which the values will be assigned
         */
        private AccessibleObject mField;

        public ToObjectFunction(final DataSource theSource, Resource theResource, final AccessibleObject theField,
                final URI theProp) {
            valueToObject = new ValueToObject(theSource, theResource, theField, theProp);

            mField = theField;
        }

        public Object apply(final Collection<Value> theList) {
            if (theList == null || theList.isEmpty()) {
                return BeanReflectUtil.instantiateCollectionFromField(BeanReflectUtil.classFrom(mField));
            }
            long start = System.currentTimeMillis();
            if (Collection.class.isAssignableFrom(BeanReflectUtil.classFrom(mField))) {
                try {
                    if (PROXY_COLLECTIONS && !BeanReflectUtil
                            .isPrimitive(refineClass(mField, BeanReflectUtil.classFrom(mField), null, null))) {
                        Object aColType = BeanReflectUtil
                                .instantiateCollectionFromField(BeanReflectUtil.classFrom(mField));

                        ProxyFactory aFactory = new ProxyFactory();
                        aFactory.setInterfaces(aColType.getClass().getInterfaces());
                        aFactory.setSuperclass(aColType.getClass());
                        aFactory.setFilter(METHOD_FILTER);

                        Object aResult = aFactory.createClass().newInstance();
                        ((ProxyObject) aResult).setHandler(
                                new CollectionProxyHandler(new CollectionProxy(mField, theList, valueToObject)));
                        return aResult;
                    } else {
                        Collection<Object> aValues = BeanReflectUtil
                                .instantiateCollectionFromField(BeanReflectUtil.classFrom(mField));

                        for (Value aValue : theList) {
                            Object aListValue = valueToObject.apply(aValue);

                            if (aListValue == null) {
                                throw new RuntimeException("Error converting a list value.");
                            }

                            if (aListValue instanceof Collection) {
                                aValues.addAll(((Collection) aListValue));
                            } else {
                                aValues.add(aListValue);
                            }
                        }

                        return aValues;
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

            /**
             * if not list all literals
             *   proceed
             * else
             *  if not lang aware
             *     find non lang typed literals
             *   if >= 1 non lang typed literals
             *     proceed
             *   else get language based on locale
             *     find literals based on local lang
             *
             *   if == 0 literals
             *     use original list
             *   else use filtered list
             *
             * else if lang aware
             */

            Collection<Value> aList = new HashSet<Value>(theList);
            if (!Iterables2.find(aList, CONTAINS_RESOURCES)) {
                if (!EmpireOptions.ENABLE_LANG_AWARE) {
                    Collection<Value> aLangFiltered = Collections2.filter(aList, new Predicate<Value>() {
                        public boolean apply(final Value theValue) {
                            return ((Literal) theValue).getLanguage() == null;
                        }
                    });

                    if (aLangFiltered.isEmpty()) {
                        LANG_FILTER.setLangCode(getLanguageForLocale());
                        aLangFiltered = Collections2.filter(aList, LANG_FILTER);
                    }

                    if (!aLangFiltered.isEmpty()) {
                        aList = aLangFiltered;
                    }
                } else {
                    LANG_FILTER.setLangCode(mField.getAnnotation(RdfProperty.class).language());
                    aList = Collections2.filter(aList, LANG_FILTER);
                }
            }

            //         aList = filter(aList, new Predicate<Value>() {
            //            public boolean accept(final Value theValue) {
            //               if (theValue instanceof Resource
            //                  || !EmpireOptions.ENABLE_LANG_AWARE
            //                  || (EmpireOptions.ENABLE_LANG_AWARE
            //                     && theValue instanceof Literal
            //                     && !mField.getAnnotation(RdfProperty.class).language().equals(""))
            //                     && mField.getAnnotation(RdfProperty.class).language().equals(((Literal)theValue).getLanguage())) {
            //                  return true;
            //               }
            //               else {
            //                  return false;
            //               }
            //            }
            //         });

            if (aList.isEmpty()) {
                // yes, we checked for emptiness to begin the method, but we might have done some filtering based on the
                // language tags, so we need to check again.
                return BeanReflectUtil.instantiateCollectionFromField(BeanReflectUtil.classFrom(mField));
            } else if ((aList.size() == 1) || (!EmpireOptions.STRICT_MODE)) {
                // collection of one element, just convert the single element and send that back
                return valueToObject.apply(aList.iterator().next());
            } else {
                throw new RuntimeException("Cannot convert list of values to anything meaningful for the field. "
                        + mField + " " + aList);
            }
        }
    }

    private static String getLanguageForLocale() {
        return Locale.getDefault() == null || Locale.getDefault().toString().equals("") ? "en"
                : (Locale.getDefault().toString().indexOf("_") != -1
                        ? Locale.getDefault().toString().substring(0, Locale.getDefault().toString().indexOf("_"))
                        : Locale.getDefault().toString());
    }

    private static Class refineClass(final Object theAccessor, final Class theClass, final DataSource theSource,
            final Resource theId) {
        Class aClass = theClass;

        if (Collection.class.isAssignableFrom(aClass)) {
            // if the field we're assigning from is a collection, try and figure out the type of the thing
            // we're creating from the collection

            Type[] aTypes = null;

            if (theAccessor instanceof Field
                    && ((Field) theAccessor).getGenericType() instanceof ParameterizedType) {
                aTypes = ((ParameterizedType) ((Field) theAccessor).getGenericType()).getActualTypeArguments();
            } else if (theAccessor instanceof Method) {
                aTypes = ((Method) theAccessor).getGenericParameterTypes();
            }

            if (aTypes != null && aTypes.length >= 1) {
                // first type argument to a collection is usually the one we care most about
                if (aTypes[0] instanceof ParameterizedType
                        && ((ParameterizedType) aTypes[0]).getActualTypeArguments().length > 0) {
                    Type aType = ((ParameterizedType) aTypes[0]).getActualTypeArguments()[0];

                    if (aType instanceof Class) {
                        aClass = (Class) aType;
                    } else if (aType instanceof WildcardTypeImpl) {
                        WildcardTypeImpl aWildcard = (WildcardTypeImpl) aType;
                        // trying to suss out super v extends w/o resorting to string munging.
                        if (aWildcard.getLowerBounds().length == 0 && aWildcard.getUpperBounds().length > 0) {
                            // no lower bounds afaik indicates ? extends Foo
                            aClass = ((Class) aWildcard.getUpperBounds()[0]);
                        } else if (aWildcard.getLowerBounds().length > 0) {
                            // lower & upper bounds I believe indicates something of the form Foo super Bar
                            aClass = ((Class) aWildcard.getLowerBounds()[0]);
                        } else {
                            // shoot, we'll try the string hack that Adrian posted on the mailing list.
                            try {
                                aClass = Class.forName(aType.toString().split(" ")[2].substring(0,
                                        aTypes[0].toString().split(" ")[2].length() - 1));
                            } catch (Exception e) {
                                // everything has failed, let aClass be the default (theClass) and hope for the best
                            }
                        }
                    } else {
                        // punt? wtf else could it be?
                        try {
                            aClass = Class.forName(aType.toString());
                        } catch (ClassNotFoundException e) {
                            // oh well, we did the best we can
                        }
                    }
                } else if (aTypes[0] instanceof Class) {
                    aClass = (Class) aTypes[0];
                }
            } else {
                // could not figure out the type from the generics assertions on the Collection, they are either
                // not present, or my algorithm is not bullet proof.  So lets try checking on the annotations
                // for a type hint.

                Class aTarget = BeanReflectUtil.getTargetEntity(theAccessor);
                if (aTarget != null) {
                    aClass = aTarget;
                }
            }
        }

        if (!BeanReflectUtil.hasAnnotation(aClass, RdfsClass.class) || aClass.isInterface()) {
            // k, so either the parameter of the collection or the declared type of the field does
            // not map to an instance/bean type.  this is most likely an error, but lets try and find
            // the rdf:type of the field, and see if we can map that to a class in the path and we'll
            // create an instance of that.  that will work, and pushes the likely failure back off to
            // the assignment of the created instance

            Iterable<Resource> aTypes = DataSourceUtil.getTypes(theSource, theId);

            // k, so now we know the type, if we can match the type to a class then we're in business
            for (Resource aType : aTypes) {
                if (aType instanceof URI) {
                    for (Class aTypeClass : TYPE_TO_CLASS.get((URI) aType)) {
                        if ((BeanReflectUtil.hasAnnotation(aTypeClass, RdfsClass.class))
                                && (aClass.isAssignableFrom(aTypeClass))) {
                            // lets try this one
                            aClass = aTypeClass;
                            break;
                        }
                    }
                }
            }
        }

        if (aClass.isInterface()) {
            if (BeanReflectUtil.hasAnnotation(aClass, RdfsClass.class)) {
                URI aType = FACTORY.createURI(((RdfsClass) aClass.getAnnotation(RdfsClass.class)).value());
                for (Class aTypeClass : TYPE_TO_CLASS.get(aType)) {
                    if ((BeanReflectUtil.hasAnnotation(aTypeClass, RdfsClass.class))
                            && (aClass.isAssignableFrom(aTypeClass))) {
                        // lets try this one
                        aClass = aTypeClass;
                        return aClass;
                    }
                }
            }
        }

        return aClass;
    }

    public static class ValueToObject implements Function<Value, Object> {
        static final List<URI> integerTypes = Arrays.asList(XMLSchema.INT, XMLSchema.INTEGER,
                XMLSchema.POSITIVE_INTEGER, XMLSchema.NEGATIVE_INTEGER, XMLSchema.NON_NEGATIVE_INTEGER,
                XMLSchema.NON_POSITIVE_INTEGER, XMLSchema.UNSIGNED_INT);
        static final List<URI> longTypes = Arrays.asList(XMLSchema.LONG, XMLSchema.UNSIGNED_LONG);
        static final List<URI> floatTypes = Arrays.asList(XMLSchema.FLOAT, XMLSchema.DECIMAL);
        static final List<URI> shortTypes = Arrays.asList(XMLSchema.SHORT, XMLSchema.UNSIGNED_SHORT);
        static final List<URI> byteTypes = Arrays.asList(XMLSchema.BYTE, XMLSchema.UNSIGNED_BYTE);

        private URI mProperty;
        private Object mAccessor;
        private DataSource mSource;
        private Resource mResource;

        public ValueToObject(final DataSource theSource, Resource theResource, final Object theAccessor,
                final URI theProp) {
            mResource = theResource;
            mSource = theSource;
            mAccessor = theAccessor;
            mProperty = theProp;
        }

        public Object apply(final Value theValue) {
            long start = System.currentTimeMillis();
            if (mAccessor == null) {
                throw new RuntimeException("Null accessor is not permitted");
            }

            if (theValue instanceof Literal) {
                Literal aLit = (Literal) theValue;
                URI aDatatype = aLit.getDatatype() != null ? aLit.getDatatype() : null;
                if (aDatatype == null || XMLSchema.STRING.equals(aDatatype) || RDFS.LITERAL.equals(aDatatype)) {
                    return aLit.getLabel();
                } else if (XMLSchema.BOOLEAN.equals(aDatatype)) {
                    return Boolean.valueOf(aLit.getLabel());
                } else if (integerTypes.contains(aDatatype)) {
                    return Integer.parseInt(aLit.getLabel());
                } else if (longTypes.contains(aDatatype)) {
                    return Long.parseLong(aLit.getLabel());
                } else if (XMLSchema.DOUBLE.equals(aDatatype)) {
                    return Double.valueOf(aLit.getLabel());
                } else if (floatTypes.contains(aDatatype)) {
                    return Float.valueOf(aLit.getLabel());
                } else if (shortTypes.contains(aDatatype)) {
                    return Short.valueOf(aLit.getLabel());
                } else if (byteTypes.contains(aDatatype)) {
                    return Byte.valueOf(aLit.getLabel());
                } else if (XMLSchema.ANYURI.equals(aDatatype)) {
                    try {
                        return new java.net.URI(aLit.getLabel());
                    } catch (URISyntaxException e) {
                        LOGGER.warn("URI syntax exception converting literal value which is not a valid URI {} ",
                                aLit.getLabel());
                        return null;
                    }
                } else if (XMLSchema.DATE.equals(aDatatype) || XMLSchema.DATETIME.equals(aDatatype)) {
                    return Dates.asDate(aLit.getLabel());
                } else if (XMLSchema.TIME.equals(aDatatype)) {
                    return new Date(Long.parseLong(aLit.getLabel()));
                } else {
                    // no idea what this value is from its data type.  if the field takes a string
                    // we'll just assign the plain string, otherwise its an error
                    if (BeanReflectUtil.classFrom(mAccessor).isAssignableFrom(String.class)) {
                        return aLit.getLabel();
                    } else {
                        throw new RuntimeException("Unsupported or unknown literal datatype");
                    }
                }
            } else if (theValue instanceof BNode) {
                // TODO: this is not bulletproof, clean this up

                BNode aBNode = (BNode) theValue;

                // we need to figure out what type of bean this instance maps to.
                Class<?> aClass = BeanReflectUtil.classFrom(mAccessor);

                aClass = refineClass(mAccessor, aClass, mSource, aBNode);

                if (Collection.class.isAssignableFrom(BeanReflectUtil.classFrom(mAccessor))) {
                    AccessibleObject aAccess = (AccessibleObject) mAccessor;
                    RdfProperty aPropAnnotation = aAccess.getAnnotation(RdfProperty.class);

                    // the field takes a collection, lets create a new instance of said collection, and hopefully the
                    // bnode is a list.  this approach will only work if the property is a singleton value, eg
                    // :inst someProperty _:a where _:a is the head of a list.  if you have another value _:b for
                    // some property on :inst, we don't have any way of figuring out which one you're talking about
                    // since bnode id references are not guaranteed to be stable in SPARQL, ie just because its id "a"
                    // in the result set, does not mean i can do another query for _:a and get the expected results.
                    // and you can't do a describe for the same reason.

                    try {
                        String aQuery = getBNodeConstructQuery(mSource, mResource, mProperty);

                        Graph aGraph = mSource.graphQuery(aQuery);

                        Optional<Resource> aPossibleListHead = Graphs.getResource(aGraph, mResource, mProperty);

                        if (aPossibleListHead.isPresent() && Graphs.isList(aGraph, aPossibleListHead.get())) {
                            List<Value> aList;

                            // getting the list is only safe the the query dialect supports stable bnode ids in the query language.
                            if (aPropAnnotation != null && aPropAnnotation.isList()
                                    && mSource.getQueryFactory().getDialect().supportsStableBnodeIds()) {
                                try {
                                    aList = asList(mSource, aPossibleListHead.get());
                                } catch (DataSourceException e) {
                                    throw new RuntimeException(e);
                                }
                            } else {
                                aList = new ArrayList<Value>(GraphUtil.getObjects(aGraph, mResource, mProperty));
                            }

                            //return new ToObjectFunction(mSource, null, (AccessibleObject) mAccessor, null).apply(aList);
                            Collection<Object> aValues = BeanReflectUtil
                                    .instantiateCollectionFromField(BeanReflectUtil.classFrom(aAccess));

                            for (Value aValue : aList) {
                                Object aListValue = null;

                                try {
                                    aListValue = getProxyOrDbObject(mAccessor, aClass, aValue, mSource);
                                } catch (Exception e) {
                                    // we'll throw an error in a second...
                                }

                                if (aListValue == null) {
                                    throw new RuntimeException(
                                            "Error converting a list value: " + aValue + " -> " + aClass);
                                }

                                aValues.add(aListValue);

                            }
                            return aValues;
                        }
                    } catch (QueryException e) {
                        throw new RuntimeException(e);
                    }
                }

                try {
                    Object proxyOrDbObject = getProxyOrDbObject(mAccessor, aClass, aBNode, mSource);
                    return proxyOrDbObject;
                } catch (Exception e) {
                    if (EmpireOptions.STRICT_MODE) {
                        throw new RuntimeException(e);
                    } else {
                        return null;
                    }
                }
            } else if (theValue instanceof URI) {
                URI aURI = (URI) theValue;
                try {
                    // we need to figure out what type of bean this instance maps to.
                    Class<?> aClass = BeanReflectUtil.classFrom(mAccessor);

                    aClass = refineClass(mAccessor, aClass, mSource, aURI);

                    if (aClass.isAssignableFrom(java.net.URI.class)) {
                        return java.net.URI.create(aURI.toString());
                    } else {
                        Object proxyOrDbObject = getProxyOrDbObject(mAccessor, aClass,
                                java.net.URI.create(aURI.toString()), mSource);
                        return proxyOrDbObject;
                    }
                } catch (Exception e) {
                    if (EmpireOptions.STRICT_MODE) {
                        throw new RuntimeException(e);
                    } else {
                        LOGGER.warn("Problem applying value {}, {} ", e.toString(), e.getCause());
                        return null;
                    }
                }
            } else {
                if (EmpireOptions.STRICT_MODE) {
                    throw new RuntimeException("Unexpected Value type");
                } else {
                    LOGGER.warn("Problem applying value : Unexpected Value type");
                    return null;
                }
            }
        }
    }

    private static List<Value> asList(DataSource theSource, Resource theRes) throws DataSourceException {
        List<Value> aList = Lists.newArrayList();

        Resource aListRes = theRes;

        while (aListRes != null) {

            Resource aFirst = (Resource) DataSourceUtil.getValue(theSource, aListRes, RDF.FIRST);
            Resource aRest = (Resource) DataSourceUtil.getValue(theSource, aListRes, RDF.REST);

            if (aFirst != null) {
                aList.add(aFirst);
            }

            if (aRest == null || aRest.equals(RDF.NIL)) {
                aListRes = null;
            } else {
                aListRes = aRest;
            }
        }

        return aList;
    }

    private static final MethodFilter METHOD_FILTER = new MethodFilter() {
        public boolean isHandled(final Method theMethod) {
            return !theMethod.getName().equals("finalize");
        }
    };

    @SuppressWarnings("unchecked")
    private static <T> T getProxyOrDbObject(Object theAccessor, Class<T> theClass, Object theKey,
            DataSource theSource) throws Exception {
        long start = System.currentTimeMillis();
        if (BeanReflectUtil.isFetchTypeLazy(theAccessor)) {
            // ========= start PATCH
            try {
                // try to determine proper class from rdf:type
                T rdfObj = Empire.get().instance(theClass);
                asSupportsRdfId(rdfObj).setRdfId(asPrimaryKey(theKey));
                theClass = determineClass(theClass, rdfObj, theSource, false);
            } catch (Exception ignore) {
            }
            // ========= end PATCH

            Proxy<T> aProxy = new Proxy<T>(theClass, asPrimaryKey(theKey), theSource);

            ProxyFactory aFactory = new ProxyFactory();
            aFactory.setInterfaces(ObjectArrays.concat(theClass.getInterfaces(), EmpireGenerated.class));
            if (!theClass.isInterface()) {
                aFactory.setSuperclass(theClass);
            }

            aFactory.setFilter(METHOD_FILTER);
            final ProxyHandler<T> aHandler = new ProxyHandler<T>(aProxy);

            Object aObj = aFactory.createClass(METHOD_FILTER).newInstance();

            ((ProxyObject) aObj).setHandler(aHandler);
            return (T) aObj;
        } else {
            return fromRdf(theClass, asPrimaryKey(theKey), theSource);
        }
    }

    /**
     * Javassist {@link MethodHandler} implementation for method proxying.
     * @param <T> the proxy class type
     */
    public static class ProxyHandler<T> implements MethodHandler {
        /**
         * The proxy object which wraps the instance being proxied.
         */
        private Proxy<T> mProxy;

        /**
         * Create a new ProxyHandler
         * @param theProxy the proxy object
         */
        private ProxyHandler(final Proxy<T> theProxy) {
            mProxy = theProxy;
        }

        public Proxy<T> getProxy() {
            return mProxy;
        }

        /**
         * Delegates the methods to the Proxy
         * @inheritDoc
         */
        public Object invoke(final Object theThis, final Method theMethod, final Method theProxyMethod,
                final Object[] theArgs) throws Throwable {

            return theMethod.invoke(mProxy.value(), theArgs);
        }
    }

    private static String getBNodeConstructQuery(DataSource theSource, Resource theRes, URI theProperty) {
        Dialect aDialect = theSource.getQueryFactory().getDialect();

        String aSerqlQuery = "construct * from {" + aDialect.asQueryString(theRes) + "} <" + theProperty.toString()
                + "> {o}, {o} po {oo}";

        String aSparqlQuery = "CONSTRUCT  { " + aDialect.asQueryString(theRes) + " <" + theProperty.toString()
                + "> ?o . ?o ?po ?oo  } \n" + "WHERE\n" + "{ " + aDialect.asQueryString(theRes) + " <"
                + theProperty.toString() + "> ?o.\n" + "?o ?po ?oo. }";

        if (theSource.getQueryFactory().getDialect() instanceof SerqlDialect) {
            return aSerqlQuery;
        } else {
            // TODO: we're just assuming/hoping at this point that they support sparql.  which
            // will most likely be the case, but possibly not always.
            return aSparqlQuery;
        }
    }

    public static class AsValueFunction implements Function<Object, Value> {
        private AccessibleObject mField;
        private RdfProperty annotation;

        public AsValueFunction() {
        }

        public AsValueFunction(final AccessibleObject theField) {
            mField = theField;
            annotation = mField == null ? null : mField.getAnnotation(RdfProperty.class);
        }

        public Value apply(final Object theIn) {
            if (theIn == null) {
                return null;
            } else if (!EmpireOptions.STRONG_TYPING && BeanReflectUtil.isPrimitive(theIn)) {
                return FACTORY.createLiteral(theIn.toString());
            } else if (Boolean.class.isInstance(theIn)) {
                return FACTORY.createLiteral(Boolean.class.cast(theIn).booleanValue());
            } else if (Integer.class.isInstance(theIn)) {
                return FACTORY.createLiteral(Integer.class.cast(theIn).intValue());
            } else if (Long.class.isInstance(theIn)) {
                return FACTORY.createLiteral(Long.class.cast(theIn).longValue());
            } else if (Short.class.isInstance(theIn)) {
                return FACTORY.createLiteral(Short.class.cast(theIn).shortValue());
            } else if (Double.class.isInstance(theIn)) {
                return FACTORY.createLiteral(Double.class.cast(theIn).doubleValue());
            } else if (Float.class.isInstance(theIn)) {
                return FACTORY.createLiteral(Float.class.cast(theIn).floatValue());
            } else if (Date.class.isInstance(theIn)) {
                return FACTORY.createLiteral(Dates.datetime(Date.class.cast(theIn)), XMLSchema.DATETIME);
            } else if (String.class.isInstance(theIn)) {
                if (annotation != null && !annotation.language().equals("")) {
                    return FACTORY.createLiteral(String.class.cast(theIn), annotation.language());
                } else {
                    return FACTORY.createLiteral(String.class.cast(theIn), XMLSchema.STRING);
                }
            } else if (Character.class.isInstance(theIn)) {
                return FACTORY.createLiteral(Character.class.cast(theIn));
            } else if (java.net.URI.class.isInstance(theIn)) {
                if (annotation != null && annotation.isXsdUri()) {
                    return FACTORY.createLiteral(theIn.toString(), XMLSchema.ANYURI);
                } else {
                    return FACTORY.createURI(theIn.toString());
                }
            } else if (Value.class.isAssignableFrom(theIn.getClass())) {
                return Value.class.cast(theIn);
            } else if (BeanReflectUtil.hasAnnotation(theIn.getClass(), RdfsClass.class)) {
                try {
                    return id(theIn);
                } catch (InvalidRdfException e) {
                    throw new RuntimeException(e);
                }
            } else if (theIn instanceof ProxyHandler) {
                return this.apply(((ProxyHandler) theIn).mProxy.value());
            } else {
                try {
                    Field aProxy = theIn.getClass().getDeclaredField("handler");
                    return this.apply(((ProxyHandler) BeanReflectUtil.safeGet(aProxy, theIn)).mProxy.value());
                } catch (Exception e) {
                    throw new RuntimeException(
                            "Unknown type conversion: " + theIn.getClass() + " " + theIn + " " + mField);
                }
            }
        }
    }

    private static class ContainsResourceValues implements Predicate<Value> {
        public boolean apply(final Value theValue) {
            return theValue instanceof Resource;
        }
    }

    private static class LanguageFilter implements Predicate<Value> {
        private String mLangCode;

        private LanguageFilter(final String theLangCode) {
            mLangCode = theLangCode;
        }

        public void setLangCode(final String theLangCode) {
            mLangCode = theLangCode;
        }

        public boolean apply(final Value theValue) {
            return theValue instanceof Literal && mLangCode.equals(((Literal) theValue).getLanguage());
        }
    }
}