org.plasma.sdo.access.model.Entity Maven / Gradle / Ivy
/**
* PlasmaSDO™ License
*
* This is a community release of PlasmaSDO™, a dual-license
* Service Data Object (SDO) 2.1 implementation.
* This particular copy of the software is released under the
* version 2 of the GNU General Public License. PlasmaSDO™ was developed by
* TerraMeta Software, Inc.
*
* Copyright (c) 2013, TerraMeta Software, Inc. All rights reserved.
*
* General License information can be found below.
*
* This distribution may include materials developed by third
* parties. For license and attribution notices for these
* materials, please refer to the documentation that accompanies
* this distribution (see the "Licenses for Third-Party Components"
* appendix) or view the online documentation at
* .
*
*/
package org.plasma.sdo.access.model;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.plasma.sdo.PlasmaDataObjectConstants;
import org.plasma.sdo.PlasmaProperty;
import org.plasma.sdo.access.DataAccessException;
import org.plasma.sdo.access.provider.common.TraversalMap;
import org.plasma.sdo.helper.PlasmaTypeHelper;
import commonj.sdo.Property;
import commonj.sdo.Type;
/**
*/
public abstract class Entity implements DataEntity
{
private static Log log = LogFactory.getFactory().getInstance(
Entity.class);
private static Object lock = new Object();
private static Map[]> methodMap = new HashMap[]>();
private static Object[] emptyArgs = new Object[] {};
private static int GET_METHODS = 0;
private static int SET_METHODS = 1;
private static int ADD_METHODS = 2;
private String hashKey;
private Type type;
private String namespaceURI;
public Entity()
{
synchronized (lock)
{
// check static method-map-array hash for class-specific info
Map[] maps = methodMap.get(this.getClass().getName());
if (maps == null)
methodMap.put(this.getClass().getName(), createMethodMaps());
}
}
/**
* Finds and maps an, albeit incomplete, set of specific methods
* to/for 'this' class-name for use in generic property-oriented
* get() and set() methods below.
*/
@SuppressWarnings("unchecked")
private Map[] createMethodMaps()
{
Map[] maps = new Map[3];
maps[GET_METHODS] = new HashMap();
maps[SET_METHODS] = new HashMap();
maps[ADD_METHODS] = new HashMap();
Method[] methods = this.getClass().getMethods();
for (int i = 0; i < methods.length; i++)
{
if (methods[i].getName().startsWith("get"))
{
Class[] argTypes = methods[i].getParameterTypes();
if (argTypes.length == 0) // zero arg getters
{
String key =
String.valueOf(Character.toLowerCase(methods[i].getName().charAt(3)))
+ methods[i].getName().substring(4);
maps[GET_METHODS].put(key, methods[i]);
key = methods[i].getName().substring(3).toLowerCase();
maps[GET_METHODS].put(key, methods[i]);
}
// more getters??
}
else if (methods[i].getName().startsWith("set"))
{
Class[] argTypes = methods[i].getParameterTypes();
if (argTypes.length == 1) // single arg setters
{
String key =
String.valueOf(Character.toLowerCase(methods[i].getName().charAt(3)))
+ methods[i].getName().substring(4);
maps[SET_METHODS].put(key, methods[i]);
key = methods[i].getName().substring(3).toLowerCase();
maps[SET_METHODS].put(key, methods[i]);
}
// more setters??
}
else if (methods[i].getName().startsWith("add"))
{
Class[] argTypes = methods[i].getParameterTypes();
if (argTypes.length == 1) // single arg adders
{
String key =
String.valueOf(Character.toLowerCase(methods[i].getName().charAt(3)))
+ methods[i].getName().substring(4);
maps[ADD_METHODS].put(key, methods[i]);
key = methods[i].getName().substring(3).toLowerCase();
maps[ADD_METHODS].put(key, methods[i]);
}
// more adders??
}
}
return maps;
}
/**
* Generic reflection-based 'getter'. Finds the
* specific getter method based on the given property
* name and 'this' class and invokes it.
* @param - the property name
* @return - the value
*/
public Object get(String name)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Map[] maps = methodMap.get(this.getClass().getName());
Method method = (Method)maps[GET_METHODS].get(name);
if (method != null) {
return method.invoke(this, emptyArgs);
}
else {
String lowerName = name.toLowerCase();
method = (Method)maps[GET_METHODS].get(lowerName);
if (method != null)
return method.invoke(this, emptyArgs);
else
throw new EntityException("no "+this.getClass().getName()
+ " 'get' method found for property '" + name + "'");
}
}
/**
* Generic reflection-based 'setter'. Finds the
* specific setter method based on the given property
* name and 'this' class and invokes it using the
* given value.
* @param - the property name
* @param - the value to set
* @return - the old value
*/
public Object set(String name, Object o)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Object old = get(name);
Map[] maps = methodMap.get(this.getClass().getName());
Method method = (Method)maps[SET_METHODS].get(name);
if (method != null)
{
Object[] args = new Object[] {o};
method.invoke(this, args);
}
else {
String lowerName = name.toLowerCase();
method = (Method)maps[SET_METHODS].get(lowerName);
if (method != null) {
Object[] args = new Object[] {o};
method.invoke(this, args);
}
else
throw new EntityException("no "+this.getClass().getName()
+ " 'set' method found for property '" + name + "'");
}
return old;
}
/**
* Generic reflection-based 'adder'. Finds the
* specific adder method based on the given property
* name and 'this' class and invokes it using the
* given value.
* @param - the property name
* @param - the value to add
*/
public void add(String name, Object o)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Map[] maps = methodMap.get(this.getClass().getName());
Method method = (Method)maps[ADD_METHODS].get(name);
if (method != null)
{
Object[] args = new Object[] {o};
method.invoke(this, args);
}
else {
String lowerName = name.toLowerCase();
method = (Method)maps[ADD_METHODS].get(lowerName);
if (method != null) {
Object[] args = new Object[] {o};
method.invoke(this, args);
}
else
throw new EntityException("no "+this.getClass().getName()
+ " 'add' method found for property '" + name + "'");
}
}
/**
* Returns the value for the id property, often
* the property associated with the DB primary key.
*/
public Object getEntityId()
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
return get(getIdProperty().getName());
}
/**
* Sets the value for the id property, often
* the property associated with the DB primary key.
* @param - the id value
*/
public Object setEntityId(Object id)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
return set(getIdProperty().getName(), id);
}
/**
* This recursive traversal algorithm is based on the Visitor Pattern and is capable of
* both breadth-first and depth-first traversals. The entry point is found in the
* Entity.accept(IPOMVisitor visitor) method. It assumes a strict hierarchy of POM
* objects linked through reference properties. Within contexts where other than strict
* hierarchical structures are possible, the associated graph traversal algorithm should be
* used instead Though this algorithm will traverse any structure including a
* hierarchy, graph or network without errors, the traversal path could be undesirable
* except for strict hierarchies.
* @param - the visitor
*/
public void accept(EntityVisitor visitor)
{
try {
accept(visitor, this, null, null, new HashMap(), false);
} catch (IllegalAccessException e) {
throw new EntityException(e);
} catch (IllegalArgumentException e) {
throw new EntityException(e);
} catch (InvocationTargetException e) {
throw new EntityException(e);
}
}
/**
* Variant of above.
*/
public void accept(EntityVisitor visitor, boolean depthFirst)
{
try {
accept(visitor, this, null, null, new HashMap(), depthFirst);
} catch (IllegalAccessException e) {
throw new EntityException(e);
} catch (IllegalArgumentException e) {
throw new EntityException(e);
} catch (InvocationTargetException e) {
throw new EntityException(e);
}
}
/**
* This recursive traversal algorithm is based on the Visitor Pattern and
* is capable of only breadth-first traversals. It assumes a complex POM graph linked through
* reference properties. Within contexts where a strict hierarchical structure can be
* guaranteed, the associated hierarchy traversal algorithm should be used instead. This algorithm
* relies on a "traversal map" which is a structure which maps property name
* path strings, in the format '/shipment/orders', to property name string arrays.
* @param - the graph visitor
* @param - the traversal map
*/
public void accept(EntityGraphVisitor visitor, TraversalMap traversalMap)
{
try {
accept(visitor, this, traversalMap);
} catch (IllegalAccessException e) {
throw new EntityException(e);
} catch (InvocationTargetException e) {
throw new EntityException(e);
}
}
private boolean accept(EntityVisitor visitor, Entity entity,
Entity source, String sourceKey,
Map visited, boolean depthFirst)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Entity[] entitys = null;
visited.put(entity, entity);
if (!depthFirst)
if (!visitor.visit(entity, source, sourceKey))
return false; // abort
List properties = this.getType().getDeclaredProperties();
for (Property property : properties)
{
if (property.getName().equals(this.getIdProperty().getName()))
continue;
if (property.getType().isDataType())
continue;
entitys = getNodes(entity, property);
for (int j = 0; j < entitys.length; j++)
{
if (visited.get(entitys[j]) == null)
if (!accept(visitor, entitys[j], entity, property.getName(), visited, depthFirst))
return false; // abort
}
}
if (depthFirst)
if (!visitor.visit(entity, source, sourceKey))
return false; // abort
return true;
}
private void accept(EntityGraphVisitor visitor, Entity entity,
TraversalMap traversalMap)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Entity target = entity;
Iterator iter = traversalMap.iterator();
while (iter.hasNext())
{
String pathStr = (String)iter.next();
if (!pathStr.startsWith(TraversalMap.DELIM_PATH))
continue; // not path-oriented format
//log.info(pathStr);
String[] path = pathStr.substring(1).split(TraversalMap.DELIM_PATH);
if (path.length == 1 && path[0].length() == 0)
path = null; // it's a root
accept(visitor, entity, null, null, path, -1, pathStr, TraversalMap.DELIM_PATH);
}
}
private void accept(EntityGraphVisitor visitor, Entity entity,
Entity source, String sourceKey, String[] path, int item, String fullPath, String currPath)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Entity[] entities = null;
item += 1;
if (path != null && item < path.length)
{
//log.info("path: " + fullPath + " item: " + path[item] + " curr: " + String.valueOf(currPath));
visitor.visit(entity, source, sourceKey, null); // no path-key yet - haven't reached end
currPath += path[item] + TraversalMap.DELIM_PATH;
Property property = entity.getType().getProperty(path[item]);
entities = getNodes(entity, property);
for (int i = 0; i < entities.length; i++)
accept(visitor, entities[i], entity, property.getName(), path, item, fullPath, currPath);
}
else
{
//log.info("path: " + fullPath + " curr: " + String.valueOf(currPath));
visitor.visit(entity, source, sourceKey, currPath); // last or only one
}
}
private Entity[] getNodes(Entity entity, Property property)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Entity[] entitys = null;
if (property.getType().isDataType())
throw new EntityException("Expected reference property");
if (property.isMany())
{
entitys = (Entity[])entity.get(property.getName());
}
else
{
Entity temp = (Entity)entity.get(property.getName());
if (temp != null)
{
entitys = new Entity[1];
entitys[0] = temp;
}
else
entitys = new Entity[0];
}
return entitys;
}
public String getNamespaceURI() {
if (this.namespaceURI == null) {
try {
Field uriField = this.getClass().getDeclaredField(
PlasmaDataObjectConstants.NAMESPACE_URI_FIELD_NAME);
this.namespaceURI = (String)uriField.get(null);
} catch (SecurityException e) {
throw new EntityException(e);
} catch (NoSuchFieldException e) {
throw new EntityException(e);
} catch (IllegalArgumentException e) {
throw new EntityException(e);
} catch (IllegalAccessException e) {
throw new EntityException(e);
}
}
return this.namespaceURI;
}
public Type getType()
{
if (this.type == null)
{
String className = this.getClass().getSimpleName();
this.type = PlasmaTypeHelper.INSTANCE.getType(this.getNamespaceURI(), className);
}
return this.type;
}
public Property getIdProperty()
{
List pkList = (List)this.getType().get(PlasmaProperty.INSTANCE_PROPERTY_OBJECT_PRIKEY_PROPERTIES);
if (pkList == null || pkList.size() == 0)
throw new DataAccessException("no pri-key properties found for type '"
+ this.getType().getName() + "'");
if (pkList.size() > 1)
throw new DataAccessException("multiple pri-key properties found for type '"
+ this.getType().getName() + "' - not yet supported");
return pkList.get(0);
}
public String getHashKey()
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
{
if (this.hashKey == null) {
StringBuilder buf = new StringBuilder();
buf.append(this.getType().getName());
List pkPropertyList = (List)this.getType().get(PlasmaProperty.INSTANCE_PROPERTY_OBJECT_PRIKEY_PROPERTIES);
if (pkPropertyList == null)
throw new DataAccessException("found no pri-key properties found for type '"
+ this.getType().getName() + "'");
for (Property property : pkPropertyList)
{
Object id = this.get(property.getName());
if (id != null) {
buf.append(id.toString());
}
else {
// May not always want pri-keys in results sets, say when
// using aggregate functions.
this.hashKey = java.util.UUID.randomUUID().toString();
if (log.isDebugEnabled())
log.debug("null pri-key value found for '"
+ this.getType().getName() + "." + property.getName()
+ "' when constructing hash-key - using UUID");
break;
}
}
this.hashKey = buf.toString();
}
return hashKey;
}
public String toString() {
try {
return String.valueOf(getHashKey());
} catch (IllegalArgumentException e) {
throw new EntityException(e);
} catch (IllegalAccessException e) {
throw new EntityException(e);
} catch (InvocationTargetException e) {
throw new EntityException(e);
}
}
/*
public int hashCode() {
return hash;
}
private void resetHash()
{
try {
String s = this.getClass().getName() + getId().toString();
hash = Hash.intHash(s.getBytes());
}
catch (IllegalAccessException e) {
throw new POMException(e);
}
catch (IllegalArgumentException e) {
throw new POMException(e);
}
catch (InvocationTargetException e) {
throw new POMException(e);
}
}
*/
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy