Code Search for Developers
 
 
  

RDFEntityPersister.java from Texai at Krugle


Show RDFEntityPersister.java syntax highlighted

/*
 * RDFEntityPersister.java
 *
 * Created on October 31, 2006, 11:23 AM
 *
 * Description: This class persists domain entities into the RDF store,
 * mapping entity associations onto RDF triples.
 *
 * Copyright (C) 2006 Stephen L. Reed.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms
 * of the GNU General Public License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program;
 * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package org.texai.kb.persistence;

import com.sun.xml.bind.DatatypeConverterImpl;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.UUID;
import javax.xml.bind.DatatypeConverterInterface;
import javax.xml.namespace.QName;
import net.jcip.annotations.NotThreadSafe;
import net.sf.cglib.proxy.Factory;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.apache.log4j.Logger;
import org.openrdf.OpenRDFException;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.datatypes.XMLDatatypeUtil;
import org.openrdf.model.vocabulary.OWL;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.model.vocabulary.RDFS;
import org.openrdf.model.vocabulary.XMLSchema;
import org.openrdf.query.BindingSet;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.texai.kb.Constants;
import org.texai.kb.persistence.lazy.LazyList;
import org.texai.kb.persistence.lazy.LazySet;
import org.texai.util.TexaiException;

/** This class persists new or updated entities to the Sesame RDF store.  New RDF classes and predicates are automatically
 * defined in the RDF store. The persist() method cascades to new RDF entities that are field values of the entity being persisted.
 *
 * @author reed
 */
@NotThreadSafe
public class RDFEntityPersister                     // NOPMD
        extends AbstractRDFEntityAccessor {
  
  // session variables
  
  /** the logger */
  private final Logger logger = Logger.getLogger(RDFEntityPersister.class);                         // NOPMD
  
  /** the indicator whether the debug logging level is enabled */
  private final boolean isDebugEnabled;
  
  /** the Sesame repository connection */
  private final RepositoryConnection repositoryConnection;
  
  /** the Sesame value factory */
  private final ValueFactory valueFactory;
  
  /** the RDF utility bean */
  private final RDFUtility rdfUtility;
  
  /** the query that gathers objects for a given subject and predicate. */
  private final TupleQuery objectsTupleQuery;
  
  /** the query that gathers existing types for a given subject. */
  private final TupleQuery existingTypesTupleQuery;
  
  /** the query that gathers existing subClassOfs for a given entity class. */
  private final TupleQuery existingSubClassOfsTupleQuery;
  
  /** the query that gathers objects for a given predicate and object. */
  private final TupleQuery subjectsTupleQuery;
  
  /** the set of defined class and predicate URIs */
  private final Set<URI> definedClassAndPredicateURIs;
  
  /** the XML datatype converter */
  private final DatatypeConverterInterface datatypeConverter;
  
  /** the URI http://sw.cyc.com/2006/07/27/cyc/Collection */
  private final URI uriCollection;
  
  /** the URI http://sw.cyc.com/2006/07/27/cyc/conceptuallyRelated */
  private final URI uriConceptuallyRelated;
  
  /** the URI http://texai.org/texai/domainEntityClassName */
  private final URI uriDomainEntityClassName;
  
  /** the URI http://sw.cyc.com/2006/07/27/cyc/FirstOrderCollection */
  private final URI uriFirstOrderCollection;
  
  /** the URI http://sw.cyc.com/2006/07/27/cyc/UniversalVocabularyMt */
  private final URI uriUniversalVocabularyMt;
  
  // variables used to persist a particular entity
  
  /** the indicator that the entity instance is new */
  private boolean isNewDomainInstance;
  
  /** the stack of RDF entity information that allows the session bean to perform recursive method calls */
  private Stack<RDFEntityInfo> rdfEntityInfoStack;
  
  /** the indicator to validate persisted statements */
  private boolean areStatementsValidated = false;
  
  /** the context for recursive calls to the persist method. */
  private URI recursiveContextURI;
  
  /** Creates a new instance of RDFEntityPersister.
   *
   * @param repositoryConnection the Sesame repository connection
   * @param valueFactory the Sesame value factory
   */
  public RDFEntityPersister(
          final RepositoryConnection repositoryConnection,
          final ValueFactory valueFactory) {
    //preconditions
    assert repositoryConnection != null : "repositoryConnection must not be null";
    assert valueFactory != null : "valueFactory must not be null";
    
    this.repositoryConnection = repositoryConnection;
    this.valueFactory = valueFactory;
    rdfUtility = new RDFUtility(repositoryConnection, valueFactory);
    try {
      objectsTupleQuery = repositoryConnection.prepareTupleQuery(
              QueryLanguage.SERQL,
              "SELECT o, c FROM CONTEXT c {s} p {o}");
      existingTypesTupleQuery = repositoryConnection.prepareTupleQuery(
              QueryLanguage.SERQL,
              "SELECT typeTerm FROM {s} rdf:type {typeTerm}");
      existingSubClassOfsTupleQuery = repositoryConnection.prepareTupleQuery(
              QueryLanguage.SERQL,
              "SELECT superClassTerm FROM {classTerm} rdfs:subClassOf {superClassTerm}");
      subjectsTupleQuery = repositoryConnection.prepareTupleQuery(
              QueryLanguage.SERQL,
              "SELECT s, c FROM CONTEXT c {s} p {o}");
    } catch (final MalformedQueryException ex) {
      throw new TexaiException(ex);
    } catch (final RepositoryException ex) {
      throw new TexaiException(ex);
    }
    definedClassAndPredicateURIs = new HashSet<URI>();
    rdfEntityInfoStack = new Stack<RDFEntityInfo>();
    datatypeConverter = DatatypeConverterImpl.theInstance;
    uriCollection = valueFactory.createURI(Constants.TERM_COLLECTION);
    uriConceptuallyRelated = valueFactory.createURI(Constants.TERM_CONCEPTUALLY_RELATED);
    uriDomainEntityClassName = valueFactory.createURI(Constants.TERM_DOMAIN_ENTITY_CLASS_NAME);
    uriFirstOrderCollection = valueFactory.createURI(Constants.TERM_FIRST_ORDER_COLLECTION);
    uriUniversalVocabularyMt = valueFactory.createURI(Constants.TERM_UNIVERSAL_VOCABULARY_MT);
    isDebugEnabled = logger.isDebugEnabled();
  }
  
  /** Persists the given RDF entity as propositions in the RDF store.
   *
   * @param rdfEntity the RDF entity
   * @return the instance URI that represents the RDF entity
   */
  public URI persist(final Object rdfEntity) {
    //Preconditions
    assert rdfEntity != null : "rdfEntity must not be null";
    assert isRDFEntity(rdfEntity) : rdfEntity + "(" +  rdfEntity.getClass().getName()
    + ") must be have a @RDFEntity class level annotation in " + Arrays.toString(rdfEntity.getClass().getAnnotations());
    
    final boolean wasStackEmpty = rdfEntityInfoStack.empty();
    initializeAbstractSessionState();
    initializeSessionState();
    if (isDebugEnabled) {
      logger.debug(stackLevel() + "persisting " + rdfEntity);
    }
    setRDFEntity(rdfEntity);
    setRDFEntityClass(rdfEntity.getClass());
    gatherAnnotationsForRDFEntityClass();
    configureRDFEntitySettings();
    if (!definedClassAndPredicateURIs.contains(getClassURI())) {
      persistTypes();
      persistSubClassOfs();
      definedClassAndPredicateURIs.add(getClassURI());
    }
    findOrCreateDomainInstanceURI();
    persistFields();
    
    //Postconditions
    assert getInstanceURI() != null : "instanceURI must not be null";
    assert wasStackEmpty == rdfEntityInfoStack.empty() : "beginning stack empty status " + wasStackEmpty
            + " must equal ending stack status " + rdfEntityInfoStack.empty();
    
    return getInstanceURI();
  }
  
  /** Gets the indicator to validate persisted statements.
   *
   * @return the indicator whether to validate persisted statements
   */
  public boolean getAreStatementsValidated() {
    return areStatementsValidated;
  }
  
  /** Sets the indicator to validate persisted statements.
   *
   * @param areStatementsValidated the indicator whether to validate persisted statements
   */
  public void setAreStatementsValidated(boolean areStatementsValidated) {
    this.areStatementsValidated = areStatementsValidated;
  }
  
  /** Gets the logger.
   *
   * @return the logger
   */
  protected Logger getLogger() {
    return logger;
  }
  
  /** Gets the value factory.
   *
   * @return the value factory
   */
  protected ValueFactory getValueFactory() {
    return valueFactory;
  }
  
  /** Gathers the existing type classes for which the entity class is a direct instance.
   *
   * @return the existing type classes names for which the entity class is a direct instance
   */
  private Set<URI> gatherExistingTypes() {
    //Preconditions
    assert getClassURI() != null : "classURI must not be null";
    
    final Set<URI> existingTypeURIs = new HashSet<URI>();
    try {
      existingTypesTupleQuery.setBinding("s", getClassURI());
      final TupleQueryResult tupleQueryResult = existingTypesTupleQuery.evaluate();
      while (tupleQueryResult.hasNext()) {
        existingTypeURIs.add((URI) tupleQueryResult.next().getBinding("typeTerm").getValue());     // NOPMD
      }
        tupleQueryResult.close();
    } catch (final OpenRDFException ex) {
      throw new TexaiException(ex);
    }
    return existingTypeURIs;
  }
  
  /** Gathers the existing classes for which the entity class is a direct subclass.
   *
   * @return the existing classes for which the entity class is a direct subclass
   */
  private Set<URI> gatherExistingSubClassOfs() {
    //Preconditions
    assert getClassURI() != null : "classURI must not be null";
    
    final Set<URI> existingSubClassOfURIs = new HashSet<URI>();
    try {
      existingSubClassOfsTupleQuery.setBinding("classTerm", getClassURI());
      final TupleQueryResult tupleQueryResult = existingSubClassOfsTupleQuery.evaluate();
      while (tupleQueryResult.hasNext()) {
        existingSubClassOfURIs.add((URI) tupleQueryResult.next().getBinding("superClassTerm").getValue());
      }
        tupleQueryResult.close();
    } catch (final OpenRDFException ex) {
      throw new TexaiException(ex);
    }
    return existingSubClassOfURIs;
  }
  
  /**
   * Persists the RDF entity class typeURI statements.
   */
  private void persistTypes() {
    //Preconditions
    assert getClassURI() != null : "classURI must not be null";
    
    final Set<URI> existingTypeURIs = gatherExistingTypes();
    if (existingTypeURIs.isEmpty() && getTypeURIs().length == 0) {
      // default to FirstOrderCollection for a new domain class
      final URI[] typeURIs = {uriFirstOrderCollection};
      setTypeURIs(typeURIs);
    }
    try {
      for (final URI typeURI : getTypeURIs()) {
        if (!existingTypeURIs.contains(typeURI)) {
          if (isDebugEnabled) {
            logger.debug(stackLevel() + "adding type: " + typeURI);
          }
          final Statement statement = valueFactory.createStatement(
                  getClassURI(),
                  RDF.TYPE,
                  typeURI,
                  uriUniversalVocabularyMt);
          repositoryConnection.add(statement);
          logger.info("persisted: " + rdfUtility.formatStatement(statement));    // NOPMD
        }
      }
      if (getTypeURIs().length > 0) {
        // if the RDFEntity annotation specified typeURI, then delete any existing typeURI relationships not contained in the specification
        final List<URI> typeURIsList = Arrays.asList(getTypeURIs());
        for (final URI existingTypeURI : existingTypeURIs) {
          if (!typeURIsList.contains(existingTypeURI)) {
            if (isDebugEnabled) {
              logger.debug(stackLevel() + "deleting existingType: " + existingTypeURI);
            }
            final Statement statement = valueFactory.createStatement(
                    getClassURI(),
                    RDF.TYPE,
                    existingTypeURI,
                    uriUniversalVocabularyMt);
            repositoryConnection.remove(statement);
            logger.info("removed: " + rdfUtility.formatStatement(statement));    // NOPMD
          }
        }
      }
    } catch (final RepositoryException ex) {
      throw new TexaiException(ex);
    }
  }
  
  /** Persists the RDF entity class genls propositions. */
  private void persistSubClassOfs() {
    //Preconditions
    assert getClassURI() != null : "classURI must not be null";
    
    final Set<URI> existingSubClassOfURIs = gatherExistingSubClassOfs();
    if (existingSubClassOfURIs.isEmpty() && getSubClassOfURIs().length == 0) {
      // default to Collection for a new domain class
      final URI[] subClassOfURIs = {uriCollection};
      setSubClassOfURIs(subClassOfURIs);
    }
    try {
      for (final URI subClassOfURI : getSubClassOfURIs()) {
        if (!existingSubClassOfURIs.contains(subClassOfURI)) {
          if (isDebugEnabled) {
            logger.debug(stackLevel() + "adding subClassOf: " + subClassOfURI);
          }
          final Statement statement = valueFactory.createStatement(
                  getClassURI(),
                  RDFS.SUBCLASSOF,
                  subClassOfURI,
                  uriUniversalVocabularyMt);
          repositoryConnection.add(statement);
          logger.info("persisted: " + rdfUtility.formatStatement(statement));    // NOPMD
        }
      }
      if (getSubClassOfURIs().length > 0) {
        // if the RDFEntity annotation specified subClassOf, then delete any existing subClassOf relationships not contained in the specification
        final List<URI> subClassOfURIsList = Arrays.asList(getSubClassOfURIs());
        for (final URI existingSubClassOfURI : existingSubClassOfURIs) {
          if (!subClassOfURIsList.contains(existingSubClassOfURI)) {
            if (isDebugEnabled) {
              logger.debug(stackLevel() + "deleting : existingSubClassOf" + existingSubClassOfURI);
            }
            final Statement statement = valueFactory.createStatement(
                    getClassURI(),
                    RDFS.SUBCLASSOF,
                    existingSubClassOfURI,
                    uriUniversalVocabularyMt);
            repositoryConnection.remove(statement);
            logger.info("removed: " + rdfUtility.formatStatement(statement));    // NOPMD
          }
        }
      }
    } catch (final RepositoryException ex) {
      throw new TexaiException(ex);
    }
  }
  
  /** Finds or creates the domain instance URI. */
  private void findOrCreateDomainInstanceURI() {
    //Preconditions
    assert getFieldAnnotationDictionary() != null : "fieldAnnotationDictionary must not be null";
    assert !getFieldAnnotationDictionary().isEmpty() : "fieldAnnotationDictionary must not be empty";
    assert getClassURI() != null : "classURI must not be null";
    assert getRDFEntity() != null : "rdfEntity() must not be null";
    
    final Field idField = getIdField();
    if (idField == null) {
      throw new TexaiException("Id field not found for RDF entity " + getRDFEntity());
    }
    if (!idField.isAccessible()) {
      idField.setAccessible(true);
    }
    Object value;
    try {
      value = idField.get(getRDFEntity());
    } catch (final IllegalArgumentException ex) {
      throw new TexaiException(ex);
    } catch (final IllegalAccessException ex) {
      throw new TexaiException(ex);
    }
    if (value == null) {
      setInstanceURI(makeURI(getClassURI() + "_" + UUID.randomUUID().toString()));
      try {
        if (idField.getType().equals(String.class)) {
          idField.set(getRDFEntity(), getInstanceURI().toString());
        } else if (URI.class.isAssignableFrom(idField.getType())) {
          idField.set(getRDFEntity(), getInstanceURI());
        } else if (idField.getType().equals(java.net.URI.class)) {
          try {
            idField.set(getRDFEntity(), new java.net.URI(getInstanceURI().toString()));
          } catch (final IllegalArgumentException ex) {
            throw new TexaiException(ex);
          } catch (final URISyntaxException ex) {
            throw new TexaiException(ex);
          } catch (final IllegalAccessException ex) {
            throw new TexaiException(ex);
          }
        } else {
          throw new TexaiException("Id field is not a supported type");
        }
      } catch (final IllegalArgumentException ex) {
        throw new TexaiException(ex);
      } catch (final IllegalAccessException ex) {
        throw new TexaiException(ex);
      }
      if (isDebugEnabled) {
        logger.debug(stackLevel() + "  created new instance " + getInstanceURI());
      }
      try {
        final Statement typeStatement = valueFactory.createStatement(
                getInstanceURI(),
                RDF.TYPE,
                getClassURI(),
                uriUniversalVocabularyMt);
        repositoryConnection.add(typeStatement);
        logger.info("persisted: " + rdfUtility.formatStatement(typeStatement));
        
        assert getRDFEntityClass() != null : "rdfEntityClass must not be null";
        final Statement classNameStatement = valueFactory.createStatement(
                getInstanceURI(),
                uriDomainEntityClassName,
                valueFactory.createLiteral(getRDFEntityClass().getName()),
                uriUniversalVocabularyMt);
        repositoryConnection.add(classNameStatement);
        logger.info("persisted: " + rdfUtility.formatStatement(classNameStatement));
      } catch (final RepositoryException ex) {
        throw new TexaiException(ex);
      }
    } else {
      if (URI.class.isAssignableFrom(value.getClass())) {
        setInstanceURI((URI) value);
      } else {
        setInstanceURI(makeURI(value.toString()));
      }
      isNewDomainInstance = false;
      if (isDebugEnabled) {
        logger.debug(stackLevel() + "  Id specifies existing instance " + getInstanceURI());
      }
    }
    final Cache cache = CacheManager.getInstance().getCache(Constants.CACHE_CONNECTED_RDF_ENTITIES);
    assert cache != null : "cache not found for: " + Constants.CACHE_CONNECTED_RDF_ENTITIES;
    final Element element = new Element(getRDFEntity(), getInstanceURI());
    cache.put(element);
    
    //Postconditions
    assert getInstanceURI() != null : "instanceURI must not be null";
  }
  
  /** Iterates over the fields in the RDF entity instance and persists them
   * to the RDF store as propositions.
   */
  private void persistFields() {    
    for (final Field field : getFieldAnnotationDictionary().keySet()) {
      final Annotation annotation = getFieldAnnotationDictionary().get(field);
      if (isDebugEnabled) {
        logger.debug(stackLevel() + "field: " + field + ", annotation: " + annotation);
      }
      if ("@javax.persistence.Id()".equals(annotation.toString())) {
        if (isDebugEnabled) {
          logger.debug(stackLevel() + "  skipping Id field");
        }
        continue;
      } else {
        if (annotation instanceof RDFProperty) {
          persistField(field, (RDFProperty) annotation);
        }
      }
    }
  }
  
  
  //TODO persist list and array fields as rdf Collections, drop @OrderBy
  //  <instance> <predicate> <bnode_1>
  //  <bnode_1> rdf:first <element1>
  //  <bnode_1> rdf:rest <bnode_2>
  //  <bnode_2> rdf:first <element2>
  //  <bnode_2> rdf:rest <bnode_3>
  // ...
  //  <bnode_N> rdf:first <elementN>
  //  <bnode_N> rdf:rest rdfs:nil
  
  
  /** Persists the given field according to the given rdf property.
   *
   * @param field the given RDF entity instance field
   * @param rdfProperty the property annotation associated with the field
   */
  @SuppressWarnings({"unchecked"})
  private void persistField(final Field field, final RDFProperty rdfProperty) {     // NOPMD
    //Preconditions
    assert field != null : "field must not be null";                                // NOPMD
    assert rdfProperty != null : "rdfProperty must not be null";
    
    if (isDebugEnabled) {
      logger.debug(stackLevel() + "  processing rdf property: " + rdfProperty);
    }
    // obtain the value object
    if (!field.isAccessible()) {
      field.setAccessible(true);
    }
    Object value;
    try {
      value = field.get(getRDFEntity());
    } catch (final IllegalArgumentException ex) {
      throw new TexaiException(ex);
    } catch (final IllegalAccessException ex) {
      throw new TexaiException(ex);
    }
    final Class fieldType = field.getType();
    if (isDebugEnabled) {
      logger.debug(stackLevel() + "  field type: " + fieldType.getName());
    }
    if (value != null) {
      if (value instanceof LazySet || value instanceof LazyList) {
        if (isDebugEnabled) {
          logger.debug(stackLevel() + "    skipping lazyily loaded field which has not yet been loaded");
        }
        return;                                                      // NOPMD
      }
      if (Factory.class.isAssignableFrom(value.getClass())) {
        if (isDebugEnabled) {
          logger.debug(stackLevel() + "    skipping lazyily loaded proxy field which has not yet been loaded");
        }
        return;                                                      // NOPMD
      }
    }
    final boolean isBooleanField = "boolean".equals(fieldType.getName());
    // determine the predicate
    final URI predicate = findPredicate(field, rdfProperty, isBooleanField);
    // obtain the existings
    List<Value> existingRDFValues;
    if (isNewDomainInstance) {
      existingRDFValues = Collections.EMPTY_LIST;
    } else if (rdfProperty.inverse()) {
      try {
        subjectsTupleQuery.setBinding("p", predicate);
        subjectsTupleQuery.setBinding("o", getInstanceURI());
        subjectsTupleQuery.setBinding("c", getContextURI());
        final TupleQueryResult tupleQueryResult = subjectsTupleQuery.evaluate();
        existingRDFValues = new ArrayList<Value>();
        while (tupleQueryResult.hasNext()) {
          existingRDFValues.add(tupleQueryResult.next().getBinding("s").getValue());
        }
        tupleQueryResult.close();
      } catch (final OpenRDFException ex) {
        throw new TexaiException(ex);
      }
    } else {
      try {
        objectsTupleQuery.setBinding("s", getInstanceURI());
        objectsTupleQuery.setBinding("p", predicate);
        objectsTupleQuery.setBinding("c", getContextURI());
        final TupleQueryResult tupleQueryResult = objectsTupleQuery.evaluate();
        existingRDFValues = new ArrayList<Value>();
        while (tupleQueryResult.hasNext()) {
          existingRDFValues.add(tupleQueryResult.next().getBinding("o").getValue());
        }
        tupleQueryResult.close();
      } catch (final OpenRDFException ex) {
        throw new TexaiException(ex);
      }
    }
    Set<Value> assertedValueTerms = null;
    if (isDebugEnabled) {
      logger.debug(stackLevel() + "  existing RDF values for predicate " + predicate + ": " + existingRDFValues);
    }
    // persist by the field type
    if (isBooleanField) {
      final String trueClassName = rdfProperty.trueClass();
      if (trueClassName.isEmpty()) {
        throw new TexaiException("trueClass must be specified for boolean field " + field.getName());
      }
      final String falseClassName = rdfProperty.falseClass();
      if (falseClassName.isEmpty()) {
        throw new TexaiException("falseClass must be specified for boolean field " + field.getName());
      }
      persistBooleanField(
              (Boolean) value,
              existingRDFValues,
              predicate,
              makeURI(trueClassName),
              makeURI(falseClassName),
              field);
      return;
    }
    // persist the current value(s)
    if (value == null) {
      if (isDebugEnabled) {
        logger.debug(stackLevel() + "  field value is null: " + field);
      }
    } else if (value instanceof Collection) {
      if (isDebugEnabled) {
        logger.debug(stackLevel() + "  field value is Collection: " + field + " value: " + value);
      }
      assertedValueTerms = persistFieldValues(
              (Collection) value,
              existingRDFValues,
              rdfProperty,
              predicate);
    } else if (field.getType().isArray()) {
      if (isDebugEnabled) {
        logger.debug(stackLevel() + "  field value is Array: " + field);
      }
      final int arrayLength = Array.getLength(value);
      final List<Object> valueObjects = new ArrayList<Object>(arrayLength);
      for (int i = 0; i < arrayLength; i++) {
        valueObjects.add(Array.get(value, i));
      }
      assertedValueTerms = persistFieldValues(
              valueObjects,
              existingRDFValues,
              rdfProperty,
              predicate);
    } else {
      if (isDebugEnabled) {
        logger.debug(stackLevel() + "  field value is Object: " + field);
      }
      final List<Object> valueList = new ArrayList<Object>(1);
      valueList.add(value);
      assertedValueTerms = persistFieldValues(
              valueList,
              existingRDFValues,
              rdfProperty,
              predicate);
    }
    // delete previous associations if no longer applicable
    for (final Value existingRDFValue : existingRDFValues) {
      // weaker form of the membership test to determine equal RDF literals
      boolean isObsoleteValue = true;
      final String existingRDFValueString = existingRDFValue.toString();
      for (final Value assertedValueTerm : assertedValueTerms) {
        if (assertedValueTerm.toString().equals(existingRDFValueString)) {
          isObsoleteValue = false;
          break;
        }
      }
      if (isObsoleteValue) {
        if (isDebugEnabled) {
          logger.debug(stackLevel() + "deleting obsolete value: " + existingRDFValue + " for predicate: " + predicate);
        }
        final Statement statement = valueFactory.createStatement(
                getInstanceURI(),
                predicate,
                existingRDFValue,
                getContextURI());
        try {
          repositoryConnection.remove(statement);
        } catch (final RepositoryException ex) {
          throw new TexaiException(ex);
        }
        logger.info("deleted: " + rdfUtility.formatStatement(statement));
      }
    }
  }
  
  /** Finds the predicate that will be used to persist the current field.
   *
   * @param field the given RDF entity instance field
   * @param rdfProperty the property annotation associated with the field
   * @param isBooleanField the indicator that the current field is a boolean type
   * @return the predicate that will be used to persist the current field
   */
  private URI findPredicate(final Field field, final RDFProperty rdfProperty, final boolean isBooleanField) {
    //Preconditions
    assert field != null : "field must not be null";
    assert rdfProperty != null : "rdfProperty must not be null";
    
    URI predicate;
    if (isBooleanField) {
      predicate = RDF.TYPE;
    } else if (rdfProperty.predicate().isEmpty()) {
      throw new TexaiException("predicate annotation property is missing");
    } else {
      predicate = makeURI(rdfProperty.predicate());
    }
    // check whether to define the predicate
    if (!definedClassAndPredicateURIs.contains(predicate)) {
      final Set<URI> existingTypes = new HashSet<URI>();
      existingTypesTupleQuery.setBinding("s", predicate);
      TupleQueryResult tupleQueryResult;
      try {
        tupleQueryResult = existingTypesTupleQuery.evaluate();
        while (tupleQueryResult.hasNext()) {
          existingTypes.add((URI) tupleQueryResult.next().getBinding("typeTerm").getValue());
        }
        tupleQueryResult.close();
      } catch (final QueryEvaluationException ex) {
        throw new TexaiException(ex);
      }
      
      if (existingTypes.isEmpty()
      && (rdfProperty.subPropertyOf().length > 0
              || !rdfProperty.domain().isEmpty()
              || !rdfProperty.range().isEmpty())) {
        defineNewPredicate(
                rdfProperty,
                predicate,
                field);
      }
      definedClassAndPredicateURIs.add(predicate);
    }
    
    //Postconditions
    assert predicate != null : "predicate must not be null";
    
    return predicate;
  }
  
  /** Persists the given boolean field value.  When the boolean value is true, a statement of the form
   * <instance-term> <rdf:type> <true-class> is asserted in the RDF store, otherwise a statement of the form
   * <instance-term> <rdf:type> <false-class> is asserted.
   *
   * @param value the boolean value
   * @param existingValues the existing values
   * @param predicate the predicate that represents the association
   * @param trueClass the class of RDF instances for which the boolean association holds true
   * @param falseClass the class of RDF instances for which the boolean association holds false
   * @param field the given RDF entity instance field
   */
  @SuppressWarnings("unchecked")
  private void persistBooleanField(
          final Boolean value,
          final List<Value> existingValues,
          final URI predicate,
          final URI trueClass,
          final URI falseClass,
          final Field field) {
    //Preconditions
    assert value != null : "value must not be null";
    assert existingValues != null : "existingValues must not be null";
    assert predicate != null : "predicate must not be null";
    assert trueClass != null : "trueClassName must not be null";
    assert falseClass != null : "falseClassName must not be null";
    
    try {
      boolean haveCreatedTrueOrFalseClassTerm = false;
      // check whether to define the true and false classes
      if (!definedClassAndPredicateURIs.contains(getClassURI())) {
        // query for existing definition of the true class
        final Set<URI> existingTypes = new HashSet<URI>();
        existingTypesTupleQuery.setBinding("s", trueClass);
        TupleQueryResult tupleQueryResult = existingTypesTupleQuery.evaluate();
        while (tupleQueryResult.hasNext()) {
          existingTypes.add((URI) tupleQueryResult.next().getBinding("typeTerm").getValue());
        }
        tupleQueryResult.close();
        if (existingTypes.isEmpty()) {
          // define true class
          final List<URI> types = new ArrayList<URI>();
          types.add(uriFirstOrderCollection);
          final List<URI> subClassOfURIs = new ArrayList<URI>();
          subClassOfURIs.add(getClassURI());
          rdfUtility.defineRDFClass(
                  trueClass,
                  "This is the collection of " + getClassURI() + " instances for which boolean property " + field.getName() + " holds true.",
                  types,
                  subClassOfURIs);
          haveCreatedTrueOrFalseClassTerm = true;
        }
        // query for existing definition of the false class
        existingTypes.clear();
        existingTypesTupleQuery.setBinding("s", falseClass);
        tupleQueryResult = existingTypesTupleQuery.evaluate();
        while (tupleQueryResult.hasNext()) {
          existingTypes.add((URI) tupleQueryResult.next().getBinding("typeTerm").getValue());
        }
        tupleQueryResult.close();
        if (existingTypes.isEmpty()) {
          // define false class
          final List<URI> types = new ArrayList<URI>();
          types.add(uriFirstOrderCollection);
          final List<URI> subClassOfURIs = new ArrayList<URI>();
          subClassOfURIs.add(getClassURI());
          rdfUtility.defineRDFClass(
                  falseClass,
                  "This is the collection of " + getClassURI() + " instances for which boolean property " + field.getName() + " holds false.",
                  types,
                  subClassOfURIs);
          haveCreatedTrueOrFalseClassTerm = true;
        }
      }
      if (haveCreatedTrueOrFalseClassTerm) {
        rdfUtility.assertDisjoint(trueClass, falseClass);
      }
      
      // assert the truth value relationship
      if (value.booleanValue()) {
        if (!existingValues.contains(trueClass)) {
          final Statement statement = valueFactory.createStatement(
                  getInstanceURI(),
                  RDF.TYPE,
                  trueClass,
                  getContextURI());
          repositoryConnection.add(statement);
          logger.info("persisted: " + rdfUtility.formatStatement(statement));
        }
        if (existingValues.contains(falseClass)) {
          if (isDebugEnabled) {
            logger.debug(stackLevel() + "deleting existing value URI: " + falseClass + " for predicate: " + predicate);
          }
          final Statement statement = valueFactory.createStatement(
                  getInstanceURI(),
                  RDF.TYPE,
                  falseClass,
                  getContextURI());
          repositoryConnection.remove(statement);
          logger.info("deleted: " + rdfUtility.formatStatement(statement));
        }
      } else {
        if (!existingValues.contains(falseClass)) {
          final Statement statement = valueFactory.createStatement(
                  getInstanceURI(),
                  RDF.TYPE,
                  falseClass,
                  getContextURI());
          repositoryConnection.add(statement);
          logger.info("persisted: " + rdfUtility.formatStatement(statement));
        }
        if (existingValues.contains(trueClass)) {
          if (isDebugEnabled) {
            logger.debug(stackLevel() + "deleting existing value URI: " + trueClass + " for predicate: " + predicate);
          }
          final Statement statement = valueFactory.createStatement(
                  getInstanceURI(),
                  RDF.TYPE,
                  trueClass,
                  getContextURI());
          repositoryConnection.remove(statement);
          logger.info("deleted: " + rdfUtility.formatStatement(statement));
        }
      }
    } catch (final OpenRDFException ex) {
      throw new TexaiException(ex);
    }
  }
  
  /**
   * Persists the field value(s) and strength(s).
   *
   *
   * @param valueList the list of java values
   * @param existingRDFValues the  existing RDF values
   * @param isInverseProperty the indicator that the property is to be inverted with respect to treatment of subject and object
   * @param predicateURI the predicate that represents the association
   * @return the set of asserted RDF values
   */
  @SuppressWarnings("unchecked")
  private Set<Value> persistFieldValues(
          final Collection valueList,
          final List<Value> existingRDFValues,
          final RDFProperty rdfProperty,
          final URI predicateURI) {
    //Preconditions
    assert valueList != null : "valueList must not be null";
    assert existingRDFValues != null : "existingValues must not be null";
    assert getContextURI() != null : "contextURI must not be null";
    
    final Set<Value> assertedRDFValues = new HashSet<Value>();
    final List<Value> rdfValues = createRDFValueTerms(valueList, rdfProperty);
    final Iterator<Value> rdfValues_iter = rdfValues.iterator();
    while (rdfValues_iter.hasNext()) {
      final Value rdfValue = rdfValues_iter.next();
      // weaker form of the membership test to determine equal RDF literals
      boolean isNewValue = true;
      final String rdfValueString = rdfValue.toString();
      for (final Value existingRDFValue : existingRDFValues) {
        if (existingRDFValue.toString().equals(rdfValueString)) {
          isNewValue = false;
          break;
        }
      }
      if (isNewValue) {
        Statement statement;
        if (rdfProperty.inverse()) {
          if (!(rdfValue instanceof URI)) {
            throw new TexaiException(rdfValue + " must be a URI for inverse property " + predicateURI);
          }
          statement = valueFactory.createStatement(
                  (URI) rdfValue,
                  predicateURI,
                  getInstanceURI(),
                  getContextURI());
        } else {
          statement = valueFactory.createStatement(
                  getInstanceURI(),
                  predicateURI,
                  rdfValue,
                  getContextURI());
        }
        try {
          repositoryConnection.add(statement);
        } catch (final RepositoryException ex) {
          throw new TexaiException(ex);
        }
        logger.info("persisted: " + rdfUtility.formatStatement(statement));
      }
      assertedRDFValues.add(rdfValue);
    }
    return assertedRDFValues;
  }
  
  /** Creates a new predicate for the given rdf property.
   *
   * @param rdfProperty the given rdf property
   * @param predicate the predicate
   * @param field the given field
   * @return a new predicate for the given rdf property
   */
  private void defineNewPredicate(
          final RDFProperty rdfProperty,
          final URI predicate,
          final Field field) {
    //Preconditions
    assert rdfProperty != null : "rdfProperty must not be null";
    assert predicate != null : "predicate must not be null";
    assert field != null : "field must not be null";
    
    URI domainURI;
    final String domainName = rdfProperty.domain();
    if (domainName.length() == 0) {
      domainURI = getClassURI();
    } else {
      domainURI = makeURI(domainName);
    }
    URI rangeURI;
    final String rangeName = rdfProperty.range();
    if (rangeName.length() == 0) {
      rangeURI = getDefaultTypeURIForFieldType(field);
    } else {
      rangeURI = makeURI(rangeName);
    }
    final List<URI> typeURIs = new ArrayList<URI>(1);
    if (XMLDatatypeUtil.isBuiltInDatatype(rangeURI)) {
      typeURIs.add(OWL.DATATYPEPROPERTY);
    } else {
      typeURIs.add(OWL.OBJECTPROPERTY);
    }
    final List<URI> subPropertyOfURIs = new ArrayList<URI>(1);
    subPropertyOfURIs.add(uriConceptuallyRelated);
    rdfUtility.defineRDFPredicate(
            predicate,
            (String) null,
            typeURIs,
            subPropertyOfURIs,
            domainURI,
            rangeURI);
  }
  
  /** For the purpose of defineing a new predicate, returns the RDF type that corresponds to the type of the given field.
   *
   * @param field the given field
   * @return the URI type that corresponds to the type of the given field
   */
  private URI getDefaultTypeURIForFieldType(final Field field) {
    //Preconditions
    assert field != null : "field must not be null";
    
    return getTypeURIForClass(field.getType());
  }
  
  /** For the purpose of defineing a new predicate, returns the RDF type that corresponds to the type of the given field.
   *
   * @param field the given field
   * @return the URI type that corresponds to the type of the given field
   */
  private URI getTypeURIForClass(final Class fieldClass) {
    //Preconditions
    assert fieldClass != null : "clazz must not be null";
    
    URI fieldClassURI;
    if (fieldClass.equals(Byte.class) || fieldClass.equals(byte.class)) {
      fieldClassURI = XMLSchema.BYTE;
    } else if (fieldClass.equals(Short.class) || fieldClass.equals(short.class)) {
      fieldClassURI = XMLSchema.SHORT;
    } else if (fieldClass.equals(Integer.class) || fieldClass.equals(int.class)) {
      fieldClassURI = XMLSchema.INT;
    } else if (fieldClass.equals(Long.class) || fieldClass.equals(long.class)) {
      fieldClassURI = XMLSchema.LONG;
    } else if (fieldClass.equals(Float.class) || fieldClass.equals(float.class)) {
      fieldClassURI = XMLSchema.FLOAT;
    } else if (fieldClass.equals(Double.class) || fieldClass.equals(double.class)) {
      fieldClassURI = XMLSchema.DOUBLE;
    } else if (fieldClass.equals(String.class)) {
      fieldClassURI = XMLSchema.STRING;
    } else if (fieldClass.equals(BigInteger.class)) {
      fieldClassURI = XMLSchema.INTEGER;
    } else if (fieldClass.equals(BigDecimal.class)) {
      fieldClassURI = XMLSchema.DECIMAL;
    } else if (fieldClass.equals(Calendar.class)) {
      fieldClassURI = XMLSchema.DATETIME;
    } else if (fieldClass.equals(Date.class)) {
      fieldClassURI = XMLSchema.DATETIME;
    } else if (fieldClass.equals(QName.class)) {
      fieldClassURI = XMLSchema.QNAME;
    } else if (fieldClass.equals(java.net.URI.class)) {
      fieldClassURI = XMLSchema.ANYURI;
    } else if (fieldClass.equals(byte[].class)) {
      fieldClassURI = XMLSchema.BASE64BINARY;
    } else if (fieldClass.isArray()) {
      fieldClassURI = getTypeURIForClass(fieldClass.getComponentType());
    } else {
      fieldClassURI = getClassURI(fieldClass);
    }
    
    //Postconditions
    assert fieldClassURI != null : "classURI must not be null";
    
    return fieldClassURI;
  }
  
  /** Creates the RDF values for the given value list.
   *
   * @param valueList the given java value list
   * @param rdfProperty the RDF property annotation
   * @return the RDF values for the given value list
   */
  private List<Value> createRDFValueTerms(final Collection valueList, final RDFProperty rdfProperty) {
    //Preconditions
    assert valueList != null : "valueList must not be null";
    assert rdfProperty != null : "rdfProperty must not be null";
    
    if (isDebugEnabled) {
      logger.debug(stackLevel() + "  valueList: " + valueList);
    }
    final List<Value> rdfValues = new ArrayList<Value> (valueList.size());
    for (final Object value : valueList) {
      rdfValues.add(getRDFValueFromJavaValue(value, rdfProperty));
    }
    return rdfValues;
  }
  
  /** Gets the RDF value that will be persisted for the given java value.
   *
   * @param value the given java value
   * @param rdfProperty the RDF property annotation
   * @return the RDF value term for the given value
   */
  private Value getRDFValueFromJavaValue(final Object value, final RDFProperty rdfProperty) {    // NOPMD
    //Preconditions
    assert value != null : "value must not be null";
    assert rdfProperty != null : "rdfProperty must not be null";
    
    Value rdfValue = null;
    if (value instanceof Byte) {
      if (rdfProperty.range().endsWith("unsignedByte")) {
        if (((Byte) value).byteValue() < 0) {
          throw new TexaiException("attempt to persist a negative byte value [" + value + "] for an unsignedByte field");
        }
        rdfValue = valueFactory.createLiteral(value.toString(), XMLSchema.UNSIGNED_BYTE);
      } else {
        rdfValue = valueFactory.createLiteral((Byte) value);
      }
    } else if (value instanceof Short) {
      if (rdfProperty.range().endsWith("unsignedShort")) {
        if (((Short) value).shortValue() < 0) {
          throw new TexaiException("attempt to persist a negative short value [" + value + "] for an unsignedShort field");
        }
        rdfValue = valueFactory.createLiteral(value.toString(), XMLSchema.UNSIGNED_SHORT);
      } else {
        rdfValue = valueFactory.createLiteral((Short) value);
      }
    } else if (value instanceof Integer) {
      if (rdfProperty.range().isEmpty()) {
        rdfValue = valueFactory.createLiteral((Integer) value);
      } else if (rdfProperty.range().endsWith("unsignedInt")) {
        if (((Integer) value).intValue() < 0) {
          throw new TexaiException("attempt to persist a negative integer value [" + value + "] for an unsignedInt field");
        }
        rdfValue = valueFactory.createLiteral(value.toString(), XMLSchema.UNSIGNED_INT);
      } else {
        rdfValue = valueFactory.createLiteral((Integer) value);
      }
    } else if (value instanceof Long) {
      if (rdfProperty.range().endsWith("unsignedLong")) {
        if (((Long) value).longValue() < 0) {
          throw new TexaiException("attempt to persist a negative long value [" + value + "] for an unsignedLong field");
        }
        rdfValue = valueFactory.createLiteral(value.toString(), XMLSchema.UNSIGNED_LONG);
      } else {
        rdfValue = valueFactory.createLiteral((Long) value);
      }
    } else if (value instanceof Float) {
      rdfValue = valueFactory.createLiteral((Float) value);
    } else if (value instanceof Double) {
      rdfValue = valueFactory.createLiteral((Double) value);
    } else if (value instanceof String) {
      rdfValue = valueFactory.createLiteral((String) value);
    } else if (value instanceof BigInteger) {
      if (rdfProperty.range().endsWith("positiveInteger")) {
        if (((BigInteger) value).longValue() <= 0) {
          throw new TexaiException("attempt to persist a non-positive integer value [" + value + "] for an positiveInteger field");
        }
        rdfValue = valueFactory.createLiteral(datatypeConverter.printInteger((BigInteger) value), XMLSchema.POSITIVE_INTEGER);
      } else if (rdfProperty.range().endsWith("nonNegativeInteger")) {
        if (((BigInteger) value).longValue() < 0) {
          throw new TexaiException("attempt to persist a negative integer value [" + value + "] for an nonNegativeInteger field");
        }
        rdfValue = valueFactory.createLiteral(datatypeConverter.printInteger((BigInteger) value), XMLSchema.NON_NEGATIVE_INTEGER);
      } else if (rdfProperty.range().endsWith("nonPositiveInteger")) {
        if (((BigInteger) value).longValue() > 0) {
          throw new TexaiException("attempt to persist a positive integer value [" + value + "] for an nonPositiveInteger field");
        }
        rdfValue = valueFactory.createLiteral(datatypeConverter.printInteger((BigInteger) value), XMLSchema.NON_POSITIVE_INTEGER);
      } else if (rdfProperty.range().endsWith("negativeInteger")) {
        if (((BigInteger) value).longValue() >= 0) {
          throw new TexaiException("attempt to persist a non-negative integer value [" + value + "] for an negativeInteger field");
        }
        rdfValue = valueFactory.createLiteral(datatypeConverter.printInteger((BigInteger) value), XMLSchema.NEGATIVE_INTEGER);
      } else {
        rdfValue = valueFactory.createLiteral(datatypeConverter.printInteger((BigInteger) value), XMLSchema.INTEGER);
      }
    } else if (value instanceof BigDecimal) {
      rdfValue = valueFactory.createLiteral(datatypeConverter.printDecimal((BigDecimal) value), XMLSchema.DECIMAL);
    } else if (value instanceof Calendar) {
      rdfValue = valueFactory.createLiteral(datatypeConverter.printDateTime((Calendar) value), XMLSchema.DATETIME);
    } else if (value instanceof Date) {
      final Calendar calendar = Calendar.getInstance();
      calendar.setTime((Date) value);
      rdfValue = valueFactory.createLiteral(datatypeConverter.printDateTime(calendar), XMLSchema.DATETIME);
    } else if (value instanceof QName) {
      throw new TexaiException("QName type is not implemented for persistence");
    } else if (value instanceof java.net.URI) {
      rdfValue = valueFactory.createLiteral(((java.net.URI) value).toString(), XMLSchema.ANYURI);
    } else if (value instanceof Value) {
      rdfValue = (Value) value;
    } else if (value instanceof Byte[]) {
      throw new TexaiException("Byte[] type is not implemented for persistence");
    } else if (isRDFEntity(value)) {
      final Cache cache = CacheManager.getInstance().getCache(Constants.CACHE_CONNECTED_RDF_ENTITIES);
      final Element element = cache.get(value);
      if (element != null) {
        rdfValue = (Value) element.getObjectValue();
      }
      if (rdfValue == null) {
        // save this session state before the recursive method call because session beans cannot
        // create a new session bean instance
        if (isDebugEnabled) {
          logger.debug(stackLevel() + "  saving session state for rdfEntity: " + getRDFEntity());
        }
        saveAbstractSessionState();
        saveSessionState();
        rdfValue = persist(value);
        restoreAbstractSessionState();
        restoreSessionState();
        if (isDebugEnabled) {
          logger.debug(stackLevel() + "  restored session state for rdfEntity: " + getRDFEntity());
        }              if (isDebugEnabled) {
          
        } else {
          logger.debug(stackLevel() + "  previously persisted rdfValue: " + rdfValue);
        }
      }
    } else {
      throw new TexaiException("unhandled value type (" + value.getClass().getName() + ") " + value);
    }
    
    //Postconditions
    assert rdfValue != null : "rdfValue must not be null";
    
    return rdfValue;
  }
  
  /** Pushes the current session bean state onto a stack and then intitializes the session state. */
  public void saveSessionState() {
    //Preconditions
    assert rdfEntityInfoStack != null : "rdfEntityInfoStack must not be null";
    
    if (isDebugEnabled) {
      logger.debug(stackLevel() + "saving session state");
    }
    rdfEntityInfoStack.push(new RDFEntityInfo(recursiveContextURI, isNewDomainInstance));
  }
  
  /** Initializes the session bean state during a recursive method call. */
  private void initializeSessionState() {
    recursiveContextURI = null;                            // NOPMD
    isNewDomainInstance = true;                            // NOPMD
  }
  
  /** Restores the session state following a recursive method call. */
  public void restoreSessionState() {
    //Preconditions
    assert rdfEntityInfoStack != null : "rdfEntityInfoStack must not be null";
    
    final RDFEntityInfo rdfEntityInfo = rdfEntityInfoStack.pop();
    recursiveContextURI = rdfEntityInfo.contextURI;
    isNewDomainInstance = rdfEntityInfo.isNewDomainInstance;
    if (isDebugEnabled) {
      logger.debug(stackLevel() + "restored session state");
    }
  }
  
  /** Contains the session bean state for recursive method calls. */
  private static class RDFEntityInfo {
    
    /** the context into which the class-scoped associations are persisted */
    private final URI contextURI;                          // NOPMD
    
    /** the indicator that the domain instance is new */
    private boolean isNewDomainInstance;                   // NOPMD
    
    /**
     * Creates a new RDFEntityInfo instance.
     *
     * @param contextURI the context into which the class-scoped associations are persisted
     * @param isNewDomainInstance  the indicator that the domain instance is new
     */
    protected RDFEntityInfo(
            final URI contextURI,
            final boolean isNewDomainInstance) {
      this.contextURI = contextURI;
      this.isNewDomainInstance = isNewDomainInstance;
    }
  }
  
  
}




See more files for this project here

Texai

Texai is an chatbot that intelligently seeks to acquire knowledge and friendly behaviors.

Project homepage: http://sourceforge.net/projects/texai
Programming language(s): Java,Shell Script,XML
License: other

  benchmark/
    AbstractRDFTestEntity.java
    PerformanceBenchmark.java
    RDFTestEntity.java
  lazy/
    LazyList.java
    LazySet.java
    RDFEntityLazyLoader.java
  sample/
    Agent.java
    Friend.java
    LoadAFriend.java
    PersistANewFriend.java
    Person.java
    RemoveAFriend.java
    UpdateAFriend.java
  AbstractRDFEntityAccessor.java
  RDFEntity.java
  RDFEntityLoader.java
  RDFEntityLoaderFactory.java
  RDFEntityLoaderPool.java
  RDFEntityManager.java
  RDFEntityPersister.java
  RDFEntityRemover.java
  RDFNamespace.java
  RDFProperty.java
  RDFUtility.java