DomainEntityLoaderBean.java from Texai at Krugle
Show DomainEntityLoaderBean.java syntax highlighted
/*
* DomainEntityLoaderBean.java
*
* Created on October 31, 2006, 11:22 AM
*
* Description: This stateless session bean loads domain entities from the knowledge base,
* mapping KB propositions onto domain entity associations.
*
* 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.ejb.session;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Stack;
import java.util.UUID;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.FetchType;
import javax.persistence.OrderBy;
import javax.persistence.Transient;
import net.sf.cglib.proxy.Enhancer;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.apache.log4j.Logger;
import org.texai.kb.Constants;
import org.texai.kb.ejb.session.shared.AssociationFinderLocal;
import org.texai.kb.ejb.session.shared.TermDefinitionAccessorLocal;
import org.texai.kb.ejb.session.shared.TermFinderFacadeLocal;
import org.texai.kb.entity.AtomicTerm;
import org.texai.kb.entity.Formula;
import org.texai.kb.entity.NonAtomicTerm;
import org.texai.kb.entity.PDate;
import org.texai.kb.entity.PDouble;
import org.texai.kb.entity.PLong;
import org.texai.kb.entity.PString;
import org.texai.kb.entity.AbstractReifiedTerm;
import org.texai.kb.entity.AbstractTerm;
import org.texai.kb.persistence.DomainProperty;
import org.texai.kb.persistence.lazy.DomainEntityLazyLoader;
import org.texai.kb.persistence.lazy.LazyList;
import org.texai.kb.persistence.lazy.LazySet;
import org.texai.util.ByteUtils;
import org.texai.util.TexaiException;
/**
*
* @author reed
*/
@Stateless
public class DomainEntityLoaderBean extends AbstractDomainEntityAccessor implements DomainEntityLoaderLocal { // NOPMD
/** the term finder, which is injected by the container */
@EJB
private TermFinderFacadeLocal termFinderFacade; // NOPMD
/** the association finder, which is injected by the container */
@EJB
private AssociationFinderLocal associationFinder; // NOPMD
/** the term definition accessor, which is injected by the container */
@EJB
private TermDefinitionAccessorLocal termDefinitionAccessor; // NOPMD
/** the logger */
@Transient
private Logger logger; // NOPMD
/** the dictionary of connected domain entities, term --> domain instance */
@Transient
private Map<AtomicTerm, Object> connectedDomainEntityDictionary = new HashMap<AtomicTerm, Object>();
/** the stack of domain entity information that allows the session bean to perform recursive method calls */
@Transient
private final Stack<DomainEntityInfo> domainEntityInfoStack = new Stack<DomainEntityInfo>();
/** Creates a new instance of DomainEntityLoaderBean */
public DomainEntityLoaderBean() {
super();
}
/** Injects the shared session bean dependencies when executed out of the container. */
public void injectSharedBeanDependencies() {
if (termFinderFacade != null) {
termFinderFacade.setTermDefinitionAccessor(termDefinitionAccessor);
}
if (associationFinder != null) {
associationFinder.setTermFinderFacade(termFinderFacade);
}
if (termDefinitionAccessor != null) {
termDefinitionAccessor.setTermFinderFacade(termFinderFacade);
termDefinitionAccessor.setAssociationFinder(associationFinder);
}
}
/** Loads the domain entity from propositions in the knowledge base given its term id.
*
* @param termId the id of the atomic term that represents the domain entity
* @return the domain entity
*/
public Object loadDomainEntity(final UUID termId) {
//Preconditions
assert termId != null : "termId must not be null";
return loadDomainEntity(termId, false);
}
/** Loads the domain entity from propositions in the knowledge base given its term id.
*
* @param termId the id of the atomic term that represents the domain entity
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
* @return the domain entity
*/
public Object loadDomainEntity(final UUID termId, final boolean isLoadableFromDomainEntityCache) {
//Preconditions
assert termId != null : "termId must not be null";
getLogger().debug(stackLevel() + "loading domain entity with term id: " + termId);
final AtomicTerm instanceTerm = termFinderFacade.findAtomicTermByTermId(ByteUtils.toBytes(termId));
if (instanceTerm == null) {
throw new IllegalArgumentException("no domain entity instance term found for term id " + termId);
}
return loadDomainEntity(instanceTerm, isLoadableFromDomainEntityCache);
}
/** Loads the domain entity from propositions in the knowledge base given its instance term.
*
* @param instanceTerm the atomic term that represents the domain entity
* @return the domain entity
*/
public Object loadDomainEntity(final AtomicTerm instanceTerm) {
//Preconditions
assert instanceTerm != null : "instanceTerm must not be null";
return loadDomainEntity(instanceTerm, false);
}
/** Loads the domain entity from propositions in the knowledge base given its term id.
*
* @param instanceTerm the atomic term that represents the domain entity
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
* @return the domain entity
*/
public Object loadDomainEntity(final AtomicTerm instanceTerm, final boolean isLoadableFromDomainEntityCache) {
//Preconditions
assert instanceTerm != null : "instanceTerm must not be null";
connectedDomainEntityDictionary.clear();
return loadDomainEntityInternal(instanceTerm, isLoadableFromDomainEntityCache);
}
/** Loads domain entities having the given property and value.
*
* @param property the given property
* @param value the value of the property
* @param domainEntityClass the class of the desired domain entity
* @return the domain entities having the given property and value
*/
public Set<Object> loadDomainEntitiesByPropertyValue(
final AtomicTerm property,
final Object value,
final Class domainEntityClass) {
//Preconditions
assert property != null : "property must not be null";
assert value != null : "value must not be null";
assert domainEntityClass != null : "domainEntityClass must not be null";
final Set<Object> domainEntities = new HashSet<Object>();
connectedDomainEntityDictionary.clear();
initializeAbstractSessionState();
final Set<AtomicTerm> instanceTerms = findInstanceTermsByPropertyValue(property, value, domainEntityClass);
for (final AtomicTerm instanceTerm : instanceTerms) {
domainEntities.add(loadDomainEntity(instanceTerm, false));
}
return domainEntities;
}
/** Loads the domain entity from propositions in the knowledge base given an identifying property and value, or null if
* not found.
*
* @param property the identifying property
* @param value the value of the property which identifies the desired domain entity
* @param domainEntityClass the class of the desired domain entity
* @return the domain entity having the given property and value, or null if not found
*/
public Object loadDomainEntityByIndentifyingPropertyValue(
final AtomicTerm property,
final Object value,
final Class domainEntityClass) {
//Preconditions
assert property != null : "property must not be null";
assert value != null : "value must not be null";
assert domainEntityClass != null : "domainEntityClass must not be null";
return loadDomainEntityByIndentifyingPropertyValue(property, value, domainEntityClass, false);
}
/** Loads the domain entity from propositions in the knowledge base given an identifying property and value, or null if
* not found.
*
* @param property the identifying property
* @param value the value of the property which identifies the desired domain entity
* @param domainEntityClass the class of the desired domain entity
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
* @return the domain entity having the given property and value, or null if not found
*/
public Object loadDomainEntityByIndentifyingPropertyValue(
final AtomicTerm property,
final Object value,
final Class domainEntityClass,
final boolean isLoadableFromDomainEntityCache) {
//Preconditions
assert property != null : "property must not be null";
assert value != null : "value must not be null";
assert domainEntityClass != null : "domainEntityClass must not be null";
loadDomainEntityShellByIndentifyingPropertyValue(property, value, domainEntityClass);
if (getInstanceTerm() == null) {
return null;
}
loadFields(isLoadableFromDomainEntityCache);
return getDomainEntity();
}
/** Loads the domain entity from propositions in the knowledge base given an identifying property and value.
*
* @param property the identifying property
* @param value the value of the property which identifies the desired domain entity
* @param ClassPersistenceContext the context from which the class-scoped associations are loaded
* @return the domain entity having the given property and value, or null if not found
*/
public Object loadDomainEntityByIndentifyingPropertyValue(
final AtomicTerm property,
final Object value,
final AbstractReifiedTerm ClassPersistenceContext) {
//Preconditions
assert property != null : "property must not be null";
assert value != null : "value must not be null";
assert ClassPersistenceContext != null : "ClassPersistenceContext must not be null";
return loadDomainEntityByIndentifyingPropertyValue(property, value, ClassPersistenceContext, false);
}
/** Loads the domain entity from propositions in the knowledge base given an identifying property and value.
*
* @param property the identifying property
* @param value the value of the property which identifies the desired domain entity
* @param ClassPersistenceContext the context from which the class-scoped associations are loaded
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
* @return the domain entity having the given property and value, or null if not found
*/
public Object loadDomainEntityByIndentifyingPropertyValue(
final AtomicTerm property,
final Object value,
final AbstractReifiedTerm ClassPersistenceContext,
final boolean isLoadableFromDomainEntityCache) {
//Preconditions
assert property != null : "property must not be null";
assert value != null : "value must not be null";
assert ClassPersistenceContext != null : "ClassPersistenceContext must not be null";
getLogger().debug(stackLevel() + "loading domain entity by identifying property: "
+ property + "\n and value: " + value
+ "\n context: " + ClassPersistenceContext);
final AbstractTerm arg2 = termFinderFacade.findTermByValue(value);
if (arg2 == null) {
return null;
}
final Set<AbstractTerm> arg1s = associationFinder.gatherArg1TermsFromBinaryGAFs(
property,
arg2,
ClassPersistenceContext);
if (arg1s.isEmpty()) {
return null;
}
if (arg1s.size() > 1) {
throw new TexaiException("expected only one domain entity, found " + arg1s + "\n property: "
+ property + "\n and value: " + value
+ "\n context: " + ClassPersistenceContext);
}
return loadDomainEntity((AtomicTerm) arg1s.toArray()[0], isLoadableFromDomainEntityCache);
}
/** Loads the domain entity shell, which is the @Id field only, from propositions in the knowledge base
* given an identifying property and value, or null if not found.
*
* @param property the identifying property
* @param value the value of the property which identifies the desired domain entity
* @param domainEntityClass the class of the desired domain entity
* @return the domain entity shell having the given property and value, or null if not found
*/
public Object loadDomainEntityShellByIndentifyingPropertyValue(
final AtomicTerm property,
final Object value,
final Class domainEntityClass) {
//Preconditions
assert property != null : "property must not be null";
assert value != null : "value must not be null";
assert domainEntityClass != null : "domainEntityClass must not be null";
getLogger().debug(stackLevel() + "loading domain entity shell by identifying property: "
+ property + "\n and value: " + value
+ "\n domain enitity class: " + domainEntityClass);
connectedDomainEntityDictionary.clear();
initializeAbstractSessionState();
if (!findInstanceTermByIndentifyingPropertyValue(property, value, domainEntityClass)) {
return null;
}
instantiateDomainEntity();
loadIdField();
final Cache cache = CacheManager.getInstance().getCache(Constants.CACHE_CONNECTED_DOMAIN_ENTITY_TERM_IDS);
assert cache != null : "cache not found for: " + Constants.CACHE_CONNECTED_DOMAIN_ENTITY_TERM_IDS;
final Element element = new Element(getDomainEntity(), getInstanceTerm().getTermId());
cache.put(element);
return getDomainEntity();
}
/** Loads the domain entity field.
*
* @param domainEntity the domain entity
* @param field the field to be loaded
* @param domainProperty the domain property the associates field value(s) in the knowledge base
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
* @return the value of the loaded field
*/
public Object loadDomainEntityField(
final Object domainEntity,
final Field field,
final DomainProperty domainProperty,
final boolean isLoadableFromDomainEntityCache) {
//Preconditions
assert domainEntity != null : "domainInstance must not be null";
assert field != null : "field must not be null";
assert domainProperty != null : "domainProperty must not be null";
getLogger().debug(stackLevel() + "loading domain entity entity " + domainEntity.getClass().getName()
+ "\n field: " + field
+ "\n domainProperty: " + domainProperty);
saveAbstractSessionState();
initializeAbstractSessionState();
setDomainEntityClass(domainEntity.getClass());
setDomainEntity(domainEntity);
gatherAnnotationsForDomainEntityClass();
configureDomainContextSettings();
setClassPersistenceContext(termFinderFacade.findAtomicTermByTermName(getClassPersistenceContextName()));
setInstanceTermFromIdField();
final Object value = loadField(field, domainProperty, isLoadableFromDomainEntityCache);
restoreAbstractSessionState();
return value;
}
/** Finds the instance term by the given identifying property value.
*
* @param property the identifying property
* @param value the value of the property which identifies the desired domain entity
* @param domainEntityClass the class of the desired domain entity
* @return true if the term is found
*/
private boolean findInstanceTermByIndentifyingPropertyValue(
final AtomicTerm property,
final Object value,
final Class domainEntityClass) {
//Preconditions
assert property != null : "property must not be null";
assert value != null : "value must not be null";
assert domainEntityClass != null : "domainEntityClass must not be null";
final AbstractTerm arg2 = termFinderFacade.findTermByValue(value);
if (arg2 == null) {
return false;
}
setDomainEntityClass(domainEntityClass);
gatherAnnotationsForDomainEntityClass();
configureDomainContextSettings();
setClassPersistenceContext(termFinderFacade.findAtomicTermByTermName(getClassPersistenceContextName()));
final Set<AbstractTerm> arg1s = associationFinder.gatherArg1TermsFromBinaryGAFs(
property,
arg2,
getClassPersistenceContext());
if (arg1s.isEmpty()) {
return false;
}
if (arg1s.size() > 1) {
throw new TexaiException("expected only one domain entity, found " + arg1s + "\n property: "
+ property + "\n and value: " + value
+ "\n domain enitity class: " + domainEntityClass);
}
final AtomicTerm instanceTerm = (AtomicTerm) arg1s.toArray()[0];
setInstanceTerm(instanceTerm);
return true;
}
/** Returns the set of instance terms having the given property and value.
*
* @param property the identifying property
* @param value the value of the property which identifies the desired domain entity
* @param domainEntityClass the class of the desired domain entity
* @return true if the term is found
*/
private Set<AtomicTerm> findInstanceTermsByPropertyValue(
final AtomicTerm property,
final Object value,
final Class domainEntityClass) {
//Preconditions
assert property != null : "property must not be null";
assert value != null : "value must not be null";
assert domainEntityClass != null : "domainEntityClass must not be null";
final Set<AtomicTerm> instanceTerms = new HashSet<AtomicTerm>();
final AbstractTerm arg2 = termFinderFacade.findTermByValue(value);
if (arg2 == null) {
return instanceTerms;
}
setDomainEntityClass(domainEntityClass);
gatherAnnotationsForDomainEntityClass();
configureDomainContextSettings();
setClassPersistenceContext(termFinderFacade.findAtomicTermByTermName(getClassPersistenceContextName()));
final Set<AbstractTerm> arg1s = associationFinder.gatherArg1TermsFromBinaryGAFs(
property,
arg2,
getClassPersistenceContext());
for (final AbstractTerm arg1 : arg1s) {
instanceTerms.add((AtomicTerm) arg1);
}
return instanceTerms;
}
/** Loads the domain entity from propositions in the knowledge base given its instance term, without clearing
* the dictionary of connected domain entities.
*
* @param instanceTerm the atomic term that represents the domain entity
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
* @return the domain entity
*/
private Object loadDomainEntityInternal(final AtomicTerm instanceTerm, boolean isLoadableFromDomainEntityCache) {
//Preconditions
assert instanceTerm != null : "instanceTerm must not be null";
getLogger().debug(stackLevel() + "loading domain entity instance term: " + instanceTerm);
initializeAbstractSessionState();
setInstanceTerm(instanceTerm);
final Cache cache = CacheManager.getInstance().getCache(Constants.CACHE_CONNECTED_DOMAIN_ENTITY_TERM_IDS);
assert cache != null : "cache not found for: " + Constants.CACHE_CONNECTED_DOMAIN_ENTITY_TERM_IDS;
if (isLoadableFromDomainEntityCache) {
final Element element = cache.get(instanceTerm.getTermId());
if (element != null) {
setDomainEntity(element.getObjectValue());
}
} else {
instantiateDomainEntity();
gatherAnnotationsForDomainEntityClass();
configureDomainContextSettings();
loadIdField();
loadFields(isLoadableFromDomainEntityCache);
getLogger().debug(stackLevel() + "caching connected domain entity: " + getDomainEntity().getClass().getName() + "-->" + instanceTerm);
final Element element = new Element(getDomainEntity(), instanceTerm.getTermId());
cache.put(element);
}
//Postconditions
assert getDomainEntity() != null : "domainEntity must not be null";
return getDomainEntity();
}
/** Sets the instance term from the term id field contained in the domain entity. */
private void setInstanceTermFromIdField() {
//Preconditions
assert getFieldAnnotationDictionary() != null : "fieldAnnotationDictionary must not be null";
assert !getFieldAnnotationDictionary().isEmpty() : "fieldAnnotationDictionary must not be empty";
assert getDomainEntity() != null : "domainEntity() must not be null";
getLogger().debug(stackLevel() + " finding ID field for domainEntity " + getDomainEntity().getClass().getName());
boolean isIdFound = false;
Object value = null;
for (final Field field : getFieldAnnotationDictionary().keySet()) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
try {
value = field.get(getDomainEntity());
} catch (final IllegalArgumentException ex) {
throw new TexaiException(ex);
} catch (final IllegalAccessException ex) {
throw new TexaiException(ex);
}
final Annotation annotation = getFieldAnnotationDictionary().get(field);
getLogger().debug(stackLevel() + " field: " + field + ", annotation: " + annotation);
if ("@javax.persistence.Id()".equals(annotation.toString())) {
getLogger().debug(stackLevel() + " found ID field");
isIdFound = true;
break;
}
}
if (!isIdFound) {
throw new TexaiException("Id field not found for domain entity " + getDomainEntity().getClass().getName());
}
if (!(value instanceof UUID)) {
throw new TexaiException("Id field must be type UUID " + getDomainEntity() + ", is type " + value.getClass());
}
setInstanceTerm(termFinderFacade.findAtomicTermByTermId(ByteUtils.toBytes((UUID) value)));
getLogger().debug(stackLevel() + " found existing instance " + getInstanceTerm());
}
/** Returns an iterator over the set of domain entity terms that represent instances of the given domain entity class.
*
* @param domainEntityClass the given domain entity class
*/
public Iterator<Object> domainEntityIterator(final Class domainEntityClass) {
//Preconditions
assert domainEntityClass != null : "domainEntityClass must not be null";
return new DomainEntityIterator<Object>(domainEntityClass);
}
/** Provides an iterator over the instances of the specified domain entity class. */
public class DomainEntityIterator<E> implements Iterator<E> {
/** the domain entity class name */
private final String domainEntityClassName;
/** the iterator over the set of domain entity terms that represent instances of the given domain entity class */
private final Iterator<AbstractTerm> domainEntityTermSetIter;
/** Constructs a new DomainEntityIterator instance.
*
* @param domainEntityClass the given domain entity class
*/
public DomainEntityIterator(final Class domainEntityClass) {
//Preconditions
assert domainEntityClass != null : "domainEntityClass must not be null";
domainEntityClassName = domainEntityClass.getName();
saveAbstractSessionState();
saveSessionState();
connectedDomainEntityDictionary.clear();
initializeAbstractSessionState();
setDomainEntityClass(domainEntityClass);
gatherAnnotationsForDomainEntityClass();
configureDomainContextSettings();
setClassPersistenceContext(termFinderFacade.findAtomicTermByTermName(getClassPersistenceContextName()));
configureDomainEntitySettings();
final AtomicTerm domainEntityClassTerm = termFinderFacade.findAtomicTermByTermName(getClassTermName());
if (domainEntityClassTerm == null) {
throw new TexaiException("cannot find class term for " + domainEntityClass);
}
final Set<AbstractTerm> domainEntityTermSet = associationFinder.gatherArg1TermsFromBinaryGAFs(
termFinderFacade.findAtomicTermByTermName(Constants.TERM_NAME_ISA),
domainEntityClassTerm,
getClassPersistenceContext());
restoreAbstractSessionState();
restoreSessionState();
domainEntityTermSetIter = domainEntityTermSet.iterator();
}
/** Returns <tt>true</tt> if the iteration has more elements. (In other
* words, returns <tt>true</tt> if <tt>next</tt> would return an element
* rather than throwing an exception.)
*
* @return <tt>true</tt> if the iterator has more elements.
*/
public boolean hasNext() {
return domainEntityTermSetIter.hasNext();
}
/** Returns the next element in the iteration. Calling this method
* repeatedly until the {@link #hasNext()} method returns false will
* return each element in the underlying collection exactly once.
*
* @return the next element in the iteration.
* @exception NoSuchElementException iteration has no more elements.
*/
@SuppressWarnings("unchecked")
public E next() {
if (!domainEntityTermSetIter.hasNext()) {
throw new NoSuchElementException("iterator is empty and cannot return the next element");
}
final AtomicTerm domainEntityTerm = (AtomicTerm) domainEntityTermSetIter.next();
saveAbstractSessionState();
connectedDomainEntityDictionary.clear();
final Object domainEntity = loadDomainEntityInternal(domainEntityTerm, false);
restoreAbstractSessionState();
return (E) domainEntity;
}
/** Removes from the underlying collection the last element returned by the
* iterator (optional operation). This method can be called only once per
* call to <tt>next</tt>. The behavior of an iterator is unspecified if
* the underlying collection is modified while the iteration is in
* progress in any way other than by calling this method.
*
* @exception UnsupportedOperationException if the <tt>remove</tt>
* operation is not supported by this Iterator.
*
* @exception IllegalStateException if the <tt>next</tt> method has not
* yet been called, or the <tt>remove</tt> method has already
* been called after the last call to the <tt>next</tt>
* method.
*/
public void remove() {
domainEntityTermSetIter.remove();
}
/** Returns a string representation of this object.
*
* @return a string representation of this object
*/
@Override
public String toString() {
return "[iterator over instances of " + domainEntityClassName + "]";
}
}
/** Sets the term finder during out-of-the-container unit testing.
*
* @param termFinderFacade the term finder
*/
public void setTermFinderFacade(final TermFinderFacadeLocal termFinderFacade) {
//Preconditions
assert termFinderFacade != null : "termFinderFacade must not be null";
this.termFinderFacade = termFinderFacade;
}
/** Sets the association finder during out-of-the-container unit testing.
*
* @param associationFinder the association finder
*/
public void setAssociationFinder(final AssociationFinderLocal associationFinder) {
//Preconditions
assert associationFinder != null : "associationFinder must not be null";
this.associationFinder = associationFinder;
}
/** Sets the term definition accessor during out-of-the-container unit testing.
*
* @param termDefinitionAccessor the term definition accessor
*/
public void setTermDefinitionAccessor(final TermDefinitionAccessorLocal termDefinitionAccessor) {
//Preconditions
assert termDefinitionAccessor != null : "termDefinitionAccessor must not be null";
this.termDefinitionAccessor = termDefinitionAccessor;
}
/** Gets the logger.
*
* @return the logger
*/
protected Logger getLogger() {
if (logger == null) {
logger = Logger.getLogger(DomainEntityLoaderBean.class.getName());
}
return logger;
}
/** Instantiates the domain entity. */
private void instantiateDomainEntity() {
//Preconditions
assert getInstanceTerm() != null : "instanceTerm must not be null";
Object domainEntity = null;
try {
if (getDomainEntityClass() == null) {
setDomainEntityClass(Class.forName(queryDomainEntityClassName()));
}
assert getDomainEntityClass() != null : "domainEntityClass must not be null";
getLogger().debug(stackLevel() + "instantiating: " + getDomainEntityClass().getName() + " for " + getInstanceTerm());
domainEntity = getDomainEntityClass().newInstance();
} catch (final IllegalAccessException ex) {
throw new TexaiException(ex);
} catch (final InstantiationException ex) {
throw new TexaiException("instantiating: " + getDomainEntityClass().getName() + " for " + getInstanceTerm(), ex);
} catch (final ClassNotFoundException ex) {
throw new TexaiException(ex.getMessage()
+ "\ninstance term: " + getInstanceTerm()
+ "\nprettyName: "+ getInstanceTerm().getPrettyName(), ex);
}
assert domainEntity != null : Constants.DOMAIN_ENTITY_ERR;
setDomainEntity(domainEntity);
connectedDomainEntityDictionary.put(getInstanceTerm(), getDomainEntity());
}
/** Returns the domain entity class name for the instance term via KB lookup.
*
* @return the domain entity class name for the instance term
*/
private String queryDomainEntityClassName() {
//Preconditions
assert getInstanceTerm() != null : "instanceTerm must not be null";
final PString classNameTerm = (PString) associationFinder.getArg2TermFromBinaryGAF(
termFinderFacade.findAtomicTermByTermName(Constants.TERM_NAME_DOMAIN_ENTITY_CLASS_NAME),
getInstanceTerm(),
termFinderFacade.findAtomicTermByTermName(Constants.TERM_NAME_UNIVERSAL_VOCABULARY_MT));
assert classNameTerm != null : "cannot find class name term for instance term " + getInstanceTerm();
final String domainEntityClassName = classNameTerm.getStringValue();
//Postconditions
assert domainEntityClassName != null : "domainEntityClassName must not be null";
assert !domainEntityClassName.isEmpty() : "domainEntityClassName must not be an empty string";
return domainEntityClassName;
}
/** Loads the id field of the domain entity. */
private void loadIdField() {
//Preconditions
assert getFieldAnnotationDictionary() != null : "fieldAnnotationDictionary must not be null";
assert !getFieldAnnotationDictionary().isEmpty() : "fieldAnnotationDictionary must not be empty";
assert getDomainEntity() != null :Constants.DOMAIN_ENTITY_ERR;
Field idField = null;
for (final Field field : getFieldAnnotationDictionary().keySet()) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
final Annotation annotation = getFieldAnnotationDictionary().get(field);
getLogger().debug(stackLevel() + "field: " + field + ", annotation: " + annotation);
if ("@javax.persistence.Id()".equals(annotation.toString())) {
getLogger().debug(stackLevel() + " found ID field");
idField = field;
break;
}
}
if (idField == null) {
throw new TexaiException("ID field not found for domain entity " + getDomainEntity());
}
try {
idField.set(getDomainEntity(), getInstanceTerm().getTermId());
} catch (final IllegalArgumentException ex) {
throw new TexaiException(ex.getMessage()
+ "\n domainEntity: " + getDomainEntity()
+ "\n instanceTerm: " + getInstanceTerm()
+ "\n termId: " + getInstanceTerm().getTermId(), ex);
} catch (final IllegalAccessException ex) {
throw new TexaiException(ex);
}
}
/** Loads the domain entity fields from propositions in the knowledge base.
*
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
*/
private void loadFields(final boolean isLoadableFromDomainEntityCache) {
//Preconditions
assert getInstanceTerm() != null : "instanceTerm must not be null";
setClassPersistenceContext(termFinderFacade.findAtomicTermByTermName(getClassPersistenceContextName()));
if (getClassPersistenceContext() == null) {
throw new TexaiException("cannot find loadFromContext term for " + getClassPersistenceContextName());
}
for (final Field field : getFieldAnnotationDictionary().keySet()) {
final Annotation annotation = getFieldAnnotationDictionary().get(field);
getLogger().debug(stackLevel() + "field: " + field + ", annotation: " + annotation);
if ("@javax.persistence.Id()".equals(annotation.toString())) {
getLogger().debug(stackLevel() + " skipping Id field");
continue;
} else {
if (annotation instanceof DomainProperty) {
final Class fieldType = field.getType();
final DomainProperty domainProperty = (DomainProperty) annotation;
if (domainProperty.fetch().equals(FetchType.EAGER)) {
loadField(field, domainProperty, isLoadableFromDomainEntityCache);
} else {
// default behavior is lazy loading
if (fieldType.equals(Set.class)) {
getLogger().debug(stackLevel() + " lazySet for field: " + field);
final LazySet lazySet = new LazySet(
this,
getDomainEntity(),
field,
domainProperty,
isLoadableFromDomainEntityCache);
setFieldValue(field, lazySet, fieldType);
} else if (fieldType.equals(List.class)) {
getLogger().debug(stackLevel() + " lazyList for field: " + field);
final LazyList lazyList = new LazyList(
this,
getDomainEntity(),
field,
domainProperty,
isLoadableFromDomainEntityCache);
setFieldValue(field, lazyList, fieldType);
} else if (isDomainEntityClass(fieldType)) {
final List<List<Object>> valueAndStrengthPairs =
getValueStrengthPairs(field, domainProperty, isLoadableFromDomainEntityCache);
if (!valueAndStrengthPairs.isEmpty()) {
getLogger().debug(stackLevel() + " lazyily loaded proxy for field: " + field);
// load the value upon first access to the associated proxy
final DomainEntityLazyLoader domainEntityLazyLoader = new DomainEntityLazyLoader(
this,
getDomainEntity(),
field,
domainProperty,
isLoadableFromDomainEntityCache);
// dynamically create the proxy using cglib
final Object lazyObjectProxy = Enhancer.create(fieldType, domainEntityLazyLoader);
setFieldValue(field, lazyObjectProxy, fieldType);
}
} else {
// otherwise rely on Hibernate proxy features when they can apply
loadField(field, domainProperty, isLoadableFromDomainEntityCache);
}
}
}
}
}
}
/** Loads the given field according to the given domain property.
*
* @param field the given domain entity instance field
* @param domainProperty the property annotation associated with the field
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
* @return the loaded value
*/
@SuppressWarnings("unchecked")
private Object loadField(
final Field field,
final DomainProperty domainProperty,
final boolean isLoadableFromDomainEntityCache) {
//Preconditions
assert field != null : "field must not be null";
assert domainProperty != null : "domainProperty must not be null";
// obtain field and strength field attributes
final Class fieldType = field.getType();
getLogger().debug(stackLevel() + " field: " + field);
getLogger().debug(stackLevel() + " field type: " + fieldType.getName());
final List<List<Object>> valueAndStrengthPairs =
getValueStrengthPairs(field, domainProperty, isLoadableFromDomainEntityCache);
final int valueAndStrengthPairsSize = valueAndStrengthPairs.size();
final Field strengthField = getStrengthFieldDictionary().get(field);
Class strengthFieldType = null;
if (strengthField != null) {
strengthFieldType = strengthField.getType();
getLogger().debug(stackLevel() + " strengthField: " + strengthField);
getLogger().debug(stackLevel() + " strengthField type: " + strengthFieldType.getName());
}
// load the value according to its field type
Object loadedValue = null;
if (fieldType.isArray()) {
// load an array field and array of strengths
final Object array = Array.newInstance(fieldType.getComponentType(), valueAndStrengthPairsSize);
Object strengthFieldArray = null;
if (strengthField != null) {
if (!strengthFieldType.isArray()) {
throw new TexaiException("strengthField " + strengthField + " must be an Array");
}
strengthFieldArray = Array.newInstance(strengthFieldType.getComponentType(), valueAndStrengthPairsSize);
}
for (int i = 0; i < valueAndStrengthPairsSize; i++) {
final List<Object> valueAndStrengthPair = valueAndStrengthPairs.get(i);
Array.set(array, i, valueAndStrengthPair.get(0));
if (strengthField != null) {
Array.set(strengthFieldArray, i, valueAndStrengthPair.get(1));
}
}
loadedValue = array;
setFieldValue(
field,
loadedValue,
fieldType);
if (strengthField != null) {
setFieldValue(
strengthField,
strengthFieldArray,
strengthFieldType);
}
} else if (fieldType.equals(List.class)) {
// load a List field and list of strengths
final OrderBy orderBy = getOrderByDictionary().get(field);
final List list = new ArrayList();
List<Double> strengthList = null;
if (strengthField != null) {
if (fieldType.equals(List.class)) {
strengthList = new ArrayList<Double>(valueAndStrengthPairsSize);
} else {
throw new TexaiException("strengthField must be a List");
}
}
if (orderBy != null) {
final ValueAndStrengthPairsComparator valueAndStrengthPairsComparator = new ValueAndStrengthPairsComparator(orderBy);
Collections.sort(valueAndStrengthPairs, valueAndStrengthPairsComparator);
getLogger().debug(stackLevel() + " ordered valueAndStrengthPairs: " + valueAndStrengthPairs);
}
for (List<Object> valueAndStrengthPair : valueAndStrengthPairs) {
list.add(valueAndStrengthPair.get(0));
if (strengthField != null) {
strengthList.add((Double) valueAndStrengthPair.get(1));
}
}
loadedValue = list;
setFieldValue(field, loadedValue, fieldType);
if (strengthField != null) {
setFieldValue(
strengthField,
strengthList,
strengthFieldType);
}
} else if (Collection.class.isAssignableFrom(fieldType)) {
// load a Collection field
Collection collection = null;
Class concreteFieldType = null;
if (fieldType.equals(Collection.class)) {
concreteFieldType = ArrayList.class;
} else if (fieldType.equals(Set.class)) {
concreteFieldType = HashSet.class;
} else {
concreteFieldType = fieldType;
}
try {
collection = (Collection) concreteFieldType.newInstance();
} catch (final InstantiationException ex) {
throw new TexaiException(ex);
} catch (final IllegalAccessException ex) {
throw new TexaiException(ex);
}
for (List<Object> valueAndStrengthPair : valueAndStrengthPairs) {
collection.add(valueAndStrengthPair.get(0));
}
loadedValue = collection;
setFieldValue(field, loadedValue, fieldType);
} else {
// load a single value field and single strength field
if (valueAndStrengthPairsSize > 1) {
getLogger().info(stackLevel() + "expected only one value/strength pair for field " + field + " but found\n " + valueAndStrengthPairs);
}
if (!valueAndStrengthPairs.isEmpty()) {
final List<Object> valueAndStrengthPair = valueAndStrengthPairs.get(0);
loadedValue = valueAndStrengthPair.get(0);
setFieldValue(field, loadedValue, fieldType);
if (strengthField != null) {
setFieldValue(
strengthField,
valueAndStrengthPair.get(1),
strengthFieldType);
}
}
}
return loadedValue;
}
/** Gets the values and GAF strengths for loading the given field, sorted by value termId.
*
* @param field the given domain entity instance field
* @param domainProperty the property annotation associated with the field
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
* @return the ordered list of value/strength pairs for the field
*/
private List<List<Object>> getValueStrengthPairs(
final Field field,
final DomainProperty domainProperty,
final boolean isLoadableFromDomainEntityCache) {
//Preconditions
assert field != null : "field must not be null";
assert domainProperty != null : "domainProperty must not be null";
getLogger().debug(stackLevel() + " processing domain property: " + domainProperty);
final boolean isBooleanField = "boolean".equals(field.getType().getName());
String predicateName = null;
if (isBooleanField) {
predicateName = Constants.TERM_NAME_ISA;
} else {
predicateName = domainProperty.name();
}
if (predicateName.length() == 0) {
predicateName = field.getName();
}
AtomicTerm predicate = null;
final Cache cache = CacheManager.getInstance().getCache(Constants.CACHE_LOADED_DOMAIN_ENTITY_PREDICATES);
assert cache != null : "cache not found: " + Constants.CACHE_LOADED_DOMAIN_ENTITY_PREDICATES;
final Element element = cache.get(predicateName);
if (element == null) {
predicate = termFinderFacade.findAtomicTermByTermName(predicateName);
if (predicate != null) {
cache.put(new Element(predicateName, predicate));
}
} else {
predicate = (AtomicTerm) element.getObjectValue();
}
if (predicate == null) {
getLogger().debug(stackLevel() + "new property: " + predicateName);
return new ArrayList<List<Object>>(0);
}
if (!field.isAccessible()) {
field.setAccessible(true);
}
final List<List<Object>> valueAndStrengthPairs = queryForValuesAndStrengths(
predicate,
field.getType(),
domainProperty.inverse(),
isLoadableFromDomainEntityCache);
if (isBooleanField) {
getLogger().debug(stackLevel() + " boolean field: " + field.getName());
// for boolean values, return a true value if the trueClass is among the queried values, otherwise return a false value with
// its associated strength
final String trueClassName = domainProperty.trueClass();
if (trueClassName.isEmpty()) {
throw new TexaiException("trueClass must be specified for boolean field " + field.getName());
}
final AtomicTerm trueClass = termFinderFacade.findAtomicTermByTermName(trueClassName);
if (trueClass == null) {
return new ArrayList<List<Object>>(0);
}
// note that there will only be one boolean value and strength pair returned
final List<List<Object>> booleanValueAndStrengthPairs = new ArrayList<List<Object>>(1);
for (final List<Object> valueAndStrengthPair : valueAndStrengthPairs) {
getLogger().debug(stackLevel() + " " + valueAndStrengthPair.get(0) + " equals " + trueClass + " ?");
if (valueAndStrengthPair.get(0).equals(trueClass)) {
final List<Object> booleanValueAndStrengthPair = new ArrayList<Object>(2);
booleanValueAndStrengthPair.add(Boolean.valueOf(true));
booleanValueAndStrengthPair.add(valueAndStrengthPair.get(1));
booleanValueAndStrengthPairs.add(booleanValueAndStrengthPair);
return booleanValueAndStrengthPairs;
}
}
final String falseClassName = domainProperty.falseClass();
if (falseClassName.isEmpty()) {
throw new TexaiException("trueClass must be specified for boolean field " + field.getName());
}
final AtomicTerm falseClass = termFinderFacade.findAtomicTermByTermName(falseClassName);
if (falseClass == null) {
return new ArrayList<List<Object>>(0);
}
for (final List<Object> valueAndStrengthPair : valueAndStrengthPairs) {
getLogger().debug(stackLevel() + " " + valueAndStrengthPair.get(0) + " equals " + falseClass + " ?");
if (valueAndStrengthPair.get(0).equals(falseClass)) {
final List<Object> booleanValueAndStrengthPair = new ArrayList<Object>(2);
booleanValueAndStrengthPair.add(Boolean.valueOf(false));
booleanValueAndStrengthPair.add(valueAndStrengthPair.get(1));
booleanValueAndStrengthPairs.add(booleanValueAndStrengthPair);
return booleanValueAndStrengthPairs;
}
}
getLogger().debug(stackLevel() + " boolean field true/false class not found for : " + field.getName() + ", defaulting to false");
final List<Object> booleanValueAndStrengthPair = new ArrayList<Object>(2);
booleanValueAndStrengthPair.add(Boolean.valueOf(false));
booleanValueAndStrengthPair.add(Constants.STRENGTH_MONOTONIC);
booleanValueAndStrengthPairs.add(booleanValueAndStrengthPair);
return booleanValueAndStrengthPairs;
}
return valueAndStrengthPairs;
}
/** Sets the given field to the given value if the value is not null.
*
* @param field the given field to be set
* @param value the field value
* @param fieldType the field type
*/
private void setFieldValue(final Field field, final Object value, final Class fieldType) {
//Preconditions
assert field != null : "field must not be null";
assert fieldType != null : "fieldType must not be null";
if (value == null) {
getLogger().debug(stackLevel() + " value is null");
} else {
getLogger().debug(stackLevel() + " load value (" + value.getClass().getName() + ") into fieldType " + fieldType.getName());
if (!field.isAccessible()) {
field.setAccessible(true);
}
try {
field.set(getDomainEntity(), value);
} catch (final IllegalArgumentException ex) {
throw new TexaiException(
ex.getMessage()
+ "\nfield: " + field
+ "\nvalue: " + value
+ "\nfieldType: " + fieldType, ex);
} catch (final IllegalAccessException ex) {
throw new TexaiException(
ex.getMessage()
+ "\nfield: " + field
+ "\nvalue: " + value
+ "\nfieldType: " + fieldType, ex);
}
}
}
/** Queries for the value terms filling the arg2 position of matching gafs having the
* given predicate, arg1 position filled by the instanceTerm and found in relevant contexts from
* the class load-from context. The value terms are translated into domain objects.
*
* @param predicate the predicate that relates the value for the domain entity field
* @param fieldType the field type
* @param isInverseProperty the indicator that the property is to be inverted with respect to treatment of arg1 and arg2
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
* @return the ordered list by value termId of value and gaf strength pairs
*/
private List<List<Object>> queryForValuesAndStrengths(
final AtomicTerm predicate,
final Class fieldType,
final boolean isInverseProperty,
final boolean isLoadableFromDomainEntityCache) {
//Preconditions
assert predicate != null : "predicate must not be null";
assert fieldType != null : "fieldType must not be null";
List<List<Object>> valueTermAndStrengthTerms = null;
if (isInverseProperty) {
valueTermAndStrengthTerms = associationFinder.gatherArg1TermsAndStrengthsFromBinaryGAFs(
predicate,
getInstanceTerm(),
getClassPersistenceContext());
} else {
valueTermAndStrengthTerms = associationFinder.gatherArg2TermsAndStrengthsFromBinaryGAFs(
predicate,
getInstanceTerm(),
getClassPersistenceContext());
}
final List<List<Object>> valuesAndStrengths = new ArrayList<List<Object>>();
for (List<Object> valueTermAndStrengthTerm : valueTermAndStrengthTerms) {
final List<Object> valueAndStrength = new ArrayList<Object>(2);
final AbstractTerm valueTerm = (AbstractTerm) valueTermAndStrengthTerm.get(0);
valueAndStrength.add(getValueFromTerm(valueTerm, fieldType, isLoadableFromDomainEntityCache));
final Double strength = (Double) valueTermAndStrengthTerm.get(1);
valueAndStrength.add(strength);
valuesAndStrengths.add(valueAndStrength);
}
return valuesAndStrengths;
}
/** Gets the domain value from the given value term. In case of domain entities,
* state is saved and a recursive domain entity load is performed.
*
* @param valueTerm the value term
* @param fieldType the field type
* @param isLoadableFromDomainEntityCache the indicator that domain entities can be loaded from the cache
* @return the domain value from the given value term
*/
private Object getValueFromTerm(
final AbstractTerm valueTerm,
final Class fieldType,
final boolean isLoadableFromDomainEntityCache) { // NOPMD
//Preconditions
assert valueTerm != null : "valueTerm must not be null";
Object value = null;
if (valueTerm instanceof PString) {
value = ((PString) valueTerm).getStringValue();
} else if (valueTerm instanceof PLong) {
final Long longValue = ((PLong) valueTerm).getLongValue();
if (fieldType.equals(Integer.class) || ("int").equals(fieldType.getName())) {
value = Integer.valueOf(longValue.intValue());
} else {
value = longValue;
}
} else if (valueTerm instanceof PDouble) {
final Double doubleValue = ((PDouble) valueTerm).getDoubleValue();
if (fieldType.equals(Float.class) || ("float").equals(fieldType.getName())) {
value = Float.valueOf(doubleValue.floatValue());
} else {
value = doubleValue;
}
} else if (valueTerm instanceof PDate) {
value = ((PDate) valueTerm).getDateValue();
} else if (valueTerm instanceof Formula) {
value = valueTerm;
} else if (valueTerm instanceof NonAtomicTerm) {
value = valueTerm;
} else if (valueTerm instanceof AtomicTerm) {
value = connectedDomainEntityDictionary.get(valueTerm);
if (value == null) {
if (isAtomicTermDomainEntity((AtomicTerm) valueTerm)) {
getLogger().debug(stackLevel() + " domain entity AtomicTerm valueTerm: " + valueTerm);
// save this session state before the recursive method call because session beans cannot
// create a new session bean instance
saveAbstractSessionState();
value = loadDomainEntityInternal((AtomicTerm) valueTerm, isLoadableFromDomainEntityCache);
restoreAbstractSessionState();
} else {
// value term is an atomic term but not a domain entity
getLogger().debug(stackLevel() + " AtomicTerm valueTerm: " + valueTerm);
value = valueTerm;
connectedDomainEntityDictionary.put((AtomicTerm) valueTerm, value);
}
} else {
getLogger().debug(stackLevel() + " previously loaded value: " + value);
}
} else {
throw new TexaiException("unhandled valueTerm " + valueTerm);
}
getLogger().debug(stackLevel() + " value from term " + valueTerm + " = " + value);
return value;
}
/** Returns true if the given atomic term value represents a domain entity.
*
* @param atomicTerm the value term
* @return true if the given value term represents a domain entity
*/
private boolean isAtomicTermDomainEntity(final AtomicTerm atomicTerm) {
//Preconditions
assert atomicTerm != null : "atomicTerm must not be null";
final PString classNameTerm = (PString) associationFinder.getArg2TermFromBinaryGAF(
termFinderFacade.findAtomicTermByTermName(Constants.TERM_NAME_DOMAIN_ENTITY_CLASS_NAME),
atomicTerm,
termFinderFacade.findAtomicTermByTermName(Constants.TERM_NAME_UNIVERSAL_VOCABULARY_MT));
return classNameTerm != null;
}
/** Pushes the current session bean state onto a stack and then intitializes the session state. */
public void saveSessionState() {
//Preconditions
assert domainEntityInfoStack != null : "domainEntityInfoStack must not be null";
getLogger().debug(stackLevel() + "saving session state");
domainEntityInfoStack.push(new DomainEntityInfo(connectedDomainEntityDictionary));
}
/** Restores the session state following a recursive method call. */
public void restoreSessionState() {
//Preconditions
assert domainEntityInfoStack != null : "domainEntityInfoStack must not be null";
final DomainEntityInfo domainEntityInfo = domainEntityInfoStack.pop();
connectedDomainEntityDictionary = domainEntityInfo.connectedDomainEntityDictionary;
getLogger().debug(stackLevel() + "restored session state");
}
/** Contains the session bean state for recursive method calls. */
private class DomainEntityInfo {
/** the dictionary of connected domain entities, term --> domain instance */
private Map<AtomicTerm, Object> connectedDomainEntityDictionary;
/** Creates a new DomainEntityInfo instance.
*
* @param connectedDomainEntityDictionary the dictionary of connected domain entities, term --> domain instance
*/
protected DomainEntityInfo(final Map<AtomicTerm, Object> connectedDomainEntityDictionary) {
//Preconditions
assert connectedDomainEntityDictionary != null : "connectedDomainEntityDictionary must not be null";
this.connectedDomainEntityDictionary = connectedDomainEntityDictionary;
}
}
/** This comparator class orders value and strength pairs by the field specified in an OrderBy annotation. */
public class ValueAndStrengthPairsComparator implements Comparator {
/** the name of the field by which to order the values */
private String orderByFieldName;
/**
* Creates a new instance of ValueAndStrengthPairsComparator.
*/
public ValueAndStrengthPairsComparator(final OrderBy orderBy) {
//Preconditions
assert orderBy != null : "orderBy must not be null";
orderByFieldName = orderBy.value();
getLogger().debug(stackLevel() + " orderByFieldName: " + orderByFieldName);
}
/**
* Compares its two arguments for order. Returns a negative integer,
* zero, or a positive integer as the first argument is less than, equal
* to, or greater than the second.<p>
*
*
* @param o1 the first object to be compared.
* @param o2 the second object to be compared.
* @return a negative integer, zero, or a positive integer as the
* first argument is less than, equal to, or greater than the
* second.
* @throws ClassCastException if the arguments' types prevent them from
* being compared by this comparator.
*/
@SuppressWarnings("unchecked")
public int compare(final Object o1, final Object o2) {
final List<Object> valueAndStrengthPair1 = (List<Object>) o1;
final Object value1 = valueAndStrengthPair1.get(0);
final Class clazz1 = value1.getClass();
final Field compareField1 = findCompareField(value1);
Object compareValue1 = null;
try {
compareField1.setAccessible(true);
compareValue1 = compareField1.get(value1);
} catch (final IllegalArgumentException ex) {
throw new TexaiException(ex);
} catch (final IllegalAccessException ex) {
throw new TexaiException(ex);
}
final List<Object> valueAndStrengthPair2 = (List<Object>) o2;
final Object value2 = valueAndStrengthPair2.get(0);
final Class clazz2 = value2.getClass();
final Field compareField2 = findCompareField(value2);
Object compareValue2 = null;
try {
compareField2.setAccessible(true);
compareValue2 = compareField2.get(value2);
} catch (final IllegalArgumentException ex) {
throw new TexaiException(ex);
} catch (final IllegalAccessException ex) {
throw new TexaiException(ex);
}
getLogger().debug(stackLevel() + " comparing " + o1 + " / " + compareValue1
+ " with " + o2 + " / " + compareValue2);
return ((Comparable) compareValue1).compareTo(((Comparable) compareValue2));
}
/** Finds the orderBy field in the given domain entity.
*
* @param domainEntity the domain entity having an orderBy field
* @return the orderBy field in the given domain entity
*/
private Field findCompareField(final Object domainEntity) {
//Preconditions
assert domainEntity != null : "domainEntity must not be null";
return findCompareField(domainEntity, domainEntity.getClass());
}
/** Finds the orderBy field in the given domain entity.
*
* @param domainEntity the domain entity having an orderBy field
* @param clazz the class or super class of the domain entity
* @return the orderBy field in the given domain entity
*/
private Field findCompareField(final Object domainEntity, final Class clazz) {
//Preconditions
assert domainEntity != null : "domainEntity must not be null";
assert clazz != null : "clazz must not be null";
Field compareField = null;
try {
compareField = clazz.getDeclaredField(orderByFieldName);
} catch (final NoSuchFieldException ex) {
if (clazz.equals(Object.class)) {
throw new TexaiException("OrderBy field [" + orderByFieldName + "] not found in " + domainEntity + " having class " + clazz, ex);
}
}
if (compareField == null) {
return findCompareField(domainEntity, clazz.getSuperclass());
} else {
return compareField;
}
}
/**
*
* Indicates whether some other object is "equal to" this
* comparator.
*
* @param obj the reference object with which to compare.
* @return <code>true</code> only if the specified object is also
* a comparator and it imposes the same ordering as this
* comparator.
*/
public boolean equals(final Object obj) {
if (!(obj instanceof ValueAndStrengthPairsComparator)) {
return false;
}
final ValueAndStrengthPairsComparator that = (ValueAndStrengthPairsComparator) obj;
return this.orderByFieldName.equals(that.orderByFieldName);
}
}
}
See more files for this project here