/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo 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 3 of the License, or * (at your option) any later version. * * OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.xmlcode; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.StringTokenizer; import java.util.Vector; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * <p> * <code>ModelEntity</code> is internally used in {@link org.openflexo.xmlcode.XMLMapping} * <p> * You never need to used it directly, this class maps <code><entity><code> tags in <i>model file</i>. * * @author <a href="mailto:Sylvain.Guerin@enst-bretagne.fr">Sylvain Guerin</a> * @see XMLMapping */ public class ModelEntity { /** Stores name of this entity */ protected String name; /** Stores defined xmlTag(s) of this entity */ protected String[] definedXMLTags = null; /** Stores derived xmlTag(s) of this entity */ protected String[] derivedXMLTags = null; /** Hashtable of ModelProperty objects (key is the name of the property) */ protected Map<String, ModelProperty> modelProperties; /** Hashtable of ModelProperty objects (key is the name of the property) */ protected Vector<ModelProperty> orderedModelProperties; /** * Hashtable of ModelProperty objects (key is the name of the property and value is the ModelEntity related property inherits from) */ protected Map<String, ModelEntity> inheritedModelProperties; /** * Stores a boolean indicating if this entity is abstract (won't never be instancied directly) */ protected boolean isAbstract = false; /** Stores related class */ protected Class<?> relatedClass; /** Stores string indicating decoding finalizer to run, if required */ protected String initializer = null; /** Stores string indicating decoding finalizer to run, if required */ protected String finalizer = null; /** Stores string indicating decoding finalizer to run, if required */ protected String contextsAsString = null; /** Stores string coding attribute coding generic typing, if any */ protected String genericTypingStoredIn = null; /** Property coding generic typing, if any */ protected SingleKeyValueProperty genericTypingKVProperty; /** Stores constructor without parameter, if any */ protected Constructor<?> constructorWithoutParameter = null; /** * Stores constructor with parameter (builder class defined in XMLMapping), if any */ protected Constructor<?> constructorWithParameter = null; /** Stores decoding finalizing method without parameter, if any */ protected Method finalizerWithoutParameter = null; /** * Stores decoding finalizing method with parameter (builder class defined in XMLMapping), if any */ protected Method finalizerWithParameter = null; /** Stores link to XMLMapping */ protected XMLMapping model; /** Description of this entity */ protected String description; private Method initializerWithoutParameter; private Method initializerWithParameter; /** * Creates a new <code>ModelEntity</code> instance<br> * This constructor should be called for dynamically XMLMapping building purposes.<br> * Use {@link XMLMapping#registerNewModelEntity(ModelEntity)} to register this new <code>ModelEntity</code> object in an * <code>XMLMapping</code> instance. Use {@link #registerNewModelProperty(ModelProperty)} to register new <code>ModelPropery</code> * objects. * * @param someXMLTags * comma separated strings coding all the values a XML tag could take * @exception InvalidModelException * if an error occurs */ public ModelEntity(String aName, String someXMLTags, boolean abstractFlag, String aFinalizer, XMLMapping anXMLMapping) throws InvalidModelException { super(); name = aName; finalizer = aFinalizer; parseXMLTags(someXMLTags); isAbstract = abstractFlag; init(anXMLMapping); } /** * Creates a new <code>ModelEntity</code> instance, given a node <code>anEntityNode</code> * * @param anEntityNode * a <code>Node</code> value * @exception InvalidModelException * if an error occurs */ public ModelEntity(Node anEntityNode, XMLMapping anXMLMapping) throws InvalidModelException { super(); Node tempAttribute; NamedNodeMap attributes; boolean nameIsSpecified = false; boolean xmlTagIsSpecified = false; NodeList propertiesNodeList; Node tempNode; isAbstract = false; if (!anEntityNode.getNodeName().equals(XMLMapping.entityLabel)) { throw new InvalidModelException("Invalid tag '" + anEntityNode.getNodeName() + "' found in model file"); } // end of if () attributes = anEntityNode.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { tempAttribute = attributes.item(i); if (tempAttribute.getNodeName().equals(XMLMapping.nameLabel)) { nameIsSpecified = true; name = tempAttribute.getNodeValue(); } else if (tempAttribute.getNodeName().equals(XMLMapping.xmlTagLabel)) { xmlTagIsSpecified = true; parseXMLTags(tempAttribute.getNodeValue()); } else if (tempAttribute.getNodeName().equals(XMLMapping.abstractLabel)) { isAbstract = tempAttribute.getNodeValue().equalsIgnoreCase("yes") || tempAttribute.getNodeValue().equalsIgnoreCase("true"); } else if (tempAttribute.getNodeName().equals(XMLMapping.finalizerLabel)) { finalizer = tempAttribute.getNodeValue(); } else if (tempAttribute.getNodeName().equals(XMLMapping.initializerLabel)) { initializer = tempAttribute.getNodeValue(); } else if (tempAttribute.getNodeName().equals(XMLMapping.contextsLabel)) { contextsAsString = tempAttribute.getNodeValue(); } else if (tempAttribute.getNodeName().equals(XMLMapping.genericTypingStoredIn)) { genericTypingStoredIn = tempAttribute.getNodeValue(); } else { throw new InvalidModelException("Invalid attribute '" + tempAttribute.getNodeName() + "' found in model file for tag 'entity'"); } } init(anXMLMapping); if (genericTypingStoredIn != null) { // System.out.println("genericTypingStoredIn="+genericTypingStoredIn); genericTypingKVProperty = new SingleKeyValueProperty(getRelatedClass(), genericTypingStoredIn, true); } propertiesNodeList = anEntityNode.getChildNodes(); for (int i = 0; i < propertiesNodeList.getLength(); i++) { tempNode = propertiesNodeList.item(i); if (tempNode.getNodeType() == Node.ELEMENT_NODE) { if (tempNode.getNodeName().equals(XMLMapping.descriptionLabel)) { if (tempNode.getChildNodes().getLength() == 1 && tempNode.getFirstChild().getNodeType() == Node.TEXT_NODE) { setDescription(tempNode.getFirstChild().getNodeValue()); } } else if (tempNode.getNodeName().equals(XMLMapping.propertyLabel)) { ModelProperty newModelProperty = new ModelProperty(tempNode, this); if (modelProperties.get(newModelProperty.getName()) != null) { throw new InvalidModelException("Duplicate property " + newModelProperty.getName() + " found in model file"); } else { modelProperties.put(newModelProperty.getName(), newModelProperty); orderedModelProperties.add(newModelProperty); } } else { throw new InvalidModelException("Invalid tag '" + tempNode.getNodeName() + "' found in model file for tag 'entity'"); } } else if (tempNode.getNodeType() == Node.TEXT_NODE) { // Non significative text will be simply ignored if (tempNode.getNodeValue().trim().length() > 0) { throw new InvalidModelException("Invalid text found in model file"); } } else if (tempNode.getNodeType() == Node.ATTRIBUTE_NODE) { // Simply ignore it } else if (tempNode.getNodeType() == Node.COMMENT_NODE) { // Simply ignore it } else { throw new InvalidModelException("Invalid xml tag found as child of 'entity' tag in model file"); } } if (!nameIsSpecified) { throw new InvalidModelException("No attribute 'name' defined for tag 'entity' in model file"); } if (!xmlTagIsSpecified && !isAbstract()) { throw new InvalidModelException("No attribute 'xmlTag' defined for tag 'entity' in model file"); } } /** * Internaly used by constructors * * @param anXMLMapping */ private void init(XMLMapping anXMLMapping) { Class<XMLSerializable> XMLSerialisableClass = org.openflexo.xmlcode.XMLSerializable.class; model = anXMLMapping; try { try { relatedClass = Class.forName(getName()); } catch (ClassNotFoundException e) { throw new InvalidModelException("Class " + getName() + " not found."); } // Looking for constructors try { constructorWithoutParameter = relatedClass.getConstructor((Class<?>[]) null); } catch (NoSuchMethodException e) { // Ignore for now } if (model.hasBuilderClass()) { findConstructorWithParameter(model.builderClass); } if (!model.serializeOnly) { if (!hasConstructorWithoutParameter() && !hasConstructorWithParameter() && !isAbstract()) { if (!model.hasBuilderClass()) { throw new InvalidModelException( "Class " + getName() + " cannot be instanciated directly (check that this class has a constructor with no arguments [public " + getName() + "()] and that this class is not abstract. If this class should never be instanciated directly, declares it as abstract (see abtract xml tag)."); } else { throw new InvalidModelException( "Class " + getName() + " cannot be instanciated directly or with a parameter of " + model.builderClass().getName() + " and this class is not abstract. If this class should never be instanciated directly, declares it as abstract (see abtract xml tag)."); } } } // Looking for decoding initializing methods if (initializer != null) { try { initializerWithoutParameter = relatedClass.getMethod(initializer, (Class<?>[]) null); } catch (NoSuchMethodException e) { // Ignore for now } if (model.hasBuilderClass()) { boolean initializerHasBeenFound = false; Class currentClass = model.builderClass(); Class[] params = new Class[1]; while (!initializerHasBeenFound && currentClass != null) { try { params[0] = currentClass; initializerWithParameter = relatedClass.getMethod(finalizer, params); initializerHasBeenFound = true; } catch (NoSuchMethodException e) { currentClass = currentClass.getSuperclass(); } } } if (!hasInitializerWithoutParameter() && !hasFinalizerWithParameter()) { throw new InvalidModelException("Class " + getName() + " does not implement specified decoding initializing method : " + initializer); } } // Looking for decoding finalizing methods if (finalizer != null) { try { finalizerWithoutParameter = relatedClass.getMethod(finalizer, (Class<?>[]) null); } catch (NoSuchMethodException e) { // Ignore for now } if (model.hasBuilderClass()) { boolean finalizerHasBeenFound = false; Class currentClass = model.builderClass(); Class[] params = new Class[1]; while (!finalizerHasBeenFound && currentClass != null) { try { params[0] = currentClass; finalizerWithParameter = relatedClass.getMethod(finalizer, params); finalizerHasBeenFound = true; } catch (NoSuchMethodException e) { currentClass = currentClass.getSuperclass(); } } } if (!hasFinalizerWithoutParameter() && !hasFinalizerWithParameter()) { throw new InvalidModelException("Class " + getName() + " does not implement specified decoding finalizing method : " + finalizer); } } } catch (InvalidModelException e) { throw e; } catch (Exception e) { throw new InvalidModelException("Unexpected error " + e.getClass().getName() + " occurs during model initialization. Please send a bug report."); } if (!XMLSerialisableClass.isAssignableFrom(relatedClass)) { throw new InvalidModelException("Class " + getName() + " MUST implement XMLSerializable interface"); } modelProperties = new LinkedHashMap<String, ModelProperty>(); orderedModelProperties = new Vector<ModelProperty>(); inheritedModelProperties = new LinkedHashMap<String, ModelEntity>(); locallyDefinedContexts = new Vector<String>(); if (contextsAsString != null) { StringTokenizer st = new StringTokenizer(contextsAsString, ","); while (st.hasMoreTokens()) { locallyDefinedContexts.add(st.nextToken()); } } _availableContexts = new Vector<String>(); _availableContexts.addAll(locallyDefinedContexts); _xmlTagsForContext = new Hashtable<String, String[]>(); } /** * */ private void findConstructorWithParameter(Class<?> builderClass) { try { Class[] builder = { builderClass }; constructorWithParameter = relatedClass.getConstructor(builder); } catch (NoSuchMethodException e) { // Ignore for now } if (constructorWithoutParameter == null && builderClass.getSuperclass() != null) { findConstructorWithParameter(builderClass.getSuperclass()); } } private Vector<String> _availableContexts = null; private Vector<String> locallyDefinedContexts; public Vector getAvailableContexts() { /*if (_availableContexts == null) { _availableContexts = new Vector(); _availableContexts.addAll(locallyDefinedContexts); if (getParentEntity() != null) { Vector inheritedContexts = getParentEntity().getAvailableContexts(); for (Enumeration en=inheritedContexts.elements(); en.hasMoreElements();) { String next = (String)en.nextElement(); if (!_availableContexts.contains(next)) _availableContexts.add(next); } } }*/ return _availableContexts; } private void updateAvailableContexts(ModelEntity parent) { if (parent != null) { Vector inheritedContexts = parent.getAvailableContexts(); for (Enumeration en = inheritedContexts.elements(); en.hasMoreElements();) { String next = (String) en.nextElement(); if (!_availableContexts.contains(next)) { _availableContexts.add(next); } } } } private void parseXMLTags(String someXMLTags) { StringTokenizer st = new StringTokenizer(someXMLTags, ","); Vector<String> temp = new Vector<String>(); while (st.hasMoreElements()) { String anXMLTag = (String) st.nextElement(); temp.add(anXMLTag); } if (temp.size() == 0) { throw new InvalidModelException("No XML tags specified in model file for entity " + getName()); } else { definedXMLTags = new String[temp.size()]; int i = 0; for (Enumeration e = temp.elements(); e.hasMoreElements(); i++) { String next = (String) e.nextElement(); definedXMLTags[i] = next; // Debugging.debug ("Found tag "+next+" for entity "+getName()); } } } /** * Register new <code>ModelProperty</code> object in mapping<br> * This method MUST be use to dynamically handle <code>XMLMapping</code> instances. * * @exception InvalidModelException * if an error occurs during mapping construction (invalid model) */ public void registerNewModelProperty(ModelProperty aModelProperty) { if (modelProperties.get(aModelProperty.getName()) != null) { throw new InvalidModelException("Duplicate property " + aModelProperty.getName() + " found in model file"); } else { modelProperties.put(aModelProperty.getName(), aModelProperty); orderedModelProperties.add(aModelProperty); } } /** * Returns <code>name</code> of this <code>ModelEntity</code> * * @return a <code>String</code> value */ public String getName() { return name; } /** * Returns related class */ public Class<?> getRelatedClass() { return relatedClass; } /** * Returns default <code>xmlTag</code> of this <code>ModelEntity</code> * * @return a <code>String</code> value */ public String getDefaultXmlTag() { if (definedXMLTags != null && definedXMLTags.length > 0) { return definedXMLTags[0]; } else { throw new InvalidModelException("No XML tag defined for entity '" + getName() + "' Is it an abstract entity ?"); } } /** * Returns an array of all <code>xmlTag</code> of this <code>ModelEntity</code> * * @return a <code>String[]</code> value */ public String[] getXmlTags() { if (definedXMLTags == null) { return null; } if (derivedXMLTags == null) { if (getAvailableContexts().size() == 0) { derivedXMLTags = definedXMLTags; } else { derivedXMLTags = new String[definedXMLTags.length * (getAvailableContexts().size() + 1)]; for (int i = 0; i < definedXMLTags.length; i++) { derivedXMLTags[i] = definedXMLTags[i]; } for (int j = 0; j < getAvailableContexts().size(); j++) { for (int i = 0; i < definedXMLTags.length; i++) { derivedXMLTags[(j + 1) * definedXMLTags.length + i] = (String) getAvailableContexts().elementAt(j) + definedXMLTags[i]; } } } } return derivedXMLTags; } private Hashtable<String, String[]> _xmlTagsForContext; /** * Returns an array of all <code>xmlTag</code> of this <code>ModelEntity</code> given a suppliedContext * * @return a <code>String[]</code> value */ public String[] getXmlTags(String context) { if (getAvailableContexts().contains(context) || context == null) { if (definedXMLTags == null) { return null; } if (context == null) { context = ""; } if (_xmlTagsForContext.get(context) == null) { String[] tags = new String[definedXMLTags.length]; for (int i = 0; i < definedXMLTags.length; i++) { tags[i] = context + definedXMLTags[i]; } _xmlTagsForContext.put(context, tags); } return _xmlTagsForContext.get(context); } else { throw new InvalidModelException("Undefined context '" + context + "' for entity '" + getName() + "'"); } } /** * Returns a string representation of all <code>xmlTag</code> of this <code>ModelEntity</code> * * @return a <code>String</code> value */ public String getConcatenedXmlTag() { String returned = ""; if (derivedXMLTags != null) { for (int i = 0; i < derivedXMLTags.length; i++) { if (i > 0) { returned += ","; } returned += derivedXMLTags[i]; } return returned; } else { return "null"; } } /** * Returns a string representation of all <code>context</code> of this <code>ModelEntity</code> * * @return a <code>String</code> value */ public String getConcatenedContexts() { String returned = ""; if (getAvailableContexts() != null) { for (int i = 0; i < getAvailableContexts().size(); i++) { if (i > 0) { returned += ","; } returned += getAvailableContexts().elementAt(i); } return returned; } else { return "null"; } } /** * Returns an ordered enumeration of <code>ModelProperty</code> objects of this <code>ModelEntity</code> * * @return a <code>Vector</code> value */ public Enumeration<ModelProperty> getModelProperties() { return orderedModelProperties.elements(); // return modelProperties.elements(); } public Iterator<ModelProperty> getModelPropertiesIterator() { return orderedModelProperties.iterator(); // return modelProperties.elements(); } /** * Returns <code>ModelProperty</code> object with specified name, null if such an object doesn't exist * * @return a <code>ModelProperty</code> value */ public ModelProperty getModelPropertyWithName(String aPropertyName) { return modelProperties.get(aPropertyName); } /** * Returns first <code>ModelProperty</code> object with XML tag name matching <code>aTagName</code> value, <code>null</code> if such an * object doesn't exist. * * @return a <code>ModelProperty</code> value */ public ModelProperty getModelPropertyWithXMLTag(String aTagName) { for (Enumeration e = getModelProperties(); e.hasMoreElements();) { ModelProperty tempModelProperty = (ModelProperty) e.nextElement(); if (tempModelProperty.handlesXMLTag(aTagName)) { return tempModelProperty; } } return null; } private Hashtable<ModelProperty, Vector<String>> _nonAttributeProperties; public Hashtable<ModelProperty, Vector<String>> getAllNonAttributeProperties() { if (_nonAttributeProperties == null) { _nonAttributeProperties = new Hashtable<ModelProperty, Vector<String>>(); ModelEntity currentEntity = this; while (currentEntity != null) { Enumeration<ModelProperty> en = currentEntity.getModelProperties(); ModelProperty item = null; while (en.hasMoreElements()) { item = en.nextElement(); if (item.getIsAttribute()) { continue; } String[] tags = item.getXmlTags(); Vector<String> v = new Vector<String>(); for (int i = 0; i < tags.length; i++) { v.add(tags[i]); } _nonAttributeProperties.put(item, v); } currentEntity = currentEntity.getParentEntity(); } } return _nonAttributeProperties; } /** * Returns a String representation of this object suitable for debugging purposes * * @return a <code>String</code> value */ @Override public String toString() { String returnedString = " <entity name=" + '"' + getName() + '"' + " xmlTag=" + '"' + getConcatenedXmlTag() + '"' + " contexts=" + '"' + getConcatenedContexts() + '"'; if (isAbstract()) { returnedString += " " + XMLMapping.abstractLabel + "=" + '"' + "yes" + '"'; } if (finalizer != null) { returnedString += " " + XMLMapping.finalizerLabel + "=" + '"' + finalizer + '"'; } returnedString += ">\n"; for (Enumeration e = getModelProperties(); e.hasMoreElements();) { ModelProperty property = (ModelProperty) e.nextElement(); if (!property.isInherited(this)) { returnedString += property.toString(); } } returnedString += " </entity>\n"; return returnedString; } /** * Returns a boolean indicating if this entity is abstract (won't never be instancied directly). An abstract class should be declared in * mapping file with abstract XML tag set to true ('YES' value). */ public boolean isAbstract() { return isAbstract; } /** * Returns a boolean indicating if entity represents a class which inherits from specified ModelEntity's represented class */ public boolean inheritsFrom(ModelEntity aModelEntity) { return aModelEntity.getRelatedClass().isAssignableFrom(getRelatedClass()); } /** * Update this <code>ModelEntity</code> by taking under account specified <code>parentModelEntity</code> */ public void takeParentUnderAccount(ModelEntity parentModelEntity) { if (!inheritsFrom(parentModelEntity)) { throw new InvalidModelException("Incoherent data. Please send a bug report."); } else { // _availableContexts = null; for (Enumeration<ModelProperty> e = parentModelEntity.getModelProperties(); e.hasMoreElements();) { ModelProperty parentModelProperty = e.nextElement(); if (getModelPropertyWithName(parentModelProperty.getName()) != null) { // This entity already has this property defined, // which overrides the parent one > do nothing } else { modelProperties.put(parentModelProperty.getName(), parentModelProperty); orderedModelProperties.add(parentModelProperty); inheritedModelProperties.put(parentModelProperty.getName(), parentModelEntity); // System.out.println ("Setting property // "+parentModelProperty.getName()+" for "+getName()+" as // property inherited from "+parentModelEntity.getName()); } } updateAvailableContexts(parentModelEntity); derivedXMLTags = null; } } public void updateProperties() { for (Enumeration e = getModelProperties(); e.hasMoreElements();) { ModelProperty property = (ModelProperty) e.nextElement(); property.updateHandledXMLTags(); } } /** * Returns constructor without parameter, if any */ public Constructor getConstructorWithoutParameter() { return constructorWithoutParameter; } /** * Returns boolean indicating if related class has a constructor without parameters * * @return */ public boolean hasConstructorWithoutParameter() { return constructorWithoutParameter != null; } /** * Returns constructor with parameter (builder class defined in XMLMapping), if any */ public Constructor getConstructorWithParameter() { return constructorWithParameter; } /** * Returns boolean indicating if related class has a constructor with parameters * * @return */ public boolean hasConstructorWithParameter() { return constructorWithParameter != null; } /** * Returns decoding finalizing method without parameter, if any */ public Method getFinalizerWithoutParameter() { if (finalizerWithoutParameter == null) { if (getParentEntity() != null) { return getParentEntity().getFinalizerWithoutParameter(); } else { return null; } } return finalizerWithoutParameter; } /** * Returns decoding finalizing method with parameter (builder class defined in XMLMapping), if any */ public Method getFinalizerWithParameter() { if (finalizerWithParameter == null) { if (getParentEntity() != null) { return getParentEntity().getFinalizerWithParameter(); } else { return null; } } return finalizerWithParameter; } /** * Returns boolean indicating if this entity has a decoding finalizing method without parameter, if any */ public boolean hasFinalizerWithoutParameter() { if (getParentEntity() != null) { return finalizerWithoutParameter != null || getParentEntity().hasFinalizerWithoutParameter(); } return finalizerWithoutParameter != null; } /** * Returns boolean indicating if this entity has a decoding finalizing method with parameter (builder class defined in XMLMapping), if * any */ public boolean hasFinalizerWithParameter() { if (getParentEntity() != null) { return finalizerWithParameter != null || getParentEntity().hasFinalizerWithParameter(); } return finalizerWithParameter != null; } /** * Returns decoding finalizing method without parameter, if any */ public Method getInitializerWithoutParameter() { if (initializerWithoutParameter == null) { if (getParentEntity() != null) { return getParentEntity().getInitializerWithoutParameter(); } else { return null; } } return initializerWithoutParameter; } /** * Returns decoding finalizing method with parameter (builder class defined in XMLMapping), if any */ public Method getInitializerWithParameter() { if (initializerWithParameter == null) { if (getParentEntity() != null) { return getParentEntity().getInitializerWithParameter(); } else { return null; } } return initializerWithParameter; } /** * Returns boolean indicating if this entity has a decoding finalizing method without parameter, if any */ public boolean hasInitializerWithoutParameter() { if (getParentEntity() != null) { return initializerWithoutParameter != null || getParentEntity().hasInitializerWithoutParameter(); } return initializerWithoutParameter != null; } /** * Returns boolean indicating if this entity has a decoding finalizing method with parameter (builder class defined in XMLMapping), if * any */ public boolean hasInitializerWithParameter() { if (getParentEntity() != null) { return initializer != null || getParentEntity().hasInitializerWithParameter(); } return initializerWithParameter != null; } /** * Return related model as XMLMapping */ public XMLMapping getModel() { return model; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } /** * Return the most specialized parent entity registered in related model * * @return */ public ModelEntity getParentEntity() { Class current = getRelatedClass().getSuperclass(); boolean parentIsFound = false; while (current != null && !parentIsFound) { if (model.entityWithClassName(current.getName()) != null) { ModelEntity parentEntity = model.entityWithClassName(current.getName()); return parentEntity; } current = current.getSuperclass(); } return null; } public Vector<ModelEntity> getAllChildrenEntities() { Vector<ModelEntity> children = new Vector<ModelEntity>(); for (ModelEntity e : getModel().modelEntitiesStoredByClassName.values()) { if (e.getParentEntity() == this) { children.add(e); } } return children; } /** * Build and returns an Hashtable composed of all ancestors registered in related model, associated with an Integer object representing * ancestor level. * * @return Hashtable of ModelEntity keys associated to Integer values */ public Hashtable<ModelEntity, Integer> getAncestors() { Hashtable<ModelEntity, Integer> returned = new Hashtable<ModelEntity, Integer>(); returned.put(this, new Integer(0)); buildAncestors(0, returned); // System.out.println ("Ancestors for "+getName()); /* * for (Enumeration e = returned.keys(); e.hasMoreElements();) { * ModelEntity temp = (ModelEntity)e.nextElement(); System.out.println * ("Found "+temp.getName()+" with level * "+((Integer)returned.get(temp)).intValue()); } */ return returned; } /** * Internally used to build ancestors hashtable * * @param level * @param ancestors * @param anEntity */ private void buildAncestors(int level, Hashtable<ModelEntity, Integer> ancestors) { ModelEntity nextParentEntity = null; Class current = getRelatedClass().getSuperclass(); int nextLevel = level; boolean parentIsFound = false; while (current != null && !parentIsFound) { if (model.entityWithClassName(current.getName()) != null) { nextParentEntity = model.entityWithClassName(current.getName()); parentIsFound = true; } else { current = current.getSuperclass(); nextLevel++; } } if (nextParentEntity != null) { ancestors.put(nextParentEntity, new Integer(nextLevel + 1)); nextParentEntity.buildAncestors(nextLevel + 1, ancestors); } for (int i = 0; i < getRelatedClass().getInterfaces().length; i++) { Class nextInterface = getRelatedClass().getInterfaces()[i]; ModelEntity parentInterfaceEntity = model.entityWithClassName(nextInterface.getName()); if (parentInterfaceEntity != null) { ancestors.put(parentInterfaceEntity, new Integer(level + 1)); parentInterfaceEntity.buildAncestors(level + 1, ancestors); } } } public boolean implementsGenericTypingKVProperty() { if (genericTypingKVProperty != null) { return true; } if (getParentEntity() != null) { return getParentEntity().implementsGenericTypingKVProperty(); } else { return false; } } public SingleKeyValueProperty getGenericTypingKVProperty() { if (genericTypingKVProperty != null) { return genericTypingKVProperty; } if (getParentEntity() != null) { return getParentEntity().getGenericTypingKVProperty(); } else { return null; } } }