diff options
Diffstat (limited to 'core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo')
94 files changed, 19100 insertions, 0 deletions
diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/Constants.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/Constants.java new file mode 100755 index 000000000..1a35d75dd --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/Constants.java @@ -0,0 +1,94 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: Constants.java,v 1.13 2009/11/10 10:06:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.xml.type.XMLTypePackage; + +/** + * AnnotationUtil used when reading a property file is also used by resources. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.13 $ + */ + +public class Constants { + /** StructuralFeatures for TEXT content (part of a mixed complex type) */ + public static final EStructuralFeature TEXT = XMLTypePackage.eINSTANCE.getXMLTypeDocumentRoot_Text(); + + /** StructuralFeatures for CDATA content (part of a mixed complex type) */ + public static final EStructuralFeature CDATA = XMLTypePackage.eINSTANCE.getXMLTypeDocumentRoot_CDATA(); + + /** StructuralFeatures for COMMENT content (part of a mixed complex type) */ + public static final EStructuralFeature COMMENT = XMLTypePackage.eINSTANCE.getXMLTypeDocumentRoot_Comment(); + + /** + * The name under which this connection (PersistenceManagerFactory) is registered + */ + public static final String PROP_NAME = "name"; + + /** The extension used to find the default editor */ + public static final String PROP_EDITOR_EXTENSTION = "editorextension"; + + /** The id used to find the default editor */ + public static final String PROP_EDITOR_ID = "editorid"; + + /** + * The NS URI of the epackage handled by this database (can be more than one, then should be a comma delimited list) + */ + public static final String PROP_EPACKAGE_NSURI = "nsuri"; + + /** The epackage property */ + public static final String PROP_EPACKAGE = "epackage"; + + /** The database name property */ + public static final String PROP_DB_NAME = "dbname"; + + /** The database user property */ + public static final String PROP_DB_USER = "dbuser"; + + /** The database password property */ + public static final String PROP_DB_PWD = "dbpassword"; + + /** The database driver property */ + public static final String PROP_DB_DRIVER = "dbdriver"; + + /** The database url property */ + public static final String PROP_DB_URL = "dburl"; + + /** The database dialect property */ + public static final String PROP_DB_DIALECT = "dbdialect"; + + /** Denotes the start range from which Elver notifications are derived */ + public static final int NOTIFICATION_START_EVENT_TYPE_COUNT = Notification.EVENT_TYPE_COUNT + 1000; + + /** Nofitication Used by elver to notify an elist load */ + public static final int ELIST_LOAD_NOTIFICATION = NOTIFICATION_START_EVENT_TYPE_COUNT + 1; + + public static final String EAV_EOBJECT_ENTITY_NAME = "EAV_EObject"; + + public static final String EAV_EOBJECT_VALUES = "values"; + + public final static String COLUMN_ECONTAINER_CLASS = "econtainer_class"; + public final static String COLUMN_ECONTAINER = "e_container"; + public final static String COLUMN_ECONTAINER_FEATURE_NAME = "e_container_feature_name"; + + public final static String ANNOTATION_SOURCE_TENEO_JPA = "teneo.jpa"; + public final static String ANNOTATION_SOURCE_TENEO_MAPPING = "teneo.mapping"; +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/DataStore.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/DataStore.java new file mode 100755 index 000000000..38026098d --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/DataStore.java @@ -0,0 +1,49 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: DataStore.java,v 1.4 2010/11/11 10:28:03 mtaal Exp $ + */ + +package org.eclipse.emf.teneo; + +import java.util.Properties; + +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.teneo.extension.ExtensionManager; + +/** + * Generic datastore interface used by hibernate and jpox. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ + +public interface DataStore { + + /** @return the mapped epackages */ + EPackage[] getEPackages(); + + /** + * Note: renamed from getProperties in a previous release. + * + * @return the properties, the combination of jpox and hibernate properties + */ + Properties getDataStoreProperties(); + + /** Return the extension manager */ + ExtensionManager getExtensionManager(); + + /** Return the name */ + String getName(); +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/EContainerRepairControl.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/EContainerRepairControl.java new file mode 100755 index 000000000..4304fe844 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/EContainerRepairControl.java @@ -0,0 +1,379 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: EContainerRepairControl.java,v 1.12 2010/02/04 11:03:01 mtaal Exp $ + */ + +package org.eclipse.emf.teneo; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.teneo.mapping.elist.PersistableDelegateList; +import org.eclipse.emf.teneo.mapping.elist.PersistableEList; +import org.eclipse.emf.teneo.mapping.elist.PersistableFeatureMap; + +/** + * Supports the repair of the eContainer and resource setting of child objects when an object is + * loaded from the backing store. + * + * Repair of the eContainer is required in two distinct cases: 1) 1:1 relation: in this case the + * repair is implemented in the caching mechanism. This was the correct location because in jpox an + * object is added to the level 1 cache just after it is retrieved from the db and before it is + * passed on to the requesting application. 2) 1:n relation: in this case the EListWrapper knows + * that a containment relation is being loaded and calls the equivalent methods here. + * + * Note that both cases need to take into account two-way relatiofns. For two-way relations the + * featureid of the opposing ereferencing is used. For one-way relations emf apparently works with + * negative featureid's. + * + * This class also supports caching so that the system can quickly determine if for a certain class + * eContainers need to be set in child objects. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.12 $ + */ + +public class EContainerRepairControl { + /** The logger */ + private static Log log = LogFactory.getLog(EContainerRepairControl.class); + + /** Hashmap of classes for which no repair is required */ + private static final Hashtable<Class<?>, Class<?>> norepairRequired = new Hashtable<Class<?>, Class<?>>(); + + /** Hashmap of repair controls for a certain class */ + private static final Hashtable<Class<?>, List<RepairControl>> repair = + new Hashtable<Class<?>, List<RepairControl>>(); + + /** + * Recursively sets the resource of the object and all its referenced objects, only if the + * object has a resource which is not set and does not have a container. + */ + public static void setEResourceToAlLContent(InternalEObject start, Resource res) { + for (EStructuralFeature estruct : start.eClass().getEAllStructuralFeatures()) { + if (estruct instanceof EReference) { + final EReference eref = (EReference) estruct; + if (eref.isMany()) { + final EList<?> list = (EList<?>) start.eGet(eref); + if (list == null) { + continue; + } + if ((list instanceof PersistableEList<?>) && !((PersistableEList<?>) list).isLoaded()) { + continue; + } + if ((list instanceof PersistableFeatureMap) && !((PersistableFeatureMap) list).isLoaded()) { + continue; + } + for (int i = 0; i < list.size(); i++) { + final InternalEObject child = (InternalEObject) list.get(i); + if (child.eResource() == null) // no container + { + setResource(child, new ArrayList<EObject>(), (Resource.Internal) res); + } + } + } else { + final InternalEObject child = (InternalEObject) start.eGet(eref); + if (child != null && child.eResource() == null) { + setResource(child, new ArrayList<EObject>(), (Resource.Internal) res); + } + } + } + } + } + + /** Sets the resource on an object or if it has a container on its container */ + private static void setResource(InternalEObject eobj, ArrayList<EObject> objs, Resource.Internal res) { + // been here go away + if (objs.contains(eobj)) { + return; + } + + // set the resource here or at the container + if (eobj.eResource() == null) { + if (eobj.eContainer() == null) { + eobj.eSetResource(res, null); + } else { + objs.add(eobj); + setResource((InternalEObject) eobj.eContainer(), objs, res); + } + } + } + + /** Method to repair the eContainer of the child object of this object */ + public static void repair(Object owner) { + if (log.isDebugEnabled()) { + log.debug("Repairing container relations of children of: " + owner.getClass().getName()); + } + + if (!(owner instanceof InternalEObject)) { + return; + } + + if (norepairRequired.get(owner.getClass()) != null) { + return; + } + + List<RepairControl> repairList = repair.get(owner.getClass()); + + if (repairList == null) { + repairList = buildRepairList((InternalEObject) owner); + } + + if (log.isDebugEnabled() && repairList.size() > 0) { + log.debug("Repairing container relations of children of: " + owner.getClass().getName()); + } + + for (int i = 0; i < repairList.size(); i++) { + RepairControl repairControl = repairList.get(i); + if (log.isDebugEnabled()) { + log.debug("Repairing reference " + repairControl.container.getName() + " to child " + + repairControl.childClass.getName()); + } + + repairControl.repair((InternalEObject) owner); + } + } + + /** + * Convenience method to just set the container directly for an object, this method does not + * cascade down. The featureid is the id of the feature of the owner which contains the child. + * The feature id is corrected in the method. + */ + public static void setContainer(InternalEObject owner, InternalEObject child, EStructuralFeature estruct) { + if (child.eContainer() == owner) { + return; + } + + final int featureID; + if (estruct instanceof EReference && ((EReference) estruct).getEOpposite() != null) { + featureID = child.eClass().getFeatureID(((EReference) estruct).getEOpposite()); + } else { + + featureID = InternalEObject.EOPPOSITE_FEATURE_BASE - owner.eClass().getFeatureID(estruct); + } + child.eBasicSetContainer(owner, featureID, null); + } + + /** + * Method to repair the eContainer of the child object of this object. Note the featureid is + * internally translated to an econtainer id, nl. subtract from EOPPOSITE_FEATURE_BASE + */ + public static void repair(Object owner, Object child, EStructuralFeature estruct) { + + if (!(owner instanceof InternalEObject)) { + return; + } + if (!(child instanceof InternalEObject)) { + return; + } + + final int correctedFeatureID; + if (estruct instanceof EReference && ((EReference) estruct).getEOpposite() != null) { + correctedFeatureID = ((InternalEObject) child).eClass().getFeatureID(((EReference) estruct).getEOpposite()); + } else { + correctedFeatureID = + InternalEObject.EOPPOSITE_FEATURE_BASE - ((InternalEObject) owner).eClass().getFeatureID(estruct); + } + + if (norepairRequired.get(owner.getClass()) != null) { + return; + } + + List<RepairControl> repairList = repair.get(owner.getClass()); + + if (repairList == null) { + repairList = buildRepairList((InternalEObject) owner); + } + + if (log.isDebugEnabled() && repairList.size() > 0) { + log.debug("Repairing container relations of children of: " + owner.getClass().getName()); + } + + for (int i = 0; i < repairList.size(); i++) { + RepairControl repairControl = repairList.get(i); + if (repairControl.getFeatureID() == correctedFeatureID && + ((Class<?>) repairControl.childClass).isAssignableFrom(child.getClass())) { + repairControl.repair((InternalEObject) owner, (InternalEObject) child); + return; + } + } + } + + /** Builds a repair control list for an object */ + private static List<RepairControl> buildRepairList(InternalEObject owner) { + final ArrayList<RepairControl> result = new ArrayList<RepairControl>(); + for (EStructuralFeature estruct : owner.eClass().getEAllStructuralFeatures()) { + if (estruct instanceof EReference) { + final EReference eref = (EReference) estruct; + if (eref.isContainment()) { + // now check if we are two or not + if (eref.getEOpposite() != null) { + result.add(new TwoWayContainer(eref, eref.getEOpposite())); + } else { + result.add(new OneWayContainer(eref)); + } + } + } + } + + if (result.size() == 0) { + norepairRequired.put(owner.getClass(), owner.getClass()); + } else { + repair.put(owner.getClass(), result); + } + return result; + } + + /** Abstract class for repairing containers */ + private static abstract class RepairControl { + /** The ereference of the owner which contains the childs */ + private final EReference container; + + /** Some shortcuts for spead */ + private final Class<?> childClass; + + /** Featureid set as container */ + private final int featureID; + + /** Constructor */ + RepairControl(EReference containerReference, int myFeatureID) { + container = containerReference; + childClass = container.getEType().getInstanceClass(); + featureID = myFeatureID; + } + + /** Returns the feature id of this containment relation */ + public int getFeatureID() { + return featureID; + } + + /** Repairs all containers of the owner */ + void repair(InternalEObject owner) { + // The container repair of a list is done through the repair(owner, + // child) method, + // directly in the elist.doLoadFromStore method + if (container.isMany()) { + return; + } + + final Object containedObject = owner.eGet(container); + if (containedObject == null) // not set + { + return; + } + + // no list should be caught in the first line + assert (!(containedObject instanceof PersistableDelegateList<?>)); + + /* + * if (containedObject instanceof JPOXEList) { if + * (((JPOXEList)containedObject).getOwner() == owner) return; + * + * throw new StoreJPOXEmfException("Owner of containerobject is different from passed + * owner, " + "this should have been solved in the + * elist" + containedObject.getClass() + "/" + owner.getClass().getName() + "/" + + * container.getName()); } + */ + + if (!(containedObject instanceof InternalEObject)) { + return; + } + + final InternalEObject containedEObject = (InternalEObject) containedObject; + if (containedEObject.eContainer() == owner) { + return; // already set? + } + + if (log.isDebugEnabled()) { + log.debug("Set container of child " + containedObject.getClass().getName() + " containerfield " + + container.getName()); + } + + // and set it + containedEObject.eBasicSetContainer(owner, featureID, null); + + // also repair the resource if applicable! + /* + * if (containedObject instanceof InternalEObject) { final InternalEObject eobj = + * (InternalEObject)containedObject; if (eobj.eResource() != owner.eResource()) { + * log.debug("Set resource of eobj " + eobj.getClass().getName() + " to resource " + + * owner.eResource().getURI()); eobj.eSetResource((Resource.Internal)owner.eResource(), + * null); } } + */ + + // and also do its children + EContainerRepairControl.repair(containedEObject); + } + + /** Repairs all specific relation */ + void repair(InternalEObject owner, InternalEObject child) { + if (!childClass.isAssignableFrom(child.getClass())) { + return; // not handled by this container + } + + if (child.eContainer() == owner) { + return; // already set? + } + + if (log.isDebugEnabled()) { + log.debug("Set container of child " + child.getClass().getName() + " containerfield " + + container.getName()); + } + + // and set it + child.eBasicSetContainer(owner, featureID, null); + + // also repair the resource if applicable! + /* + * if (child instanceof InternalEObject) { final InternalEObject eobj = + * (InternalEObject)child; Object ores = owner.eResource(); Object eres = + * eobj.eResource(); if (eobj.eResource() != owner.eResource()) { log.debug("Set + * resource of eobj " + eobj.getClass().getName() + " to resource " + + * owner.eResource().getURI()); eobj.eSetResource((Resource.Internal)owner.eResource(), + * null); } } + */ + + EContainerRepairControl.repair(child); + } + } + + /** + * Class handles a oneway container relation, in this case the eBasicSetContainer is used. + */ + private static class OneWayContainer extends RepairControl { + /** Constructor */ + OneWayContainer(EReference containerReference) { + super(containerReference, InternalEObject.EOPPOSITE_FEATURE_BASE - containerReference.getFeatureID()); + } + } + + /** + * Class handles a twoway container relation, in this case the eBasicSetContainer is used. + */ + private static class TwoWayContainer extends RepairControl { + /** Constructor */ + TwoWayContainer(EReference containerReference, EReference toContainer) { + super(containerReference, toContainer.getFeatureID()); + } + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/ERuntime.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/ERuntime.java new file mode 100755 index 000000000..8b4349a01 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/ERuntime.java @@ -0,0 +1,425 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ERuntime.java,v 1.20 2010/02/04 11:03:02 mtaal Exp $ + */ + +package org.eclipse.emf.teneo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.impl.DynamicEObjectImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.teneo.classloader.ClassLoaderResolver; +import org.eclipse.emf.teneo.classloader.StoreClassLoadException; +import org.eclipse.emf.teneo.ecore.EModelResolver; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * The ERuntime contains references to EPackages which are persistable, i.e. are persisted. + * + * It is used to compute information related to concrete class - eclass mapping, interface to + * concrete class, references for cross reference computation, contained computations. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.20 $ + */ + +public class ERuntime extends EModelResolver { + + /** The logger */ + private static Log log = LogFactory.getLog(ERuntime.class); + + /** The singleton instance */ + public static final ERuntime INSTANCE = new ERuntime(); + + /** The list of epackages processed here */ + private final ArrayList<EPackage> epackages = new ArrayList<EPackage>(); + + /** The mapping from concrete classes to eclass and back */ + private final HashMap<Class<?>, EClass> concreteToEClass = new HashMap<Class<?>, EClass>(); + + private final HashMap<Class<?>, EClass> interfaceToEClass = new HashMap<Class<?>, EClass>(); + + private final HashMap<EClassifier, Class<?>> eclassifierToConcrete = new HashMap<EClassifier, Class<?>>(); + + /** The list of topclasses/interfaces */ + private final ArrayList<Class<?>> topClasses = new ArrayList<Class<?>>(); + + /** The list of contained classes/interfaces */ + private final ArrayList<Class<?>> containedClasses = new ArrayList<Class<?>>(); + + /** Register the epackages */ + @Override + public synchronized void register(EPackage[] epacks) { + for (int i = 0; i < epacks.length; i++) { + + if (!epackages.contains(epacks[i])) { + epackages.add(epacks[i]); + } + } + + computeConcreteInstanceMapping(); + computeContainedClasses(); + } + + /** Resets the maps/lists */ + @Override + public void clear() { + epackages.clear(); + containedClasses.clear(); + concreteToEClass.clear(); + interfaceToEClass.clear(); + eclassifierToConcrete.clear(); + topClasses.clear(); + } + + /** + * Computes which classes are contained and which are non-contained. Method must be called after + * the computeReferers method! + */ + private void computeContainedClasses() { + + topClasses.clear(); + containedClasses.clear(); + + for (int i = 0; i < epackages.size(); i++) { + final EPackage epack = epackages.get(i); + if (ignorePackage(epack)) { + continue; + } + + for (EClassifier eclassifier : epack.getEClassifiers()) { + if (!(eclassifier instanceof EClass)) { + continue; + } + + final EClass eclass = (EClass) eclassifier; + + // bit ugly compare on name, but document root should be ignored + // otherwise everything is contained + if (ExtendedMetaData.INSTANCE.isDocumentRoot(eclass)) { + continue; + } + + for (EReference eref : eclass.getEReferences()) { + if (!eref.isContainment()) { + continue; + } + final Class<?> toClass = eref.getEType().getInstanceClass(); + if (!containedClasses.contains(toClass)) { + containedClasses.add(toClass); + } + } + } + } + + // and then when it is not contained add it to the contained list + for (Class<?> clazz : interfaceToEClass.keySet()) { + if (containedClasses.contains(clazz)) { + continue; // already determined so continue + } + + if (isSelfOrSuperContained(clazz, containedClasses)) { + containedClasses.add(clazz); + } else { + final EClass eClass = getEClass(clazz); + // remove all the abstract types + // see bugzilla 220106 + if (eClass == null || !eClass.isAbstract()) { + topClasses.add(clazz); + } + } + } + + // topclasses are cleaned because they are used to query and otherwise + // different queries would return overlapping results (because of + // polymor.) + cleanList(topClasses); + cleanList(containedClasses); + } + + /** + * Walks through a interface inheritance structure and determines if a superclass is contained + * if so then the class is added to the containedclasses + */ + private boolean isSelfOrSuperContained(Class<?> checkClass, ArrayList<Class<?>> containedClasses) { + // assert (checkClass.isInterface()); + if (containedClasses.contains(checkClass)) { + return true; + } + final Class<?>[] interfaces = checkClass.getInterfaces(); + for (Class<?> element : interfaces) { + if (isSelfOrSuperContained(element, containedClasses)) { + return true; + } + } + return false; + } + + /** Returns the list of topclasses */ + public Class<?>[] getTopClasses() { + return topClasses.toArray(new Class[topClasses.size()]); + } + + /** Return the list of interfaces */ + public Set<Class<?>> getAllInterfaces() { + return interfaceToEClass.keySet(); + } + + /** Returns all concrete classes */ + public Set<Class<?>> getAllConcreteClasses() { + return concreteToEClass.keySet(); + } + + /** + * Retains only the root parent class in a list, so if an entry in the list as a parent in the + * same list then the child is deleted from the list + */ + private void cleanList(ArrayList<Class<?>> list) { + final ArrayList<Class<?>> toRemove = new ArrayList<Class<?>>(); + for (Class<?> clazz : list) { + if (clazz == null) { + continue; + } + final Class<?>[] supers = clazz.getInterfaces(); + for (Class<?> element : supers) { + if (list.contains(element)) { + toRemove.add(clazz); + break; + } + } + } + list.removeAll(toRemove); + } + + /** Determines concrete impl classes for each eclass */ + private void computeConcreteInstanceMapping() { + concreteToEClass.clear(); + eclassifierToConcrete.clear(); + interfaceToEClass.clear(); + + // walk through all the epackages + for (int i = 0; i < epackages.size(); i++) { + final EPackage epack = epackages.get(i); + + if (ignorePackage(epack)) { + log.debug("Not determining concrete classes for package " + epack.getName()); + continue; + } + + log.debug("Determining concrete classes for package " + epack.getName()); + + for (EClassifier eclassifier : epack.getEClassifiers()) { + if (!(eclassifier instanceof EClass)) { + continue; + } + + final Object instance = create((EClass) eclassifier); + if (instance != null && !(instance instanceof DynamicEObjectImpl)) { + eclassifierToConcrete.put(eclassifier, instance.getClass()); + concreteToEClass.put(instance.getClass(), (EClass) eclassifier); + } + if (eclassifier.getInstanceClass() != null) { + interfaceToEClass.put(eclassifier.getInstanceClass(), (EClass) eclassifier); + } + } + } + + // packaged in an extra arraylist to prevent concurrent modification + // exception. + final List<Class<?>> classes = new ArrayList<Class<?>>(concreteToEClass.keySet()); + for (Class<?> clz : classes) { + addAbstractSupers(clz); + } + } + + /** + * Walks up the class hierarchy and adds the superclasses to the concrete-interface mapping + * class sets + */ + private void addAbstractSupers(Class<?> clazz) { + + // clazz is null or not an eobject + if (clazz == null || !EObject.class.isAssignableFrom(clazz)) { + return; + } + + // if already been here then go on for the superclasses + if (concreteToEClass.get(clazz) != null) { + addAbstractSupers(clazz.getSuperclass()); + return; + } + + // new one, find all its interfaces + final Class<?>[] interf = clazz.getInterfaces(); + for (Class<?> element : interf) { + if (EObject.class.isAssignableFrom(element)) { + final EClass eclass = interfaceToEClass.get(element); + concreteToEClass.put(clazz, eclass); + eclassifierToConcrete.put(eclass, clazz); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.jpox.emf.IEMFDataStore#getEPackages() + */ + @Override + public EPackage[] getEPackages() { + return epackages.toArray(new EPackage[epackages.size()]); + } + + /** Returns true if the epackage is registered here */ + @Override + public boolean isRegistered(EPackage epackage) { + return epackages.contains(epackage); + } + + /** Convenience method to easily determine which packages should be ignored */ + private static boolean ignorePackage(EPackage epack) { + return false; +// } +// if (epack instanceof XMLTypePackageImpl) { +// return true; // ignore this +// } +// if (epack instanceof EcorePackageImpl) { +// return true; // ignore this +// } +// return false; + } + + /** Returns the instanceclass for a passed interface */ + public Class<?> getInstanceClass(Class<?> interf) { + final EClass eclass = interfaceToEClass.get(interf); + if (eclass == null) { + throw new TeneoException("No eclass for interf " + interf.getName()); + } + return getJavaClass(eclass); + } + + /** Returns the instanceclass for a passed eclass */ + @Override + public Class<?> getJavaClass(EClassifier eclassifier) { + if (eclassifier instanceof EClass) { + final EClass eclass = (EClass) eclassifier; + if (eclass.isInterface()) { + return eclass.getInstanceClass(); + } + } + return eclassifierToConcrete.get(eclassifier); + } + + /** Returns the interface class for a passed eclass */ + @Override + public Class<?> getJavaInterfaceClass(EClass eclass) { + return eclass.getInstanceClass(); + } + + /** Returns true if the passed EClass has a javaClass representation. */ + @Override + public boolean hasImplementationClass(EClassifier eclassifier) { + return null != getJavaClass(eclassifier); + } + + /** Returns null */ + @Override + public Object create(EClass eclass) { + // abstract instance classes are added later + if (eclass.isAbstract() || eclass.isInterface()) { + return null; + } + + // Check if the class is persistable + try { + return EcoreUtil.create(eclass); + } catch (Exception e) { + // log but do nothing because this happens when we try to create an + // object + // with an invalid classifier, which is a eclass! + log.debug("The classifier: " + eclass.getName() + " is not a valid eclass"); + + return null; + } + } + + /** Get the eclass for a certain class */ + @Override + public EClass getEClass(Class<?> clazz) { + if (clazz.isInterface()) { + return interfaceToEClass.get(clazz); + } + return concreteToEClass.get(clazz); + } + + /** Get the eclass for a certain class name */ + public EClass getEClass(String classname) { + try { + return getEClass(ClassLoaderResolver.classForName(classname)); + } catch (StoreClassLoadException e) { + log.debug("Failed to retreive ECLass for name: " + classname); + return null; + } + } + + /** + * Returns the structural feature for a certain field and object comby. Null is returned if + * nothing is found + */ + public EStructuralFeature getStructuralFeature(Class<?> clazz, String FieldName) { + final EClass eclass = getEClass(clazz); + if (eclass == null) { + return null; + } + return StoreUtil.getEStructuralFeature(eclass, FieldName); + } + + /** + * Returns the list of EMF interfaces which are contained. Only the topmost interface in a class + * hierarchy is returned. This can be used to automatically create the econtainer field + * mappings. + * + * Note that multiple classes in one inheritance structure can be present. + */ + public Class<?>[] getContainedInterfaces() { + return containedClasses.toArray(new Class[containedClasses.size()]); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.ecore.EModelResolver#getAllClassesAndInterfaces() + */ + @Override + public List<Class<?>> getAllClassesAndInterfaces() { + final List<Class<?>> result = new ArrayList<Class<?>>(); + result.addAll(getAllConcreteClasses()); + result.addAll(getAllInterfaces()); + return result; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/PackageRegistryProvider.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/PackageRegistryProvider.java new file mode 100644 index 000000000..cb54612fe --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/PackageRegistryProvider.java @@ -0,0 +1,65 @@ +/** + * <copyright> + * + * Copyright (c) 2009 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: PackageRegistryProvider.java,v 1.2 2009/10/15 20:47:43 mtaal Exp $ + */ + +package org.eclipse.emf.teneo; + +import org.eclipse.emf.ecore.EPackage; + +/** + * Provides the package registry to the rest of Teneo. As a default the global Package.Registry is used. There are two + * ways to override the behavior in this class by setting a Package.Registry explicitly. Or by replacing the singleton + * instance with your own implementation. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.2 $ + */ +public class PackageRegistryProvider { + + private static PackageRegistryProvider instance; + + public static PackageRegistryProvider getInstance() { + if (instance == null) { + instance = new PackageRegistryProvider(); + } + return instance; + } + + public static void setInstance(PackageRegistryProvider instance) { + PackageRegistryProvider.instance = instance; + } + + private EPackage.Registry packageRegistry = EPackage.Registry.INSTANCE; + + // is used to handle the package registry defined in a datastore + private ThreadLocal<EPackage.Registry> threadRegistry = new ThreadLocal<EPackage.Registry>(); + + public EPackage.Registry getPackageRegistry() { + final EPackage.Registry theThreadRegistry = threadRegistry.get(); + if (theThreadRegistry != null) { + return theThreadRegistry; + } + return packageRegistry; + } + + public void setPackageRegistry(EPackage.Registry packageRegistry) { + this.packageRegistry = packageRegistry; + } + + public void setThreadPackageRegistry(EPackage.Registry thePackageRegistry) { + threadRegistry.set(thePackageRegistry); + } + +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/PersistenceOptions.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/PersistenceOptions.java new file mode 100755 index 000000000..514799f02 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/PersistenceOptions.java @@ -0,0 +1,1253 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * Jason Henriksen - Mapping File Path + * Jason Henriksen - XSDDate and XSDDateTime constants + * </copyright> + * + * $Id: PersistenceOptions.java,v 1.71 2011/10/29 06:12:48 mtaal Exp $ + */ + +package org.eclipse.emf.teneo; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.regex.Pattern; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.teneo.annotations.pannotation.CascadeType; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Defines the property names used in the persistence mapping. + * <p> + * As a convenience, this class offers type-safe property accessor wrappers. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.71 $ + */ +public class PersistenceOptions implements ExtensionPoint { + + public static final String DEFAULT_CLASSPATH_FILENAME = "/teneo-persistence.properties"; + + public static final String RUNTIME_PREFIX = "teneo.runtime."; + + public static final String MAPPING_PREFIX = "teneo.mapping."; + + public static final String NAMING_PREFIX = "teneo.naming."; + + /** The logger */ + private static Log log = LogFactory.getLog(PersistenceOptions.class); + + // START: ++++++++++++++++++++++ SQL Naming related Options + // ++++++++++++++++++++++++++++++++++++ + + /** + * DiscriminatorColumn name, default is DTYPE. + */ + public static final String DISCRIMINATOR_COLUMN_NAME = NAMING_PREFIX + + "discriminator_column_name"; + + /** + * Join table naming strategy, two values: ejb3 and unique + * + * @Deprecated use the extensionManager concept + */ + public static final String JOIN_TABLE_NAMING_STRATEGY = NAMING_PREFIX + + "join_table_naming_strategy"; + + /** + * Deprecated use JOIN_TABLE_NAMING_STRATEGY + * + * @Deprecated + */ + public static final String JOIN_TABLE_NAMING_STRATEGY_OLD = MAPPING_PREFIX + + "join_table_naming_strategy"; + + /** + * Join column naming strategy, two values: unique and simple. unique is the + * default and results in guaranteed unique naming for the join columns + * simple will always try to use minimal name lengths and will most of the + * time just use the efeaturename. + */ + public static final String JOIN_COLUMN_NAMING_STRATEGY = NAMING_PREFIX + + "join_column_naming_strategy"; + + /** The column name of the id column in the idbag, default is id */ + public static final String IDBAG_ID_COLUMN_NAME = NAMING_PREFIX + + "idbag_id_column_name"; + + /** Name of version column, default value is e_version */ + public static final String VERSION_COLUMN_NAME = NAMING_PREFIX + + "version_column"; + + /** Name of id column, default value is e_id */ + public static final String ID_COLUMN_NAME = NAMING_PREFIX + + "default_id_column"; + + /** + * Truncate the column name if the length is larger than this property. In + * case of concatenating property names for foreign keys + */ + public static final String MAXIMUM_SQL_NAME_LENGTH = NAMING_PREFIX + + "max_sql_name_length"; + + /** + * The option which determines the casing of columns and table names, + * lowercase will result in lowercase letters, uppercase in uppercase, none + * will just work as it did until now + */ + public static final String SQL_CASE_STRATEGY = NAMING_PREFIX + "strategy"; + + /** + * If set then the system will generate readable foreign key names. The + * default is true. Set to false for backward compatibility. Note that + * before the default value of this property was false. + */ + public static final String SET_FOREIGN_KEY_NAME = NAMING_PREFIX + + "set_foreign_key_name"; + + /** + * The escape character to use when escaping table and column names. + * Standard Hibernate uses the ` (backtick). This is the default value. + */ + public static final String SQL_NAME_ESCAPE_CHARACTER = MAPPING_PREFIX + + "sql_name_escape_character"; + + /** + * The sql name strategy, if not set then the ClassicSQLNameStrategy is + * used. + * + * @Deprecated use the extensionManager concept + */ + public static final String SQL_NAME_STRATEGY = NAMING_PREFIX + + "sql_name_strategy"; + + /** + * The Table name prefix, as a default set to "". + */ + public static final String SQL_TABLE_NAME_PREFIX = NAMING_PREFIX + + "sql_table_name_prefix"; + + /** + * The Column name prefix, as a default set to "". + */ + public static final String SQL_COLUMN_NAME_PREFIX = NAMING_PREFIX + + "sql_column_name_prefix"; + + /** + * The FK name prefix, as a default set to "". + */ + public static final String SQL_FOREIGN_KEY_NAME_PREFIX = NAMING_PREFIX + + "sql_fk_name_prefix"; + + /** + * The Index name prefix, as a default set to "". + */ + public static final String SQL_INDEX_KEY_NAME_PREFIX = NAMING_PREFIX + + "sql_index_name_prefix"; + + /** + * Controls if manually set sql names (table name, column names) should also + * be truncated or cased. Default is true for backward compatability. + */ + public static final String AUTO_ADAPT_MANUAL_SET_SQL_NAMES = NAMING_PREFIX + + "auto_adapt_manual_set_sql_names"; + + // END: ++++++++++++++++++++++ SQL Naming related Options + // ++++++++++++++++++++++++++++++++++++ + + /** + * Controls if non-mutable eclasses should have a discriminator or version + * column. Default is false. + * + * @Deprecated use the extensionManager concept + */ + public static final String SQL_DISCRIMINATOR_VERSION_IMMUTABLE_ECLASS = NAMING_PREFIX + + "sql_discriminator_version_immutable_eclass"; + + /** + * Controls how the econtainer feature id is persisted. There are three + * values: featureid or featurename + * + * @Deprecated use the extensionManager concept + */ + public static final String ECONTAINER_FEATURE_PERSISTENCE_STRATEGY = NAMING_PREFIX + + "econtainer_feature_persistence_strategy"; + + /** + * see bugzilla 227673, option can be used to set the hibernate usertype + * used for xsd date and xsd date time fields. + */ + public static final String USER_XSDDATE_TYPE = MAPPING_PREFIX + + "UserDateType"; + public static final String USER_XSDDATETIME_TYPE = MAPPING_PREFIX + + "UserDateTimeType"; + public static final String USER_XSDTIME_TYPE = MAPPING_PREFIX + + "UserTimeType"; + + /** + * This option can be used to control the actual xsd date class used, as a + * default the javax.xml.datatype.XMLGregorianCalendar class is used. + */ + public static final String XSDDATE_CLASS = MAPPING_PREFIX + "XSDDateClass"; + + /** + * The default length of a varchar column. Normally hibernate will choose to + * set this to 255. + */ + public static final String DEFAULT_VARCHAR_LENGTH = MAPPING_PREFIX + + "default_varchar_length"; + + /** + * The maximum length of the comments which are copied from the model to the + * mapping file. The default is zero which means no comments are copied from + * the model to the mapping. + */ + public static final String MAX_COMMENT_LENGTH = MAPPING_PREFIX + + "max_comment_length"; + + /** + * EClass marked with Embeddable is always embedded, default is false. If + * this is set to true then it is not required anymore to set a + * + * @Embedded annotation on an ereference, + * @Embeddable on the EClass is then sufficient. + */ + public static final String MAP_EMBEDDABLE_AS_EMBEDDED = MAPPING_PREFIX + + "map_embeddable_as_embedded"; + + /** Optimistic locking */ + public static final String OPTIMISTIC = MAPPING_PREFIX + + "optimistic_locking"; + + /** + * Add an index for each foreign key mapping. Some databases do not add an + * index for a foreign key automatically. Setting this to true will + * automatically add an index field to each foreign key. + */ + public static final String ADD_INDEX_FOR_FOREIGN_KEY = MAPPING_PREFIX + + "add_index_for_fk"; + + /** + * Set or not set the cascade attribute on a mto, mtm or otm non-containment + * relation. The backward compatible value is true. The better performing + * value is false. The default is false. + * + * @Deprecated use CASCADE_POLICY_ON_NON_CONTAINMENT + */ + public static final String SET_DEFAULT_CASCADE_ON_NON_CONTAINMENT = MAPPING_PREFIX + + "set_default_cascade_on_non_containment"; + + /** Inheritance mapping */ + public static final String INHERITANCE_MAPPING = MAPPING_PREFIX + + "inheritance"; + + /** + * Can be set to force an update of the schema when the application starts. + * Note this option is only meaningfull for jpox, for hibernate use the + * hibernate property: hibernate.hbm2ddl_auto + */ + public static final String UPDATE_SCHEMA = RUNTIME_PREFIX + "update_schema"; + + /** Force all containment relations to be eagerly loaded or not */ + public static final String FETCH_CONTAINMENT_EAGERLY = MAPPING_PREFIX + + "fetch_containment_eagerly"; + + /** + * Force all associations to be extra lazy loaded See here + * http://sites.google + * .com/a/pintailconsultingllc.com/java/hibernate-extra-lazy + * -collection-fetching for some information. + * + * Default is false. + */ + public static final String FETCH_ASSOCIATION_EXTRA_LAZY = MAPPING_PREFIX + + "fetch_one_to_many_extra_lazy"; + + /** + * Set cascade all (incl. orphan delete) on containment relation. + * + * @Deprecated use CASCADE_POLICY_ON_CONTAINMENT + */ + public static final String SET_CASCADE_ALL_ON_CONTAINMENT = MAPPING_PREFIX + + "cascade_all_on_containment"; + + /** + * Can be used to set custom cascade policy for containment : <br> + * <code>ALL</code> or a combination of (<code>REMOVE</code>, + * <code>REFRESH</code>, <code>PERSIST</code>, <code>MERGE</code>) <br> + * e.g. : REMOVE,PERSIST,MERGE <br> + * Warning : ALL != REMOVE,REFRESH,PERSIST,MERGE <br> + * but ALL == REMOVE with delete Orphan, REFRESH,PERSIST,MERGE + */ + public static final String CASCADE_POLICY_ON_CONTAINMENT = MAPPING_PREFIX + + "cascade_policy_on_containment"; + + /** + * Can be used to set custom cascade policy for non containment : <br> + * a combination of (<code>REFRESH</code>,<code>PERSIST</code>, + * <code>MERGE</code>) e.g. : PERSIST,MERGE + */ + public static final String CASCADE_POLICY_ON_NON_CONTAINMENT = MAPPING_PREFIX + + "cascade_policy_on_non_containment"; + + /** + * Can be used to control if the entity ann. should be added automatically + * to the model elements or that the default annotator should work according + * to the ejb3 spec. + */ + public static final String SET_ENTITY_AUTOMATICALLY = MAPPING_PREFIX + + "set_entity_automatically"; + + /** + * Map all lists as a bag to the db (does not map the list index to the db), + * default is false + */ + public static final String ALWAYS_MAP_LIST_AS_BAG = MAPPING_PREFIX + + "always_map_list_as_bag"; + + /** + * Map all lists as a hibernate idbag to the db (does not map the list index + * to the db), default is false + */ + public static final String MAP_ALL_LISTS_AS_IDBAG = MAPPING_PREFIX + + "map_all_lists_as_idbag"; + + /** Use static hibernate mapping file */ + public static final String USE_MAPPING_FILE = MAPPING_PREFIX + + "hibernate_mapping_file"; + + /** + * The complete resource path to the mapping file, can be used instead of + * the USE_MAPPING_FILE option + */ + public static final String MAPPING_FILE_PATH = MAPPING_PREFIX + + "mapping_file_name"; + + /** + * Automatically add @Id to ID feature + * + * @id annotation to ID xsd type + */ + public static final String ID_FEATURE_AS_PRIMARY_KEY = NAMING_PREFIX + + "id_feature_as_primary_key"; + + /** + * Automatically add + * + * @GeneratedValue to ID feature for which + * @Id is added automatically, default is true. + */ + public static final String SET_GENERATED_VALUE_ON_ID_FEATURE = NAMING_PREFIX + + "set_generated_value_on_id_feature"; + + /** + * The name of the id feature if no feature has an id. + * + * @id annotation + */ + public static final String DEFAULT_ID_FEATURE_NAME = NAMING_PREFIX + + "default_id_feature"; + + /** + * The path of the persistence XML file. + */ + public static final String PERSISTENCE_XML = MAPPING_PREFIX + + "persistence_xml"; + + /** + * Ignore mapping EAnnotations. Primarily meant for test cases that use + * Persistence XML mapping, so that they can reuse the same sample models. + */ + public static final String IGNORE_EANNOTATIONS = MAPPING_PREFIX + + "ignore_eannotations"; + + /** + * Map all emaps as true hibernate maps, default is true. In EMF an EMap is + * in fact an EList with Map entries. Originally Teneo maps this as a + * hibernate list. In the new behavior hibernate can map the emap as a real + * map. The default is true. + */ + public static final String EMAP_AS_TRUE_MAP = MAPPING_PREFIX + + "emap_as_true_map"; + + /** + * This option controls if in case of hibernate also a name attribute should + * be added to the class/subclass tag. By adding this a class is mapped as + * an entity as well as a normal class. Also mapping as a normal class has + * the advantage that proxies can be used and that queries can use actual + * class names and interface names. This option is really there for backward + * compatibility. There are no apparent dis-advantages of adding a name + * attribute so the default of this option is true. Note that an eclass must + * have an implementation class otherwise this option has no effect. + * Interfaces are for example always mapped as an entity. + */ + public static final String ALSO_MAP_AS_CLASS = MAPPING_PREFIX + + "also_map_as_class"; + + /** + * This option controls if as a default all classes should be proxied (for + * hibernate). This means that you don't need to add a + * + * @Proxy annotation to each eclass. As a default Teneo will use the eclass + * interface as the proxy class. When this is set to true then the + * option ALSO_MAP_AS_CLASS should also be true. + */ + public static final String SET_PROXY = MAPPING_PREFIX + "set_proxy"; + + /** + * This option forces lazy=true without the proxy attribute in the hibernate + * mapping. + */ + public static final String FORCE_LAZY = MAPPING_PREFIX + "force_lazy"; + + /** + * Disable EContainer mapping. + */ + public static final String DISABLE_ECONTAINER_MAPPING = MAPPING_PREFIX + + "disable_econtainer"; + + /** + * Option to specify that for non-contained one-to-many always a join table + * is used, default is true + */ + public static final String JOIN_TABLE_FOR_NON_CONTAINED_ASSOCIATIONS = MAPPING_PREFIX + + "join_table_for_non_contained_associations"; + + /** + * Determines whether to always include a version mapping even if one is not + * specified. Defaults to "true" + */ + public static final String ALWAYS_VERSION = MAPPING_PREFIX + + "always_version"; + + /** + * The default cache strategy, can be one of: NONE, READ_ONLY, + * NONSTRICT_READ_ONLY, READ_WRITE, TRANSACTIONAl. If different than NONE + * (=default) then for Hibernate every class will be second-level cached! + */ + public static final String DEFAULT_CACHE_STRATEGY = MAPPING_PREFIX + + "default_cache_strategy"; + + /** The default time/date type used */ + public static final String DEFAULT_TEMPORAL_VALUE = MAPPING_PREFIX + + "default_temporal"; + + /** + * If true then EAttributes which are not set are stored as null in the + * database, if false then the default values is stored in the database. + */ + public static final String HANDLE_UNSET_AS_NULL = RUNTIME_PREFIX + + "handle_unset_as_null"; + + /** + * When an unset feature is persisted, the database will get a null value, + * default is false. + */ + public static final String CONVERT_UNSET_TO_NULL = RUNTIME_PREFIX + + "convert_unset_to_null"; + + /** + * If set to true then the document root is also mapped. + */ + public static final String MAP_DOCUMENT_ROOT = MAPPING_PREFIX + + "map_document_root"; + + /** + * If set to true then the system will automatically add referenced + * epackages + */ + public static final String AUTO_ADD_REFERENCED_EPACKAGES = MAPPING_PREFIX + + "auto_add_referenced_epackages"; + + /** + * If set to true then the system will map all eclasses as an EAV mapping. + * See http://www.elver.org/hibernate/eav_mapping.html + */ + public static final String EAV_MAPPING = MAPPING_PREFIX + "eav_mapping"; + + /** + * If set then the eav mapping file is read from the location defined by + * this property. + */ + public static final String EAV_MAPPING_FILE = MAPPING_PREFIX + + "eav_location"; + + /** + * Map the FeatureMap as a component entity + */ + public static final String FEATUREMAP_AS_COMPONENT = MAPPING_PREFIX + + "featuremap_as_component"; + + /** + * Additional sources which are taken into account parsing model + * annotations. The value can be a comma delimited list of source values. + */ + public static final String EXTRA_ANNOTATION_SOURCES = MAPPING_PREFIX + + "extra_annotation_sources"; + + public final static String ECONTAINER_CLASS_COLUMN = "econtainer_class_column"; + public final static String ECONTAINER_COLUMN = "e_container_column"; + public final static String ECONTAINER_FEATURE_NAME_COLUMN = "e_container_feature_name_column"; + + /** Returns the default properties used in the system */ + public static Properties getDefaultProperties() { + final Properties props = new Properties(); + props.setProperty(HANDLE_UNSET_AS_NULL, "false"); + props.setProperty(CONVERT_UNSET_TO_NULL, "false"); + props.setProperty(JOIN_TABLE_FOR_NON_CONTAINED_ASSOCIATIONS, "true"); + props.setProperty(USE_MAPPING_FILE, "false"); + // props.setProperty(MAPPING_FILE_PATH, null); // null is the default + // anyway + props.setProperty(SET_CASCADE_ALL_ON_CONTAINMENT, ""); + props.setProperty(CASCADE_POLICY_ON_CONTAINMENT, "ALL"); + props.setProperty(OPTIMISTIC, "true"); + props.setProperty(UPDATE_SCHEMA, "false"); + props.setProperty(FETCH_CONTAINMENT_EAGERLY, "false"); + props.setProperty(FETCH_ASSOCIATION_EXTRA_LAZY, "false"); + props.setProperty(SET_ENTITY_AUTOMATICALLY, "true"); + props.setProperty(VERSION_COLUMN_NAME, "e_version"); + props.setProperty(SQL_CASE_STRATEGY, "lowercase"); + props.setProperty(ID_COLUMN_NAME, "e_id"); + props.setProperty(DISABLE_ECONTAINER_MAPPING, "false"); + props.setProperty(MAXIMUM_SQL_NAME_LENGTH, "-1"); + props.setProperty(IGNORE_EANNOTATIONS, "false"); + props.setProperty(ALWAYS_VERSION, "true"); + props.setProperty(DEFAULT_CACHE_STRATEGY, "NONE"); + props.setProperty(DISCRIMINATOR_COLUMN_NAME, "DTYPE"); + props.setProperty(JOIN_TABLE_NAMING_STRATEGY, "unique"); + // props.setProperty(JOIN_TABLE_NAMING_STRATEGY_OLD, "unique"); + props.setProperty(JOIN_COLUMN_NAMING_STRATEGY, "unique"); + props.setProperty(DEFAULT_TEMPORAL_VALUE, "TIMESTAMP"); + props.setProperty(DEFAULT_ID_FEATURE_NAME, "e_id"); + props.setProperty(ID_FEATURE_AS_PRIMARY_KEY, "true"); + props.setProperty(SET_GENERATED_VALUE_ON_ID_FEATURE, "true"); + props.setProperty(EMAP_AS_TRUE_MAP, "true"); + props.setProperty(ALWAYS_MAP_LIST_AS_BAG, "false"); + props.setProperty(ALSO_MAP_AS_CLASS, "true"); + props.setProperty(SET_PROXY, "false"); + props.setProperty(FORCE_LAZY, "false"); + props.setProperty(MAP_ALL_LISTS_AS_IDBAG, "false"); + props.setProperty(IDBAG_ID_COLUMN_NAME, "ID"); + props.setProperty(ADD_INDEX_FOR_FOREIGN_KEY, "false"); + props.setProperty(SET_DEFAULT_CASCADE_ON_NON_CONTAINMENT, ""); + props.setProperty(CASCADE_POLICY_ON_NON_CONTAINMENT, ""); + props.setProperty(SET_FOREIGN_KEY_NAME, "true"); + props.setProperty(MAP_EMBEDDABLE_AS_EMBEDDED, "false"); + props.setProperty(MAX_COMMENT_LENGTH, "0"); + props.setProperty(DEFAULT_VARCHAR_LENGTH, "-1"); + props.setProperty(SQL_NAME_ESCAPE_CHARACTER, "`"); + props.setProperty(USER_XSDDATE_TYPE, + "org.eclipse.emf.teneo.hibernate.mapping.XSDDate"); + props.setProperty(USER_XSDDATETIME_TYPE, + "org.eclipse.emf.teneo.hibernate.mapping.XSDDateTime"); + props.setProperty(USER_XSDTIME_TYPE, + "org.eclipse.emf.teneo.hibernate.mapping.XSDDateTime"); + props.setProperty(XSDDATE_CLASS, + "javax.xml.datatype.XMLGregorianCalendar"); + props.setProperty(SQL_DISCRIMINATOR_VERSION_IMMUTABLE_ECLASS, "true"); + props.setProperty(ECONTAINER_FEATURE_PERSISTENCE_STRATEGY, + "FEATURENAME"); + props.setProperty(SQL_TABLE_NAME_PREFIX, ""); + props.setProperty(SQL_COLUMN_NAME_PREFIX, ""); + props.setProperty(SQL_FOREIGN_KEY_NAME_PREFIX, ""); + props.setProperty(SQL_INDEX_KEY_NAME_PREFIX, ""); + props.setProperty(MAP_DOCUMENT_ROOT, "false"); + props.setProperty(EAV_MAPPING, "false"); + props.setProperty(AUTO_ADD_REFERENCED_EPACKAGES, "false"); + props.setProperty(ECONTAINER_CLASS_COLUMN, + Constants.COLUMN_ECONTAINER_CLASS); + props.setProperty(ECONTAINER_COLUMN, Constants.COLUMN_ECONTAINER); + props.setProperty(ECONTAINER_FEATURE_NAME_COLUMN, + Constants.COLUMN_ECONTAINER_FEATURE_NAME); + props.setProperty(FEATUREMAP_AS_COMPONENT, "false"); + props.setProperty(EXTRA_ANNOTATION_SOURCES, ""); + props.setProperty(AUTO_ADAPT_MANUAL_SET_SQL_NAMES, "true"); + + return props; + } + + /** + * The wrapped Properties instance. + */ + private final Properties properties; + + /** + * @return value of {@link #DISCRIMINATOR_COLUMN_NAME} + */ + public String getDiscriminatorColumnName() { + return properties.getProperty(DISCRIMINATOR_COLUMN_NAME); + } + + /** + * @return value of {@link #EXTRA_ANNOTATION_SOURCES} + */ + public String getExtraAnnotationSources() { + return properties.getProperty(EXTRA_ANNOTATION_SOURCES); + } + + /** + * @return value of {@link #FEATUREMAP_AS_COMPONENT} + */ + public boolean isMapFeatureMapAsComponent() { + return Boolean.valueOf(properties.getProperty(FEATUREMAP_AS_COMPONENT)) + .booleanValue(); + } + + /** + * @return value of {@link AUTO_ADAPT_MANUAL_SET_SQL_NAMES} + */ + public boolean isAutoAdaptManualSQLNames() { + return Boolean.valueOf( + properties.getProperty(AUTO_ADAPT_MANUAL_SET_SQL_NAMES)) + .booleanValue(); + } + + /** + * @return value of {@link #ECONTAINER_COLUMN} + */ + public String getEContainerColumn() { + return properties.getProperty(ECONTAINER_COLUMN); + } + + /** + * @return value of {@link #ECONTAINER_CLASS_COLUMN} + */ + public String getEContainerClassColumn() { + return properties.getProperty(ECONTAINER_CLASS_COLUMN); + } + + /** + * @return value of {@link #ECONTAINER_FEATURE_NAME_COLUMN} + */ + public String getEContainerFeatureNameColumn() { + return properties.getProperty(ECONTAINER_FEATURE_NAME_COLUMN); + } + + /** + * @return value of {@link #EAV_MAPPING} + */ + public boolean isEAVMapping() { + return Boolean.valueOf(properties.getProperty(EAV_MAPPING)) + .booleanValue(); + } + + /** + * @return value of {@link #EAV_MAPPING_FILE} + */ + public String getEAVMappingFile() { + return properties.getProperty(EAV_MAPPING_FILE); + } + + /** + * @return value of {@link #AUTO_ADD_REFERENCED_EPACKAGES} + */ + public boolean isAutoAddReferencedEPackages() { + return Boolean.valueOf( + properties.getProperty(AUTO_ADD_REFERENCED_EPACKAGES)) + .booleanValue(); + } + + /** + * @return value of {@link #MAP_DOCUMENT_ROOT} + */ + public boolean isMapDocumentRoot() { + return Boolean.valueOf(properties.getProperty(MAP_DOCUMENT_ROOT)) + .booleanValue(); + } + + /** + * @return value of the {@link #HANDLE_UNSET_AS_NULL} option + */ + public boolean getHandleUnsetAsNull() { + return Boolean.valueOf(properties.getProperty(HANDLE_UNSET_AS_NULL)) + .booleanValue(); + } + + /** + * @return value of the {@link #CONVERT_UNSET_TO_NULL} option + */ + public boolean getConvertUnsetToNull() { + return Boolean.valueOf(properties.getProperty(CONVERT_UNSET_TO_NULL)) + .booleanValue(); + } + + /** + * Construct a new instance using Properties. + */ + public PersistenceOptions(Properties properties) { + this.properties = getDefaultProperties(); + + if (properties != null) { + this.properties.putAll(properties); + } + + logProperties(); + } + + /** + * Constructs a new instance by loading properties from + * "/elver-persistence.properties" at the root of the classpath. + */ + public PersistenceOptions() { + this.properties = getDefaultProperties(); + + final Properties props = new Properties(); + InputStream in = null; + try { + in = this.getClass() + .getResourceAsStream(DEFAULT_CLASSPATH_FILENAME); + if (in != null) { + log.debug("Loading persistence options from classpath \"" + + DEFAULT_CLASSPATH_FILENAME + "\"."); + props.load(in); + } + } catch (IOException e) { + throw new RuntimeException("Error loading \"" + + DEFAULT_CLASSPATH_FILENAME + "\" from classpath:" + + e.getMessage(), e); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + // Ignore. + } + } + this.properties.putAll(props); + + logProperties(); + } + + /** Dump the props */ + public void logProperties() { + log.debug("Properties of PersistenceOptions:"); + for (Object key : properties.keySet()) { + log.debug(key + ": " + properties.get(key)); + } + } + + public String getSQLTableNamePrefix() { + return properties.getProperty(SQL_TABLE_NAME_PREFIX); + } + + public String getSQLColumnNamePrefix() { + return properties.getProperty(SQL_COLUMN_NAME_PREFIX); + } + + public String getSQLForeignKeyNamePrefix() { + return properties.getProperty(SQL_FOREIGN_KEY_NAME_PREFIX); + } + + public String getSQLIndexNamePrefix() { + return properties.getProperty(SQL_INDEX_KEY_NAME_PREFIX); + } + + /** Return the default temporal value */ + public String getDefaultTemporalValue() { + return properties.getProperty(DEFAULT_TEMPORAL_VALUE); + } + + /** Return the IDBAG_ID_COLUMN_NAME */ + public String getIDBagIDColumnName() { + return getSQLColumnNamePrefix() + + properties.getProperty(IDBAG_ID_COLUMN_NAME); + } + + /** + * Returns the value of the EMAP_AS_TRUE_MAP option, default is false + */ + public boolean isMapEMapAsTrueMap() { + return Boolean.valueOf(properties.getProperty(EMAP_AS_TRUE_MAP)) + .booleanValue(); + } + + /** + * Returns the value of the SET_DEFAULT_CASCADE_ON_MTO_MTM option, default + * is false + */ + public boolean isSetDefaultCascadeOnNonContainment() { + String property = properties + .getProperty(SET_DEFAULT_CASCADE_ON_NON_CONTAINMENT); + if (!property.equals("")) { + return Boolean.valueOf(property).booleanValue(); + } + return !properties.getProperty(CASCADE_POLICY_ON_NON_CONTAINMENT) + .equals(""); + } + + public boolean isSetCascadePolicyForNonContainment() { + return !properties.getProperty(CASCADE_POLICY_ON_NON_CONTAINMENT) + .equals(""); + } + + public String getCascadePolicyForContainment() { + return properties.getProperty(CASCADE_POLICY_ON_CONTAINMENT); + } + + public String getCascadePolicyForNonContainment() { + return properties.getProperty(CASCADE_POLICY_ON_NON_CONTAINMENT); + } + + /** + * Returns true if the CASCADE_POLICY_ON_NON_CONTAINMENT property contains + * the merge cascade type + */ + public boolean isSetCascadeMergeOnNonContainment() { + return isSetCascadeOnNonContainement(CascadeType.MERGE.getName()); + } + + /** + * Returns true if the CASCADE_POLICY_ON_NON_CONTAINMENT property contains + * the persist cascade type + */ + public boolean isSetCascadePersistOnNonContainment() { + return isSetCascadeOnNonContainement(CascadeType.PERSIST.getName()); + } + + /** + * Returns true if the CASCADE_POLICY_ON_NON_CONTAINMENT property contains + * the refresh cascade type + */ + public boolean isSetCascadeRefreshOnNonContainment() { + return isSetCascadeOnNonContainement(CascadeType.REFRESH.getName()); + } + + /** + * Returns true if the CASCADE_POLICY_ON_NON_CONTAINMENT property contains + * the given cascade type + */ + private boolean isSetCascadeOnNonContainement(String cascadeType) { + return isSetCascade( + properties.getProperty(CASCADE_POLICY_ON_NON_CONTAINMENT), + cascadeType); + } + + /** + * Returns the value of the SET_FOREIGN_KEY_NAME option, default is true + */ + public boolean isSetForeignKeyNames() { + return Boolean.valueOf(properties.getProperty(SET_FOREIGN_KEY_NAME)) + .booleanValue(); + } + + /** + * Returns the value of the ALSO_MAP_AS_CLASS option, default is false + */ + public boolean isAlsoMapAsClass() { + return Boolean.valueOf(properties.getProperty(ALSO_MAP_AS_CLASS)) + .booleanValue(); + } + + /** + * Returns the value of the MAP_EMBEDDABLE_AS_EMBEDDED option, default is + * false + */ + public boolean isMapEmbeddableAsEmbedded() { + return Boolean.valueOf( + properties.getProperty(MAP_EMBEDDABLE_AS_EMBEDDED)) + .booleanValue(); + } + + /** + * Returns true if the proxy annotation should be added automatically + */ + public boolean isSetProxy() { + return Boolean.valueOf(properties.getProperty(SET_PROXY)) + .booleanValue(); + } + + /** + * Returns true if the lazy attribute should be forced to true in the hbm + * mapping. + */ + public boolean isForceLazy() { + return Boolean.valueOf(properties.getProperty(FORCE_LAZY)) + .booleanValue(); + } + + /** + * Returns the value of the ALWAYS_MAP_LIST_AS_BAG option, default is false + */ + public boolean alwaysMapListAsBag() { + return Boolean.valueOf(properties.getProperty(ALWAYS_MAP_LIST_AS_BAG)) + .booleanValue(); + } + + /** + * Returns the value of the MAP_ALL_LISTS_AS_IDBAG option, default is false + */ + public boolean alwaysMapListAsIdBag() { + return Boolean.valueOf(properties.getProperty(MAP_ALL_LISTS_AS_IDBAG)) + .booleanValue(); + } + + /** Returns the value of the ADD_INDEX_FOR_FOREIGN_KEY option */ + public boolean isAddIndexForForeignKey() { + return Boolean.valueOf( + properties.getProperty(ADD_INDEX_FOR_FOREIGN_KEY)) + .booleanValue(); + } + + /** + * Returns the value of the UseJoinTableForNonContainedAssociations option, + * default is false + */ + public boolean isJoinTableForNonContainedAssociations() { + return Boolean + .valueOf( + properties + .getProperty(JOIN_TABLE_FOR_NON_CONTAINED_ASSOCIATIONS)) + .booleanValue(); + } + + /** Returns the value of the UseMappingFile option, default is false */ + public boolean isUseMappingFile() { + return Boolean.valueOf(properties.getProperty(USE_MAPPING_FILE)) + .booleanValue(); + } + + /** Returns the value of the MAPPING_FILE_PATH option, default is "" */ + public String getMappingFilePath() { + return properties.getProperty(MAPPING_FILE_PATH); + } + + /** Returns the value of the id feature as primary key */ + public boolean isIDFeatureAsPrimaryKey() { + return Boolean.valueOf( + properties.getProperty(ID_FEATURE_AS_PRIMARY_KEY)) + .booleanValue(); + } + + /** Returns the value of the SET_GENERATED_VALUE_ON_ID_FEATURE option */ + public boolean isSetGeneratedValueOnIDFeature() { + return Boolean.valueOf( + properties.getProperty(SET_GENERATED_VALUE_ON_ID_FEATURE)) + .booleanValue(); + } + + /** Returns the value of the orphan delete on containment, default is true */ + public boolean isSetCascadeAllOnContainment() { + if (!properties.getProperty(SET_CASCADE_ALL_ON_CONTAINMENT).equals("")) { + return Boolean.valueOf( + properties.getProperty(SET_CASCADE_ALL_ON_CONTAINMENT)) + .booleanValue(); + } + return isSetCascadeOnContainement(CascadeType.ALL.getName()); + } + + /** + * Returns true if CASCADE_POLICY_ON_CONTAINMENT property contains the MERGE + * cascade type + */ + public boolean isSetCascadeMergeOnContainment() { + return isSetCascadeOnContainement(CascadeType.MERGE.getName()); + } + + /** + * Returns true if CASCADE_POLICY_ON_CONTAINMENT property contains the + * PERSIST cascade type + */ + public boolean isSetCascadePersistOnContainment() { + return isSetCascadeOnContainement(CascadeType.PERSIST.getName()); + } + + /** + * Returns true if CASCADE_POLICY_ON_CONTAINMENT property contains the + * REMOVE cascade type + */ + public boolean isSetCascadeRemoveOnContainment() { + return isSetCascadeOnContainement(CascadeType.REMOVE.getName()); + } + + /** + * Returns true if CASCADE_POLICY_ON_CONTAINMENT property contains the + * REFRESH cascade type + */ + public boolean isSetCascadeRefreshOnContainment() { + return isSetCascadeOnContainement(CascadeType.REFRESH.getName()); + } + + /** + * Returns true if CASCADE_POLICY_ON_CONTAINMENT property contains the given + * cascade type + */ + private boolean isSetCascadeOnContainement(String cascadeType) { + return isSetCascade( + properties.getProperty(CASCADE_POLICY_ON_CONTAINMENT), + cascadeType); + } + + /** Returns true if property contains the given cascade type */ + private boolean isSetCascade(String property, String cascadeType) { + return Pattern.matches(".*\\b" + cascadeType.toUpperCase() + "\\b.*", + property.toUpperCase()); + } + + /** Returns the value of the Optimistic option, default is true */ + public boolean isUseOptimisticLocking() { + return Boolean.valueOf(properties.getProperty(OPTIMISTIC)) + .booleanValue(); + } + + /** Returns the value of the UpdateSchema option, default is true */ + public boolean isUpdateSchema() { + return Boolean.valueOf(properties.getProperty(UPDATE_SCHEMA)) + .booleanValue(); + } + + /** Returns the value of the fetch containment eagerly, default is false */ + public boolean isFetchContainmentEagerly() { + return Boolean.valueOf( + properties.getProperty(FETCH_CONTAINMENT_EAGERLY)) + .booleanValue(); + } + + /** Returns the value of the fetch extra lazy option, default is false */ + public boolean isFetchAssociationExtraLazy() { + return Boolean.valueOf( + properties.getProperty(FETCH_ASSOCIATION_EXTRA_LAZY)) + .booleanValue(); + } + + /** Is set entity automatically, default is true */ + public boolean isSetEntityAutomatically() { + return Boolean + .valueOf(properties.getProperty(SET_ENTITY_AUTOMATICALLY)) + .booleanValue(); + } + + /** Returns the inheritance mapping strategy, can be null */ + public String getInheritanceMapping() { + return properties.getProperty(INHERITANCE_MAPPING); + } + + /** Returns the value of the version column option, returns null if not set */ + public String getVersionColumnName() { + return getSQLColumnNamePrefix() + + properties.getProperty(VERSION_COLUMN_NAME); + } + + /** Returns the value of the naming strategy, default is lower case */ + public String getSQLCaseStrategy() { + return properties.getProperty(SQL_CASE_STRATEGY); + } + + /** Returns the value of the id column option, returns null if not set */ + public String getIdColumnName() { + return getSQLColumnNamePrefix() + + properties.getProperty(ID_COLUMN_NAME); + } + + /** Returns the value of the default id property */ + public String getDefaultIDFeatureName() { + return properties.getProperty(DEFAULT_ID_FEATURE_NAME); + } + + /** Return the sql escape character, default is ` */ + public String getSqlNameEscapeCharacter() { + return properties.getProperty(SQL_NAME_ESCAPE_CHARACTER); + } + + /** Returns the value of the join table naming strategy */ + public String getJoinTableNamingStrategy() { + if (properties.get(JOIN_TABLE_NAMING_STRATEGY_OLD) != null) { + log.warn("The option " + JOIN_TABLE_NAMING_STRATEGY_OLD + + " is deprecated, please use: " + + JOIN_TABLE_NAMING_STRATEGY); + return properties.getProperty(JOIN_TABLE_NAMING_STRATEGY); + } + return properties.getProperty(JOIN_TABLE_NAMING_STRATEGY); + } + + /** Returns the value of the join column naming strategy */ + public String getJoinColumnNamingStrategy() { + return properties.getProperty(JOIN_COLUMN_NAMING_STRATEGY); + } + + /** + * Returns the default second level caching strategy, default value is NONE + * (no second level caching). + */ + public String getDefaultCacheStrategy() { + return properties.getProperty(DEFAULT_CACHE_STRATEGY); + } + + /** Return the default varchar length */ + public int getDefaultVarCharLength() { + return Integer.parseInt(properties.getProperty(DEFAULT_VARCHAR_LENGTH)); + } + + /** Are econtainer mappings (hibernate) disabled */ + public boolean isDisableEContainerMapping() { + return Boolean.valueOf( + properties.getProperty(DISABLE_ECONTAINER_MAPPING)) + .booleanValue(); + } + + /** Return the value of the MAX_COMMENT_LENGTH */ + public int getMaximumCommentLength() { + final String colLength = properties.getProperty(MAX_COMMENT_LENGTH); + try { + final int maxLength = Integer.parseInt(colLength); + return maxLength; + } catch (NumberFormatException e) { + log.error("Property " + MAXIMUM_SQL_NAME_LENGTH + + " as a non-integer value: " + colLength + + " value is ignored"); + return 0; + } + } + + /** Return the max. sql name length, or -1 if not set or illegal */ + public int getMaximumSqlNameLength() { + final String colLength = properties + .getProperty(MAXIMUM_SQL_NAME_LENGTH); + try { + final int maxLength = Integer.parseInt(colLength); + if (maxLength == 0) { + throw new TeneoException( + "The option MAXIMUM_SQL_NAME_LENGTH has a value of zero. " + + "This will result in empty column and table names in the mapping! " + + "Please change this option to a more usable value."); + } + if (maxLength < 4 && maxLength > -1) { + log.warn("The option MAXIMUM_SQL_NAME_LENGTH has a low value: " + + maxLength + ". Are you sure this is correct?"); + } + return maxLength; + } catch (NumberFormatException e) { + log.error("Property " + MAXIMUM_SQL_NAME_LENGTH + + " as a non-integer value: " + colLength + + " value is ignored"); + return -1; + } + } + + /** + * Returns the value of the SQL_DISCRIMINATOR_VERSION_IMMUTABLE_ECLASS + * option. + */ + public Boolean isDiscriminatorVersionOnImmutableEClass() { + return Boolean + .valueOf( + properties + .getProperty(SQL_DISCRIMINATOR_VERSION_IMMUTABLE_ECLASS)) + .booleanValue(); + } + + /** + * Returns the path of the XML persistence mapping file or null if the + * property has not been defined. + */ + public String getPersistenceXmlPath() { + return properties.getProperty(PERSISTENCE_XML); + } + + /** + * Returns a boolean indication whether to ignore mapping EAnnotations. + */ + public boolean isIgnoreEAnnotations() { + return Boolean.valueOf(properties.getProperty(IGNORE_EANNOTATIONS)) + .booleanValue(); + } + + /** + * Returns an array of all String constants. + * + * @return + */ + public static String[] propertyNames() { + final List<String> names = new ArrayList<String>(); + for (Field field : PersistenceOptions.class.getFields()) { + try { + if ((field.getModifiers() & Modifier.STATIC) > 0 + & field.getType().equals(String.class)) { + final String value = (String) field.get(null); + if (value.startsWith("teneo.")) { + names.add(value); + } + } + } catch (IllegalAccessException e) { + } + } + Collections.sort(names); + return names.toArray(new String[names.size()]); + } + + public boolean getAlwaysVersion() { + return Boolean.valueOf(properties.getProperty(ALWAYS_VERSION)) + .booleanValue(); + } + + /** + * @return the properties + */ + public Properties getProperties() { + return properties; + } + + /** + * Creates the correct sql name strategy based on the String setting. + * + * @Deprecated use the SQLNameStrategy and extensionManager concept + */ + public String getSQLNameStrategy() { + return properties.getProperty(SQL_NAME_STRATEGY); + } + + public String getUserXSDDateType() { + return properties.getProperty(USER_XSDDATE_TYPE); + } + + public String getUserXSDTime() { + return properties.getProperty(USER_XSDTIME_TYPE); + } + + public String getUserXSDDateTime() { + return properties.getProperty(USER_XSDDATETIME_TYPE); + } + + public String getXSDDateClass() { + return properties.getProperty(XSDDATE_CLASS); + } + + public EContainerFeaturePersistenceStrategy getEContainerFeaturePersistenceStrategy() { + String strategy = properties + .getProperty(ECONTAINER_FEATURE_PERSISTENCE_STRATEGY); + if (strategy == null) { + throw new TeneoException( + "Option ECONTAINER_FEATURE_PERSISTENCE_STRATEGY not set, please set it to one of: featureid, featurename, both"); + } + EContainerFeaturePersistenceStrategy result = EContainerFeaturePersistenceStrategy + .valueOf(strategy.toUpperCase()); + if (result == null) { + throw new TeneoException( + "Option ECONTAINER_FEATURE_PERSISTENCE_STRATEGY not set correctly (" + + strategy + + "), please set it to one of: featureid, featurename, both"); + } + return result; + } + + public enum EContainerFeaturePersistenceStrategy { + FEATURENAME, FEATUREID, BOTH + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/StoreValidationException.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/StoreValidationException.java new file mode 100755 index 000000000..9e83087a2 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/StoreValidationException.java @@ -0,0 +1,62 @@ +/** + * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights + * reserved. This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal - Initial API and + * implementation </copyright> $Id: StoreValidationException.java,v 1.4 2007/02/08 23:14:41 mtaal + * Exp $ + */ + +package org.eclipse.emf.teneo; + +import org.eclipse.emf.common.util.Diagnostic; + +/** + * Is used to contain a list of Diagnostics which contain error messages found during the save of a + * resource. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.8 $ + */ + +public class StoreValidationException extends TeneoException { + /** + * Serializable id + */ + private static final long serialVersionUID = 7433341056815136417L; + + /** Creates the message */ + private static String createMessage(Diagnostic[] diagnostics) { + final StringBuffer result = new StringBuffer(); + for (int i = 0; i < diagnostics.length; i++) { + if (i > 0) { + result.append("\n"); + } + result.append(diagnostics[i].getMessage()); + + for (Diagnostic childDiagnostic : diagnostics[i].getChildren()) { + switch (childDiagnostic.getSeverity()) { + case Diagnostic.ERROR: + result.append("\n\t" + childDiagnostic.getMessage()); + } + } + } + return result.toString(); + } + + /** The list of diagnostics */ + private final Diagnostic[] diagnostics; + + /** + * The constructor, logs the exception also + */ + public StoreValidationException(Diagnostic[] diags) { + super(createMessage(diags)); + diagnostics = diags; + } + + /** Returns the list of diagnostics of this exception */ + public Diagnostic[] getDiagnostics() { + return diagnostics; + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/TeneoException.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/TeneoException.java new file mode 100755 index 000000000..8a3a17dc6 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/TeneoException.java @@ -0,0 +1,54 @@ +/** + * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights + * reserved. This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal - Initial API and + * implementation </copyright> $Id: TeneoException.java,v 1.4 2009/03/30 07:53:05 mtaal Exp $ + */ + +package org.eclipse.emf.teneo; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Is used to throw runtime store exceptions. This class offers automatic logging to commons + * logging. Note that this class extends RuntimeException, so no forced throws and catch statements. + * Although there are very differing views on this topic but it is our experience that to many + * checked exceptions only distract the programmer and have no added value. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ + +public class TeneoException extends RuntimeException { + /** + * Serializable id + */ + private static final long serialVersionUID = 7433341056815136417L; + + /** + * The constructor, logs the exception also + */ + public TeneoException(String msg, Throwable cause) { + super(msg, cause); + + // and log it + getLogger().error(msg, cause); + } + + /** + * The constructor, logs the exception also + */ + public TeneoException(String msg) { + super(msg); + + // and log it + getLogger().error(msg, this); + } + + /** Get the class specific logger */ + private Log getLogger() { + return LogFactory.getLog(this.getClass()); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/StoreAnnotationsException.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/StoreAnnotationsException.java new file mode 100755 index 000000000..86fb963d2 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/StoreAnnotationsException.java @@ -0,0 +1,39 @@ +/** + * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights + * reserved. This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal </copyright> $Id: + * StoreAnnotationsException.java,v 1.4 2007/02/01 12:35:03 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations; + +import org.eclipse.emf.teneo.TeneoException; + +/** + * Is thrown in the org.eclipse.emf.teneo.annotations package. Takes care of logging the cause. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.2 $ + */ + +public class StoreAnnotationsException extends TeneoException { + /** + * Serial id + */ + private static final long serialVersionUID = 4685665979865102829L; + + /** + * The constructor, logs the exception also + */ + public StoreAnnotationsException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * The constructor, logs the exception also + */ + public StoreAnnotationsException(String msg) { + super(msg); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/AbstractAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/AbstractAnnotator.java new file mode 100755 index 000000000..a09855927 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/AbstractAnnotator.java @@ -0,0 +1,170 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: AbstractAnnotator.java,v 1.7 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.annotations.pannotation.PannotationFactory; +import org.eclipse.emf.teneo.extension.ExtensionInitializable; +import org.eclipse.emf.teneo.extension.ExtensionManager; +import org.eclipse.emf.teneo.extension.ExtensionManagerAware; +import org.eclipse.emf.teneo.mapping.strategy.EntityNameStrategy; +import org.eclipse.emf.teneo.mapping.strategy.SQLNameStrategy; +import org.eclipse.emf.teneo.mapping.strategy.StrategyUtil; + +/** + * The parent class of all annotator classes. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.7 $ + */ + +public abstract class AbstractAnnotator implements ExtensionManagerAware, ExtensionInitializable { + + protected PannotationFactory factory = PannotationFactory.eINSTANCE; + private ExtensionManager extensionManager; + private PAnnotatedModel annotatedModel; + private SQLNameStrategy sqlNameStrategy; + private EntityNameStrategy entityNameStrategy; + private PersistenceOptions persistenceOptions; + private EFeatureAnnotator eFeatureAnnotator; + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.extension.ExtensionInitializable#initializeExtension () + */ + public void initializeExtension() { + sqlNameStrategy = getExtensionManager().getExtension(SQLNameStrategy.class); + entityNameStrategy = getExtensionManager().getExtension(EntityNameStrategy.class); + } + + /** Method is called after all the important members have been set */ + protected void initialize() { + + } + + /** + * Returns the entity name of the eclass, note that in case of maps a different approach is + * followed (the entity name of the value is returned. + */ + public String getEntityName(EClass eClass) { + return StrategyUtil.getEntityName(entityNameStrategy, persistenceOptions, annotatedModel, eClass); + } + + /** + * @return the extensionManager + */ + public ExtensionManager getExtensionManager() { + return extensionManager; + } + + /** + * @param extensionManager + * the extensionManager to set + */ + public void setExtensionManager(ExtensionManager extensionManager) { + this.extensionManager = extensionManager; + } + + /** + * @return the annotatedModel + */ + public PAnnotatedModel getAnnotatedModel() { + return annotatedModel; + } + + /** + * @param annotatedModel + * the annotatedModel to set + */ + public void setAnnotatedModel(PAnnotatedModel annotatedModel) { + this.annotatedModel = annotatedModel; + } + + /** + * @return the factory + */ + public PannotationFactory getFactory() { + return factory; + } + + /** + * @param factory + * the factory to set + */ + public void setFactory(PannotationFactory factory) { + this.factory = factory; + } + + /** + * @return the sqlNameStrategy + */ + public SQLNameStrategy getSqlNameStrategy() { + return sqlNameStrategy; + } + + /** + * @return the entityNameStrategy + */ + public EntityNameStrategy getEntityNameStrategy() { + return entityNameStrategy; + } + + /** + * @return the persistenceOptions + */ + public PersistenceOptions getPersistenceOptions() { + return persistenceOptions; + } + + /** + * @param persistenceOptions + * the persistenceOptions to set + */ + public void setPersistenceOptions(PersistenceOptions persistenceOptions) { + this.persistenceOptions = persistenceOptions; + } + + /** Creates an annotator and sets all kinds of default info */ + protected <T extends AbstractAnnotator> T createAnnotator(Class<T> clz) { + final T annotator = getExtensionManager().getExtension(clz); + annotator.setAnnotatedModel(annotatedModel); + annotator.setExtensionManager(getExtensionManager()); + annotator.setPersistenceOptions(persistenceOptions); + annotator.setFactory(getFactory()); + annotator.initialize(); + return annotator; + } + + /** + * @return the eFeatureAnnotator + */ + public EFeatureAnnotator getEFeatureAnnotator() { + return eFeatureAnnotator; + } + + /** + * @param featureAnnotator + * the eFeatureAnnotator to set + */ + public void setEFeatureAnnotator(EFeatureAnnotator featureAnnotator) { + eFeatureAnnotator = featureAnnotator; + } + +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/AbstractProcessingContext.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/AbstractProcessingContext.java new file mode 100755 index 000000000..698859f60 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/AbstractProcessingContext.java @@ -0,0 +1,364 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: AbstractProcessingContext.java,v 1.10 2010/04/22 17:57:24 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.annotations.pannotation.AssociationOverride; +import org.eclipse.emf.teneo.annotations.pannotation.AttributeOverride; +import org.eclipse.emf.teneo.annotations.pannotation.Column; +import org.eclipse.emf.teneo.annotations.pannotation.JoinColumn; + +/** + * ProcessingContext which handles attributes overrides. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.10 $ + */ + +public class AbstractProcessingContext { + + /** The logger for all these exceptions */ + protected static final Log log = LogFactory + .getLog(AbstractProcessingContext.class); + + /** The current list of overrides */ + private Map<String, Object> currentOverrides = new HashMap<String, Object>(); + + /** + * Pushes the current overrides on the stack, to be popped later, this is to + * handle nested components + */ + private Stack<Map<String, Object>> overrideStack = new Stack<Map<String, Object>>(); + + /** + * Pushes the current embedding feature on the stack, to be popped later, + * this is to handle nested components and automatic renaming of props + */ + private Stack<PAnnotatedEStructuralFeature> embeddingFeatureStack = new Stack<PAnnotatedEStructuralFeature>(); + + /** + * Add attribute overrides, happens for each mapped superclass and each + * embedded component + */ + public void addAttributeOverrides(EList<AttributeOverride> aos) { + if (aos != null) { + for (AttributeOverride override : aos) { + currentOverrides.put(override.getName(), override.getColumn()); + } + } + } + + /** Add association overrides, for each mapped subclass */ + public void addAssociationOverrides(EList<AssociationOverride> overrides) { + if (overrides != null) { + for (AssociationOverride override : overrides) { + currentOverrides.put(override.getName(), override + .getJoinColumns()); + } + } + } + + /** + * Pushes the current overrides on the stack, to be popped later, this is to + * handle nested components + */ + public void pushOverrideOnStack() { + overrideStack.push(new HashMap<String, Object>(currentOverrides)); + } + + /** Pop the current overrides on the stack */ + public void popOverrideStack() { + currentOverrides = overrideStack.pop(); + } + + /** Pushes the current embedding feature on the stack */ + public void pushEmbeddingFeature(PAnnotatedEStructuralFeature er) { + embeddingFeatureStack.push(er); + } + + /** Pops the current embedding feature from the stack */ + public void popEmbeddingFeature() { + embeddingFeatureStack.pop(); + } + + /** Peeks for the current embedding feature */ + public PAnnotatedEStructuralFeature getEmbeddingFeature() { + if (embeddingFeatureStack.isEmpty()) { + return null; + } + return embeddingFeatureStack.peek(); + } + + /** Clear the override is done before an entity is processed */ + public void clearOverrides() { + currentOverrides.clear(); + } + + /** Return the overridden column for the passed attribute */ + public Column getAttributeOverride(PAnnotatedEStructuralFeature paFeature) { + return getAttributeOverride(paFeature.getModelElement().getName()); + } + + public Column getAttributeOverride(String featureName) { + return getAttributeOverride(featureName, -1); + } + + /** Return the overridden columns for the indicated featureName */ + public Column getAttributeOverride(String featureName, + int embeddingFeatureIndex) { + final Column c = (Column) currentOverrides.get(featureName); + if (c == null) { + final Object o = getFromStack(featureName); + if (o != null && o instanceof Column) { + return (Column) o; + } + // o == null, try one level deeper + if (embeddingFeatureIndex == -1 && !embeddingFeatureStack.isEmpty()) { + String newFeatureName = embeddingFeatureStack.peek() + .getModelElement().getName() + + "." + featureName; + return getAttributeOverride(newFeatureName, + embeddingFeatureStack.size() - 1); + } else if (embeddingFeatureIndex > 0) { + String newFeatureName = embeddingFeatureStack.get( + embeddingFeatureIndex - 1).getModelElement().getName() + + "." + featureName; + return getAttributeOverride(newFeatureName, + embeddingFeatureIndex - 1); + } + } + return c; + } + + /** Return the overridden JoinColumns for this reference */ + public List<JoinColumn> getAssociationOverrides( + PAnnotatedEReference paReference) { + return getAssociationOverrides(paReference.getModelEReference() + .getName()); + } + + public List<JoinColumn> getAssociationOverrides(String featureName) { + return getAssociationOverrides(featureName, -1); + } + + @SuppressWarnings("unchecked") + public List<JoinColumn> getAssociationOverrides(String featureName, + int embeddingFeatureIndex) { + final List<JoinColumn> jcs = (List<JoinColumn>) currentOverrides + .get(featureName); + if (jcs == null) { + final Object o = getFromStack(featureName); + if (o instanceof List<?>) { + return (List<JoinColumn>) o; + } + // o == null, try one level deeper + if (embeddingFeatureIndex == -1 && !embeddingFeatureStack.isEmpty()) { + String newFeatureName = embeddingFeatureStack.peek() + .getModelElement().getName() + + "." + featureName; + return getAssociationOverrides(newFeatureName, + embeddingFeatureStack.size() - 1); + } else if (embeddingFeatureIndex > 0) { + String newFeatureName = embeddingFeatureStack.get( + embeddingFeatureIndex - 1).getModelElement().getName() + + "." + featureName; + return getAssociationOverrides(newFeatureName, + embeddingFeatureIndex - 1); + } + } + return jcs; + } + + private Object getFromStack(String name) { + for (int i = (overrideStack.size() - 1); i >= 0; i--) { + final Map<String, Object> checkOverride = overrideStack.get(i); + final Object o = checkOverride.get(name); + if (o != null) { + return o; + } + } + return null; + } + + /** + * This method returns all inherited features which need to be added to the + * mapping of the aclass itself. The method makes a distinction makes a + * distinction between the first supertype (the first one in the list) and + * later ones. The features of the first type are only added to the mapping + * if the first type is a mappedsuperclass, in all other cases the features + * of the first type are not mapped in the aclass itself because they are + * inherited (the mapping describes the inheritance relation). For the other + * supertypes (located at index 1 and up in getESuperTypes) the features are + * mapped as properties in the class itself. The superEntity is the super + * aclass denoted as the real supertype extended by teneo. + */ + public List<PAnnotatedEStructuralFeature> getInheritedFeatures( + PAnnotatedEClass aClass) { + // if no supertypes then there are no inherited features + final EClass eclass = aClass.getModelEClass(); + if (eclass.getESuperTypes().size() == 0) { + return new ArrayList<PAnnotatedEStructuralFeature>(); + } + log.debug("Determining inherited features which are mapped locally for " + + aClass.getModelEClass().getName()); + final List<EStructuralFeature> inheritedFeatures = new ArrayList<EStructuralFeature>( + eclass.getEAllStructuralFeatures()); + + // remove all the features of the eclass itself + inheritedFeatures.removeAll(eclass.getEStructuralFeatures()); + + // check if the type has a supertype (a non-transient, + // non-mappedsuperclass, if so then + // remove all features inherited from the first supertype + // as this inheritance is done in the mapping file + if (aClass.getPaSuperEntity() != null) { + inheritedFeatures.removeAll(aClass.getPaSuperEntity() + .getModelEClass().getEAllStructuralFeatures()); + } + + // get all efeatures from direct mappedsuperclasses + // the id feature inherited from a direct mappedsuperclass should be + // maintained in other cases the id features are not mapped locally. + // The system can also ignore this and let the user be more carefull not + // to + // add id features here and there in the inheritance structure but this + // is + // more robust + removeIdFeatures(aClass, inheritedFeatures); + + // convert the result + final PAnnotatedModel paModel = aClass.getPaModel(); + final ArrayList<PAnnotatedEStructuralFeature> result = new ArrayList<PAnnotatedEStructuralFeature>(); + for (EStructuralFeature esf : inheritedFeatures) { + result.add(paModel.getPAnnotated(esf)); + } + + return result; + } + + /** + * Remove all id-features not inherited from a direct mapped superclass, and + * add the features from the mapped superclass + */ + private void removeIdFeatures(PAnnotatedEClass aClass, + List<EStructuralFeature> inheritedFeatures) { + // first get all the mapped superclasses + final ArrayList<EClass> mappedSuperEClasses = new ArrayList<EClass>(); + for (EClass superEClass : aClass.getModelEClass().getESuperTypes()) { + final PAnnotatedEClass superPAClass = aClass.getPaModel() + .getPAnnotated(superEClass); + if (superPAClass != null + && superPAClass.getMappedSuperclass() != null) { + mappedSuperEClasses.add(superPAClass.getModelEClass()); + } + } + + // now get all the efeatures of the mappedsuperclasses to prevent any id + // features from them being removed, only do that when the aclass does + // not + // have a real super type, in that case the id can be inherited from the + // mappedsuperclass + final ArrayList<EStructuralFeature> mappedSuperFeatures = new ArrayList<EStructuralFeature>(); + if (aClass.getPaSuperEntity() == null + || aClass.getPaSuperEntity().getMappedSuperclass() != null) { + for (EClass mappedSuperEClass : mappedSuperEClasses) { + mappedSuperFeatures.removeAll(mappedSuperEClass + .getEAllStructuralFeatures()); + mappedSuperFeatures.addAll(mappedSuperEClass + .getEAllStructuralFeatures()); + } + } + + // now remove all id features not coming from a direct mapped superclass + final ArrayList<EStructuralFeature> toRemove = new ArrayList<EStructuralFeature>(); + for (EStructuralFeature esf : inheritedFeatures) { + final PAnnotatedEStructuralFeature pef = aClass.getPaModel() + .getPAnnotated(esf); + + if (pef instanceof PAnnotatedEAttribute + && ((PAnnotatedEAttribute) pef).getId() != null + && !mappedSuperFeatures.contains(esf)) { + toRemove.add(esf); + } + } + inheritedFeatures.removeAll(toRemove); + } + + // + // /** Returns all mapped super classes */ + // public List<PAnnotatedEClass> getMappedSuperClasses(PAnnotatedEClass + // entity) { + // final List<PAnnotatedEClass> result = new ArrayList<PAnnotatedEClass>(); + // for (EClass superEClass : entity.getAnnotatedEClass().getESuperTypes()) { + // final PAnnotatedEClass superPAClass = entity.getPaModel() + // .getPAnnotated(superEClass); + // if (superPAClass != null + // && superPAClass.getMappedSuperclass() != null) { + // result.add(superPAClass); + // // and add the mapped super classes of the mapped superclass + // // note that only the unbroken chain of mappedsuperclasses is + // // added to the result, if there + // // is a non-mappedsuperclass in the inheritance then it stops + // // there + // // issue also identified by Douglas Bitting + // result.addAll(getMappedSuperClasses(superPAClass)); + // } + // } + // + // return result; + // } + + /** + * Returns true if the eclass only has mappedsuperclasses without id + * annotated property + */ + public boolean mustAddSyntheticID(PAnnotatedEClass entity) { + if (entity.hasIdAnnotatedFeature()) { + return false; + } + for (EClass superEClass : entity.getModelEClass().getEAllSuperTypes()) { + final PAnnotatedEClass superPAClass = entity.getPaModel() + .getPAnnotated(superEClass); + if (superPAClass != null + && superPAClass.getMappedSuperclass() == null) { + return false; + } else if (superPAClass != null + && superPAClass.getMappedSuperclass() != null) { + if (superPAClass.hasIdAnnotatedFeature()) { + return false; + } + } + } + + return true; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/AnnotationGenerator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/AnnotationGenerator.java new file mode 100755 index 000000000..a50720554 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/AnnotationGenerator.java @@ -0,0 +1,180 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: AnnotationGenerator.java,v 1.8 2010/03/02 21:43:57 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.teneo.Constants; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEDataType; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEPackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.annotations.pannotation.PannotationFactory; +import org.eclipse.emf.teneo.ecore.EModelResolver; +import org.eclipse.emf.teneo.extension.ExtensionManager; +import org.eclipse.emf.teneo.extension.ExtensionManagerAware; +import org.eclipse.emf.teneo.extension.ExtensionPoint; +import org.eclipse.emf.teneo.mapping.strategy.EntityNameStrategy; +import org.eclipse.emf.teneo.mapping.strategy.SQLNameStrategy; + +/** + * Adds default annotations to an existing pamodel. Default annotations are added on the basis of + * the emf type information. It sets the default annotations according to the ejb3 spec. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.8 $ + */ +public class AnnotationGenerator implements ExtensionPoint, ExtensionManagerAware { + + // The logger + protected static final Log log = LogFactory.getLog(AnnotationGenerator.class); + + protected PersistenceOptions persistenceOptions; + + // the extension manager + private ExtensionManager extensionManager; + + // Convenience link to pamodel factory + private final PannotationFactory aFactory = PannotationFactory.eINSTANCE; + + // The annotated model which is being processed + private PAnnotatedModel annotatedModel; + + // The annotators + private EClassAnnotator eClassAnnotator; + private EDataTypeAnnotator eDataTypeAnnotator; + + /** + * Adds default annotations to a pamodel, the method is synchronized because globals are set. + * Not necessary because this class should always be used single threaded but okay. + */ + public synchronized void map(PAnnotatedModel annotatedModel, PersistenceOptions po) { + + persistenceOptions = po; + + final List<PAnnotatedEPackage> apacks = annotatedModel.getPaEPackages(); + + final EPackage[] epacks = new EPackage[apacks.size()]; + int cnt = 0; + for (PAnnotatedEPackage apack : apacks) { + epacks[cnt++] = apack.getModelEPackage(); + } + + final EModelResolver eModelResolver = EModelResolver.instance(); + log.debug("Registering epackages in model resolver, modelresolver instance is: " + + eModelResolver.getClass().getName()); + eModelResolver.register(epacks); + + // if force fully classify typename then use the EModelResolver/ERuntime + if (persistenceOptions.isAlsoMapAsClass()) { + log.debug("Class names are to be fully classified, registering all the " + "epackages"); + // and now set the map as entity for each eclass + for (PAnnotatedEPackage apack : annotatedModel.getPaEPackages()) { + for (PAnnotatedEClass aclass : apack.getPaEClasses()) { + aclass.setOnlyMapAsEntity(!eModelResolver.hasImplementationClass(aclass.getModelEClass())); + } + } + } + + // solve a specific case of the EcorePackage going wrong for the + // eSuperTypes + // see bugzilla: https://bugs.eclipse.org/bugs/show_bug.cgi?id=205790 + for (EPackage epack : epacks) { + if (epack.getNsURI() != null && epack.getNsURI().compareTo(EcorePackage.eINSTANCE.getNsURI()) == 0) { + // now find the + for (EClassifier eClassifier : epack.getEClassifiers()) { + if (eClassifier.eClass() == EcorePackage.eINSTANCE.getEClass()) { + final EClass eClass = (EClass) eClassifier; + for (EStructuralFeature eFeature : eClass.getEAllStructuralFeatures()) { + if (eFeature.getName().compareTo("eSuperTypes") == 0) { + if (eFeature.getEAnnotation(Constants.ANNOTATION_SOURCE_TENEO_JPA) == null) { + EcoreUtil.setAnnotation(eFeature, Constants.ANNOTATION_SOURCE_TENEO_JPA, "value", "@ManyToMany"); + break; + } + } + } + } + } + } + } + + annotatedModel.setInitialized(true); + this.annotatedModel = annotatedModel; + + // initialize the strategies so they have the correct information + // TODO this should be handled in aware like interfaces + final EntityNameStrategy entityNameStrategy = extensionManager.getExtension(EntityNameStrategy.class); + entityNameStrategy.setPaModel(annotatedModel); // is maybe already set? + final SQLNameStrategy sqlNameStrategy = extensionManager.getExtension(SQLNameStrategy.class); + sqlNameStrategy.setPersistenceOptions(po); + + setAnnotators(); + for (PAnnotatedEPackage pae : annotatedModel.getPaEPackages()) { + processPackage(pae); + } + } + + /** Set the annotators */ + protected void setAnnotators() { + eClassAnnotator = createAnnotator(EClassAnnotator.class); + eDataTypeAnnotator = createAnnotator(EDataTypeAnnotator.class); + } + + /** Creates an annotator and sets all kinds of default info */ + private <T extends AbstractAnnotator> T createAnnotator(Class<T> clz) { + final T annotator = extensionManager.getExtension(clz); + annotator.setAnnotatedModel(annotatedModel); + annotator.setExtensionManager(extensionManager); + annotator.setPersistenceOptions(persistenceOptions); + annotator.setFactory(aFactory); + annotator.initialize(); + return annotator; + } + + /** Maps one epackage */ + protected void processPackage(PAnnotatedEPackage aPackage) { + log.debug(">>>> Adding default annotations for EPackage " + aPackage.getModelElement().getName()); + + log.debug("Processing EDataTypes"); + for (PAnnotatedEDataType annotatedEDataType : aPackage.getPaEDataTypes()) { + eDataTypeAnnotator.annotate(annotatedEDataType); + } + + log.debug("Processing EClasses"); + for (PAnnotatedEClass annotatedEClass : aPackage.getPaEClasses()) { + eClassAnnotator.annotate(annotatedEClass); + } + } + + /** + * @param extensionManager + * the extensionManager to set + */ + public void setExtensionManager(ExtensionManager extensionManager) { + this.extensionManager = extensionManager; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/BaseEFeatureAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/BaseEFeatureAnnotator.java new file mode 100755 index 000000000..b26d54445 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/BaseEFeatureAnnotator.java @@ -0,0 +1,336 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: BaseEFeatureAnnotator.java,v 1.17 2010/08/18 12:56:38 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.xml.type.XMLTypePackage; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.annotations.pannotation.CascadeType; +import org.eclipse.emf.teneo.annotations.pannotation.Column; +import org.eclipse.emf.teneo.annotations.pannotation.FetchType; +import org.eclipse.emf.teneo.annotations.pannotation.ForeignKey; +import org.eclipse.emf.teneo.annotations.pannotation.JoinColumn; +import org.eclipse.emf.teneo.annotations.pannotation.PAnnotation; +import org.eclipse.emf.teneo.annotations.pannotation.Temporal; +import org.eclipse.emf.teneo.annotations.pannotation.TemporalType; +import org.eclipse.emf.teneo.util.EcoreDataTypes; + +/** + * Placeholder for several utility methods which are relevant for annotating ereferences and eattributes. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.17 $ + */ + +public abstract class BaseEFeatureAnnotator extends AbstractAnnotator { + + // The logger + protected static final Log log = LogFactory.getLog(BaseEFeatureAnnotator.class); + + private int defaultVarCharLength = -1; + + /** Create a foreign key and set its name */ + protected ForeignKey createFK(PAnnotatedEStructuralFeature aFeature) { + final ForeignKey fk = getFactory().createForeignKey(); + fk.setName(getSqlNameStrategy().getForeignKeyName(aFeature)); + return fk; + } + + protected FetchType getFetch(PAnnotatedEClass aClass) { + return FetchType.EAGER; + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.annotations.mapper.AbstractAnnotator# setPersistenceOptions(org.eclipse + * .emf.teneo.PersistenceOptions) + */ + @Override + public void setPersistenceOptions(PersistenceOptions persistenceOptions) { + super.setPersistenceOptions(persistenceOptions); + + defaultVarCharLength = persistenceOptions.getDefaultVarCharLength(); + } + + /** + * Adds the column level constraints on the basis of the xsd extended meta data + */ + protected void addColumnConstraints(PAnnotatedEAttribute aAttribute) { + + // disabled because of bugzilla: + // 317479 +// if (aAttribute.getId() != null) { +// aAttribute.getModelEAttribute().setLowerBound(1); +// } + + final EAttribute eAttribute = aAttribute.getModelEAttribute(); + + // decide if a column annotation should be added, this is done + // when the maxLength or length, totalDigits or fractionDigits are set + // and when no other column has been set + if (aAttribute.getColumn() == null) { + String maxLength = getExtendedMetaData(eAttribute, "maxLength"); + if (maxLength == null) { + maxLength = getExtendedMetaData(eAttribute, "length"); + } + if (maxLength == null && defaultVarCharLength > 0) { + maxLength = "" + defaultVarCharLength; + } + final String totalDigits = getExtendedMetaData(eAttribute, "totalDigits"); + final String fractionDigits = getExtendedMetaData(eAttribute, "fractionDigits"); + boolean setUnique = false; + // bugzilla 249246 + if (getPersistenceOptions().isIDFeatureAsPrimaryKey() && eAttribute.isID() && aAttribute.getId() == null) { + if (aAttribute.getPaEClass().getPaSuperEntity() != null + && aAttribute.getPaEClass().getPaSuperEntity().getMappedSuperclass() == null) { + setUnique = true; + } + } + if (maxLength != null || setUnique || totalDigits != null || fractionDigits != null + || defaultVarCharLength > -1) { + final Column column = getFactory().createColumn(); + // only support this for the string class, the length/maxlength + // is also + // used in case of the xsd list/union types but this can not be + // enforced using a constraint on the + // columnlength + if (maxLength != null && eAttribute.getEAttributeType().getInstanceClass() != null + && eAttribute.getEAttributeType().getInstanceClass() == String.class) { + column.setLength(Integer.parseInt(maxLength)); // you'll + // find + // parse + // errors! + } + if (totalDigits != null) { + column.setPrecision(Integer.parseInt(totalDigits)); + } + if (fractionDigits != null) { + column.setScale(Integer.parseInt(fractionDigits)); + } + if (aAttribute.getBasic() != null) { + column.setNullable(aAttribute.getBasic().isOptional()); + } + if (setUnique) { + column.setUnique(true); + } + aAttribute.setColumn(column); + } + } else if (aAttribute.getBasic() != null && !aAttribute.getColumn().isSetNullable()) { + // bugzilla 226775 + aAttribute.getColumn().setNullable(aAttribute.getBasic().isOptional()); + } + + final Column c = aAttribute.getColumn(); + if (isStringType(aAttribute.getModelEAttribute()) && c != null && defaultVarCharLength > 0 && !c.isSetLength()) { + c.setLength(defaultVarCharLength); + } + + // disable unique constraint as the uniqueness is covered by the primary + // key + // constraints. See issue 280169 + if (c != null && aAttribute.getId() != null) { + c.setUnique(false); + } + } + + private boolean isStringType(EAttribute eAttribute) { + final Class<?> clz = eAttribute.getEAttributeType().getInstanceClass(); + if (clz != null && String.class.isAssignableFrom(clz)) { + return true; + } + if (eAttribute.getEAttributeType() == XMLTypePackage.eINSTANCE.getString()) { + return true; + } + if (eAttribute.getEAttributeType() == XMLTypePackage.eINSTANCE.getName_()) { + return true; + } + if (eAttribute.getEAttributeType() == XMLTypePackage.eINSTANCE.getNCName()) { + return true; + } + if (eAttribute.getEAttributeType() == XMLTypePackage.eINSTANCE.getToken()) { + return true; + } + if (eAttribute.getEAttributeType() == XMLTypePackage.eINSTANCE.getQName()) { + return true; + } + return false; + } + + /** Return a list of join columns */ + protected List<JoinColumn> getJoinColumns(List<String> names, boolean optional, boolean isUpdateInsertable, + PAnnotation pAnnotation) { + final List<JoinColumn> result = new ArrayList<JoinColumn>(); + for (String name : names) { + JoinColumn jc = getFactory().createJoinColumn(); + jc.setName(name); + jc.setNullable(optional); + jc.setUpdatable(isUpdateInsertable); + jc.setInsertable(isUpdateInsertable); + result.add(jc); + } + return result; + } + + protected String getTargetTypeName(PAnnotatedEAttribute aAttribute) { + return EcoreDataTypes.INSTANCE.getTargetTypeName(aAttribute); + } + + /** Get a specific extended metadate */ + protected String getExtendedMetaData(EAttribute eAttribute, String key) { + String value = EcoreDataTypes.INSTANCE.getEAnnotationValue(eAttribute, + "http:///org/eclipse/emf/ecore/util/ExtendedMetaData", key); + if (value == null) { + value = EcoreDataTypes.INSTANCE.getEAnnotationValue(eAttribute.getEAttributeType(), + "http:///org/eclipse/emf/ecore/util/ExtendedMetaData", key); + } + return value; + } + + /** Determines if mapped by should be set */ + protected boolean setMappedBy(EReference eReference) { + // only set in two way relation + // if has not been set on the other side (mappedtoFields) + // if not a containment relation, containment relations are handled + // differently + // the other side may neither be containment + final EReference eOpposite = eReference.getEOpposite(); + if (eOpposite == null) { + return false; + } + + final PAnnotatedEReference aOpposite = getAnnotatedModel().getPAnnotated(eOpposite); + if (aOpposite.getOneToOne() != null && aOpposite.getOneToOne().getMappedBy() != null) { + return false; + } + + return compareNames(eReference, eOpposite); + // && + // !eReference.isContainment() && !eOpposite.isContainment(); + } + + /** + * Determines where to place a certain annotation/characteristic, this is done by comparing names.. + */ + protected boolean compareNames(EReference here, EReference there) { + final String nameHere = here.eClass().getName() + here.getName(); + final String nameThere = there.eClass().getName() + there.getName(); + assert (nameHere.compareTo(nameThere) != 0); + return nameHere.compareTo(nameThere) > 0; + } + + /** + * Checks if the cascade should be set in the cascade list, is only done if the list is empty + */ + protected void setCascade(List<CascadeType> cascadeList, boolean isContainment) { + if (!cascadeList.isEmpty()) { + return; + } + + if (isContainment) { + if (getPersistenceOptions().isSetCascadeAllOnContainment()) { + cascadeList.add(CascadeType.ALL); + } else { + if (getPersistenceOptions().isSetCascadeRemoveOnContainment()) { + cascadeList.add(CascadeType.REMOVE); + } + if (getPersistenceOptions().isSetCascadeMergeOnContainment()) { + cascadeList.add(CascadeType.MERGE); + } + if (getPersistenceOptions().isSetCascadePersistOnContainment()) { + cascadeList.add(CascadeType.PERSIST); + } + if (getPersistenceOptions().isSetCascadeRefreshOnContainment()) { + cascadeList.add(CascadeType.REFRESH); + } + } + } else if (getPersistenceOptions().isSetCascadePolicyForNonContainment()) { + if (getPersistenceOptions().isSetCascadeMergeOnNonContainment()) { + cascadeList.add(CascadeType.MERGE); + } + if (getPersistenceOptions().isSetCascadePersistOnNonContainment()) { + cascadeList.add(CascadeType.PERSIST); + } + if (getPersistenceOptions().isSetCascadeRefreshOnNonContainment()) { + cascadeList.add(CascadeType.REFRESH); + } + } else { + cascadeList.add(CascadeType.MERGE); + cascadeList.add(CascadeType.PERSIST); + cascadeList.add(CascadeType.REFRESH); + } + } + + protected void setTemporal(PAnnotatedEAttribute aAttribute, TemporalType defaultTemporal) { + final EAttribute eAttribute = aAttribute.getModelEAttribute(); + Class<?> clazz = eAttribute.getEAttributeType().getInstanceClass(); + // clazz is hidden somewhere + if (clazz == null || Object.class.equals(clazz)) { + ArrayList<EClassifier> eclassifiers = EcoreDataTypes.INSTANCE.getItemTypes((EDataType) eAttribute + .getEType()); + for (EClassifier eclassifier : eclassifiers) { + if (eclassifier.getInstanceClass() != null) { + clazz = eclassifier.getInstanceClass(); + break; + } + } + } + + final EDataType eDataType = aAttribute.getModelEAttribute().getEAttributeType(); + if (clazz != null + && (Date.class.isAssignableFrom(clazz) || eDataType == XMLTypePackage.eINSTANCE.getDate() || eDataType == XMLTypePackage.eINSTANCE + .getDateTime())) { + final Temporal temporal = getFactory().createTemporal(); + if (eDataType == XMLTypePackage.eINSTANCE.getDate()) { + temporal.setValue(TemporalType.DATE); + } else if (eDataType == XMLTypePackage.eINSTANCE.getDateTime()) { + temporal.setValue(TemporalType.TIMESTAMP); + } else { + temporal.setValue(defaultTemporal); + } + aAttribute.setTemporal(temporal); + temporal.setEModelElement(eAttribute); + } else if (clazz != null + && (Calendar.class.isAssignableFrom(clazz) || eDataType == XMLTypePackage.eINSTANCE.getDate() || eDataType == XMLTypePackage.eINSTANCE + .getDateTime())) { + final Temporal temporal = getFactory().createTemporal(); + if (eDataType == XMLTypePackage.eINSTANCE.getDate()) { + temporal.setValue(TemporalType.DATE); + } else if (eDataType == XMLTypePackage.eINSTANCE.getDateTime()) { + temporal.setValue(TemporalType.TIMESTAMP); + } else { + temporal.setValue(defaultTemporal); + } + aAttribute.setTemporal(temporal); + temporal.setEModelElement(eAttribute); + } + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/BasicPamodelBuilder.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/BasicPamodelBuilder.java new file mode 100755 index 000000000..5ce949aa7 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/BasicPamodelBuilder.java @@ -0,0 +1,363 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * Davide Marchignoli + * </copyright> + * + * $Id: BasicPamodelBuilder.java,v 1.7 2009/07/27 22:09:46 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EModelElement; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEDataType; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEModelElement; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEPackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.annotations.pamodel.PamodelFactory; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Convience class for building a <code>PAnnotatedModel</code>. + * + * @author <a href="mailto:marchign at elver.org">Davide Marchignoli</a> + */ +public class BasicPamodelBuilder implements ExtensionPoint { + + private PAnnotatedModel target = null; + + /** + * Uses an empty freshly instantiated PAnnotatedModel as target. + */ + public BasicPamodelBuilder() { + setPAnnotatedModel(createPAnnotatedModel()); + } + + /** Create the pAnnotatedModel */ + public PAnnotatedModel createPAnnotatedModel() { + return PamodelFactory.eINSTANCE.createPAnnotatedModel(); + } + + /** + * Uses the given PAnnotatedMmodel as target. + */ + public BasicPamodelBuilder(PAnnotatedModel target) { + setPAnnotatedModel(target); + } + + /** + * Sets the target PAnnotatedModel + */ + public void setPAnnotatedModel(PAnnotatedModel target) { + this.target = target; + } + + /** + * @return the target model. + */ + public PAnnotatedModel getPAnnotatedModel() { + return target; + } + + /** + * @return a <code>PAnnotatedModelElement</code> assoiciated to the given <code>EModelElement</code>. The element is + * created only if not already present in the model. + */ + protected PAnnotatedEModelElement create(EModelElement eModelElement) { + PAnnotatedEModelElement paElement = target.getPAnnotated(eModelElement); + if (paElement == null) { + // Factor out actual model creation so that extensions can create + // their + // own model elements. + paElement = doCreate(eModelElement); + } + return paElement; + } + + /** + * @return A newly created PAnnotatedEModelElement. This method is only responsible for the actual creation (and + * initialization) of this object. No other logic should happen here. This allows subclasses to alter how + * objects are created. If you'd like to alter any other logic around the creation, you should override the + * <code>create</code> method(s). + * @throws AssertionError + */ + protected PAnnotatedEModelElement doCreate(EModelElement eModelElement) throws AssertionError { + final EClass eModelElementEClass = eModelElement.eClass(); + PAnnotatedEModelElement paElement; + switch (eModelElementEClass.getClassifierID()) { + case EcorePackage.EATTRIBUTE: + paElement = PamodelFactory.eINSTANCE.createPAnnotatedEAttribute(); + break; + case EcorePackage.EREFERENCE: + paElement = PamodelFactory.eINSTANCE.createPAnnotatedEReference(); + break; + case EcorePackage.ECLASS: + paElement = PamodelFactory.eINSTANCE.createPAnnotatedEClass(); + break; + case EcorePackage.EPACKAGE: + paElement = PamodelFactory.eINSTANCE.createPAnnotatedEPackage(); + break; + case EcorePackage.EENUM: + case EcorePackage.EDATA_TYPE: + paElement = PamodelFactory.eINSTANCE.createPAnnotatedEDataType(); + break; + default: + throw new AssertionError("Trying to build PAnnotatedEModelElement for a " + eModelElementEClass); + } + paElement.setModelElement((ENamedElement) eModelElement); + return paElement; + } + + /** + * @return a <code>PAnnotatedEPackage</code> associated to the given <code>EPackage</code> and adds it the model. + * <p> + * The <code>PAnnotatedEPackage</code> is created only if not already present in the model. + */ + public PAnnotatedEPackage pElement(EPackage ePackage) { + PAnnotatedEPackage pPackage = (PAnnotatedEPackage) create(ePackage); + if (pPackage.eContainer() == null) { + target.getPaEPackages().add(pPackage); + } + return pPackage; + } + + /** + * @return a <code>PAnnotatedEClass</code> associated to the given <code>EClass</code> and adds it the model. + * <p> + * The <code>PAnnotatedEClass</code> is created only if not already present in the model. + * <p> + * The operation may involve the creation of a <code>PAnnotatedEPackage</code> associated to the given + * <code>EClass</code> package. + */ + protected PAnnotatedEClass pElement(EClass eClass) { + PAnnotatedEClass pClass = (PAnnotatedEClass) create(eClass); + pElement(eClass.getEPackage()).getPaEClasses().add(pClass); + return pClass; + } + + /** + * @return a <code>PAnnotatedEStructuralFeature</code> associated to the given <code>EStructuralFeature</code> and + * adds it the model. + * <p> + * The <code>PAnnotatedEStructuralFeature</code> is created only if not already present in the model. + * <p> + * The operation may involve the creation of a <code>PAnnotatedEPackage</code> and a + * <code>PAnnotatedEClass</code>. + */ + protected PAnnotatedEModelElement pElement(EStructuralFeature eFeature) { + PAnnotatedEStructuralFeature pFeature = (PAnnotatedEStructuralFeature) create(eFeature); + pElement(eFeature.getEContainingClass()).getPaEStructuralFeatures().add(pFeature); + return pFeature; + } + + /** + * @return a <code>PAnnotatedEStructuralFeature</code> associated to the given <code>EStructuralFeature</code> and + * adds it the model. + * <p> + * The <code>PAnnotatedEStructuralFeature</code> is created only if not already present in the model. + * <p> + * The operation may involve the creation of a <code>PAnnotatedEPackage</code> and a + * <code>PAnnotatedEClass</code>. + */ + protected PAnnotatedEDataType pElement(EDataType eDataType) { + PAnnotatedEDataType pDataType = (PAnnotatedEDataType) create(eDataType); + pElement(eDataType.getEPackage()).getPaEDataTypes().add(pDataType); + return pDataType; + } + + /** + * @return a <code>PAnnotatedEModelElement</code> associated to the given <code>EModelElement</code> and adds it the + * model. + * @see #pElement(EPackage) + * @see #pElement(EClass) + * @see #pElement(EStructuralFeature) + */ + protected PAnnotatedEModelElement pElement(final EModelElement eElement) throws AssertionError { + PAnnotatedEModelElement pElement = null; + switch (eElement.eClass().getClassifierID()) { + case EcorePackage.EATTRIBUTE: + case EcorePackage.EREFERENCE: + pElement = pElement((EStructuralFeature) eElement); + break; + case EcorePackage.ECLASS: + pElement = pElement((EClass) eElement); + break; + case EcorePackage.EPACKAGE: + pElement = pElement((EPackage) eElement); + break; + case EcorePackage.EDATA_TYPE: + pElement = pElement((EDataType) eElement); + break; + default: + throw new AssertionError("Trying to build PAnnotatedEModelElement for a " + eElement.eClass()); + } + return pElement; + } + + /** + * Builds a <code>PAnnotatedEPackage</code> associated to the given <code>EPackage</code> (if such an + * <code>PAnnotatedEPackage</code> does not yet exists) and adds it to the target model. + */ + public void add(EPackage ePackage) { + pElement(ePackage); + } + + /** + * Builds a <code>PAnnotatedEClass</code> associated to the given <code>EClass</code> (if such an + * <code>PAnnotatedEClass</code> does not yet exists) and adds it to the target model. + * + * <p> + * The creation of a new <code>PAnnotatedEClass</code> may involve the creation of a <code>PAnnotatedEPackage</code> + * associated to the containing <code>EPackage</code> of the given class. + */ + public void add(EClass eClass) { + pElement(eClass); + } + + /** + * Add to the the target model a new <code>PAnnotatedEStructuralFeature</code> refering to the given + * EStructuralFeature. + * + * <p> + * A PAnnotatedEClass and a PAnnotatedEPackage for the containing EClass and EPackage are added if needed. + * + * <p> + * The added element have no annotations. Elements for which a corresponding PAnnotatedElement is already present in + * the target model are ignored. + */ + public void add(EStructuralFeature eFeature) { + pElement(eFeature); + } + + /** + * Add the given annotation to the given PAnnotatedEModelElement. + * + * @throws IllegalArgumentException + * if the given PAnnotation is not admitted for the given PAnnotatedEModelElement. protected void + * setPAnnotation(PAnnotatedEModelElement pElement, PAnnotation pAnnotation) { EReference pAnnotationRef + * = PamodelPackage.eINSTANCE .pAnnotationReference(pElement.eClass(), pAnnotation.eClass()); if + * (pAnnotationRef == null) throw new IllegalArgumentException("PAnnotation of type '" + + * pAnnotation.eClass() + "' does not apply to elements of type '" + pElement.eClass() + "'"); + * pElement.eSet(pAnnotationRef, pAnnotation); } + */ + + /** + * Add the given PAnnotation to the target model. + * + * <p> + * This operation may involve the addition to the model of a newly created PAnnotatedEModelElement for the + * PAnnotation EModelElement. + * + * @throws NullPointerException + * if either <code>pAnnotation</code> or <code>pAnnotation.getEModelElement()</code> are null. + * @throws IllegalArgumentException + * if the given <code>PAnnotation</code> references an invalid <code>PAnnotatedElement</code> public + * void add(PAnnotation pAnnotation) { PAnnotatedEModelElement pElement = + * pElement(pAnnotation.getEModelElement()); setPAnnotation(pElement, pAnnotation); } + */ + + /** + * Add to the the target model a new PAnnotatedPackage refering to the given EPackage. Recursively adds a + * PAnnotatedEClass for each EClass in the given EPackage (see {@link addEClass}). + * + * <p> + * The added elements have no annotations. Elements for which a corresponding PAnnotatedElement is already present + * in the target model are ignored. + */ + public void addRecurse(EPackage ePackage) { + PAnnotatedEPackage paPackage = pElement(ePackage); + for (EClassifier eClassifier : ePackage.getEClassifiers()) { + if (eClassifier instanceof EClass) { + addRecurse(paPackage, (EClass) eClassifier); + } else if (eClassifier instanceof EDataType) { + pElement((EDataType) eClassifier); + } + } + } + + /** + * used by {@link #addRecurse(EPackage)} to avoid recomputing the container multiple times. + */ + protected void addRecurse(PAnnotatedEPackage paPackage, EClass eClass) { + PAnnotatedEClass paClass = (PAnnotatedEClass) create(eClass); + if (paClass.eContainer() == null) { + paPackage.getPaEClasses().add(paClass); + } + for (EStructuralFeature eStructuralFeature : eClass.getEStructuralFeatures()) { + add(paClass, eStructuralFeature); + } + } + + /** + * Adds only this eClass and its EPackage to the pamodel + * + * @param eClass + */ + public void addSpecificEClass(EClass eClass) { + final EPackage ePackage = eClass.getEPackage(); + PAnnotatedEPackage paPackage = null; + for (PAnnotatedEPackage aPackage : target.getPaEPackages()) { + if (aPackage.getModelEPackage() == ePackage) { + paPackage = aPackage; + break; + } + } + if (paPackage == null) { + paPackage = pElement(ePackage); + addRecurse(paPackage, eClass); + } else { + boolean alreadyDefined = false; + for (PAnnotatedEClass paClass : paPackage.getPaEClasses()) { + if (paClass.getModelEClass() == eClass) { + alreadyDefined = true; + } + } + if (!alreadyDefined) { + addRecurse(paPackage, eClass); + } + } + } + + /** + * Add to the the target model a new PAnnotatedPackage refering to the given EClass. Recursively adds a + * PAnnotatedEStructuralFeature for each EStructuralFeature in the given EClass (see {@link addEStructuralFeature} + * ). + * + * <p> + * A PAnnotatedEPackage for the containng EPackage is added if needed. + * + * <p> + * The added elements have no annotations. + * + * <p> + * Elements for which a corresponding PAnnotatedElement is already present in the target model are ignored. public + * void addRecurse(EClass eClass) { addRecurse((PAnnotatedEPackage) pElement(eClass), eClass); } + */ + + /** + * used by {@link #addRecurse(EClass)} to avoid recomputing the container multiple times. + */ + protected void add(PAnnotatedEClass paClass, EStructuralFeature eFeature) { + PAnnotatedEStructuralFeature paFeature = (PAnnotatedEStructuralFeature) create(eFeature); + if (paFeature.eContainer() == null) { + paClass.getPaEStructuralFeatures().add(paFeature); + } + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/BidirectionalManyToManyAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/BidirectionalManyToManyAnnotator.java new file mode 100755 index 000000000..a5e321c3c --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/BidirectionalManyToManyAnnotator.java @@ -0,0 +1,133 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: BidirectionalManyToManyAnnotator.java,v 1.10 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.annotations.pannotation.JoinTable; +import org.eclipse.emf.teneo.annotations.pannotation.ManyToMany; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Annotates a bidirectional many-to-many ereference. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.10 $ + */ + +public class BidirectionalManyToManyAnnotator extends BaseEFeatureAnnotator implements ExtensionPoint { + + // The logger + protected static final Log log = LogFactory.getLog(BidirectionalManyToManyAnnotator.class); + + /** Process the features of the eclass */ + public void annotate(PAnnotatedEReference aReference) { + final String featureLogStr = + aReference.getModelEReference().getName() + "/" + + aReference.getModelEReference().getEContainingClass().getName(); + + if (aReference.getOneToMany() != null || aReference.getOneToOne() != null || aReference.getManyToOne() != null) { + throw new StoreMappingException("The feature/eclass " + featureLogStr + " should be a ManyToMany but " + + "it already has a OneToMany, OneToOne or ManyToOne annotation"); + } + + final EReference eReference = (EReference) aReference.getModelElement(); + final EReference eOpposite = eReference.getEOpposite(); + assert (eOpposite != null && eOpposite.isMany()); + + ManyToMany mtm = aReference.getManyToMany(); + final boolean mtmWasSet = mtm != null; // mtm was set manually + if (mtm == null) { + log.debug("Adding manytomany annotations to ereference: " + featureLogStr); + mtm = getFactory().createManyToMany(); + aReference.setManyToMany(mtm); + mtm.setEModelElement(eReference); + } else { + log.debug("ManyToMany present check if default information should be added"); + } + + if (eReference.isContainment() || getPersistenceOptions().isSetDefaultCascadeOnNonContainment()) { + setCascade(mtm.getCascade(), eReference.isContainment()); + } + + if (mtm.getTargetEntity() == null) { + mtm.setTargetEntity(getEntityName(eReference.getEReferenceType())); + } + + if (getPersistenceOptions().isSetForeignKeyNames() && aReference.getForeignKey() == null) { + aReference.setForeignKey(createFK(aReference)); + } + + // determine where to place the jointable annotation and where to place + // the mappedby + // use a certain logic to determine as each is only set on one side + // note that the join is always set on the other side of mapped by! + // note that we can not do setJoinHere = !setMappedByHere because there + // are situations + // that even for mtm no mappedby is set on either side, nl. in case of + // containment + + // also check if the other side has a (manual) manytomany with mappedby + // set + // bugzilla: 164808 + final PAnnotatedEReference otherPA = aReference.getPaModel().getPAnnotated(eOpposite); + if (mtm.getMappedBy() == null && setMappedBy(eReference) && + (otherPA.getManyToMany() == null || otherPA.getManyToMany().getMappedBy() == null)) { + mtm.setMappedBy(eOpposite.getName()); + } + + JoinTable joinTable = aReference.getJoinTable(); + if (joinTable == null) { + joinTable = getFactory().createJoinTable(); + aReference.setJoinTable(joinTable); + } + joinTable.setEModelElement(eReference); + + // set unique and indexed + // disabled because indexed = false now for mtm, + // to overcome this the user has to explicitly set a mtm annotation. + if (!mtmWasSet) { + log.debug("Setting indexed and unique from ereference.isOrdered/isUnique " + + "because mtm was not set manually!"); + mtm.setIndexed(!getPersistenceOptions().alwaysMapListAsBag() && eReference.isOrdered()); + } + + // NOTE that the ejb3 spec states that the jointable should be the + // concatenation of the + // tablenames of the owning entities with an underscore, this will + // quickly lead to nameclashes + // in the case there is more than one relation between two classes. This + // can be pretty likely + // if the inheritance strategy is single_table. + // now possibility to use a different naming strategy + if (joinTable.getName() == null) { + joinTable.setName(getSqlNameStrategy().getJoinTableName(aReference)); + } + if (joinTable.getJoinColumns().size() == 0) { + final List<String> names = getSqlNameStrategy().getJoinTableJoinColumns(aReference, false); + joinTable.getJoinColumns().addAll(getJoinColumns(names, false, true, mtm)); + } + if (joinTable.getInverseJoinColumns().size() == 0) { + final List<String> names = getSqlNameStrategy().getJoinTableJoinColumns(aReference, true); + joinTable.getInverseJoinColumns().addAll(getJoinColumns(names, false, true, mtm)); + } + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/EClassAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/EClassAnnotator.java new file mode 100755 index 000000000..9260e4b76 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/EClassAnnotator.java @@ -0,0 +1,414 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: EClassAnnotator.java,v 1.20 2009/10/31 07:10:35 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.StoreAnnotationsException; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.annotations.pannotation.Column; +import org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorColumn; +import org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorType; +import org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorValue; +import org.eclipse.emf.teneo.annotations.pannotation.Entity; +import org.eclipse.emf.teneo.annotations.pannotation.Inheritance; +import org.eclipse.emf.teneo.annotations.pannotation.InheritanceType; +import org.eclipse.emf.teneo.annotations.pannotation.PannotationFactory; +import org.eclipse.emf.teneo.annotations.pannotation.PrimaryKeyJoinColumn; +import org.eclipse.emf.teneo.annotations.pannotation.SecondaryTable; +import org.eclipse.emf.teneo.annotations.pannotation.Table; +import org.eclipse.emf.teneo.extension.ExtensionPoint; +import org.eclipse.emf.teneo.mapping.strategy.StrategyUtil; + +/** + * Sets the annotation on an eclass. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.20 $ + */ + +public class EClassAnnotator extends AbstractAnnotator implements ExtensionPoint { + + // The logger + protected static final Log log = LogFactory.getLog(EClassAnnotator.class); + + private InheritanceType optionDefaultInheritanceMapping = InheritanceType.SINGLE_TABLE; + + // The list of processed eclasses, is used to ensure that a superclass is + // done before a subclass + private final ArrayList<PAnnotatedEClass> processedAClasses = new ArrayList<PAnnotatedEClass>(); + + private EFeatureAnnotator eFeatureAnnotator = null; + + /** + * Returns the annotated version of an EClass, Returns false if no efeatures of this eclass should be annotated, + * true if its features can be annotated. + */ + protected boolean annotate(PAnnotatedEClass aClass) { + if (aClass == null) { + throw new StoreAnnotationsException( + "Mapping Exception, no Annotated Class for EClass, " + + "a common cause is that you did not register all EPackages in the DataStore/Helper Class. " + + "When there are references between EClasses in different EPackages then they need to be handled in one DataStore/Helper Class."); + } + + final EClass eclass = (EClass) aClass.getModelElement(); + + // check if already processed + if (processedAClasses.contains(aClass)) { + return false; + } + + // do not process the document root + if (!getPersistenceOptions().isMapDocumentRoot() && ExtendedMetaData.INSTANCE.isDocumentRoot(eclass)) { + return false; + } + + log.debug("Creating mapping for eclass " + eclass.getName()); + + // first do the superclasses + for (EClass superEclass : aClass.getModelEClass().getESuperTypes()) { + final PAnnotatedEClass superAClass = aClass.getPaModel().getPAnnotated(superEclass); + if (superAClass == null) { + throw new StoreAnnotationsException( + "Mapping Exception, no Annotated Class for EClass: " + + superEclass.getName() + + " a common cause is that you did not register all EPackages in the DataStore/Helper Class. " + + "When there are references between EClasses in different EPackages then they need to be handled in one DataStore/Helper Class."); + } + if (!processedAClasses.contains(superAClass)) { + annotate(superAClass); + if (superAClass.getEavMapping() != null) { + aClass.setEavMapping(PannotationFactory.eINSTANCE.createEAVMapping()); + } + } + } + + if (getPersistenceOptions().isEAVMapping() && aClass.getNoEAVMapping() == null) { + aClass.setEavMapping(PannotationFactory.eINSTANCE.createEAVMapping()); + } + + log.debug(" Adding default annotations for EClass: " + aClass.getModelElement().getName()); + + processedAClasses.add(aClass); + + log.debug("Setting the superentity of the eclass"); + setSuperEntity(aClass); + final boolean isInheritanceRoot = aClass.getPaSuperEntity() == null + || aClass.getPaSuperEntity().getMappedSuperclass() != null; // last + + // force single table + if (isInheritanceRoot && aClass.getEavMapping() != null) { + final Inheritance inheritance = PannotationFactory.eINSTANCE.createInheritance(); + inheritance.setStrategy(InheritanceType.SINGLE_TABLE); + aClass.setInheritance(inheritance); + } + + // A not mappable type will not get an entity annotation. + // Even the features of non-mappable types are mapped because + // the efeatures can be inherited through multiple inheritance + final boolean mappable = isMappableAnnotatedClass(aClass); + + // add entity or set entity name + if (mappable && aClass.getEntity() == null && aClass.getEmbeddable() == null) { + Entity entity = getFactory().createEntity(); + entity.setEModelElement(eclass); + aClass.setEntity(entity); + } + if (aClass.getEntity() != null && aClass.getEntity().getName() == null) { + aClass.getEntity().setName(getEntityNameStrategy().toEntityName(eclass)); + } + // if (aClass.getEavMapping() != null && EModelResolver.instance().getJavaClass(aClass.getModelEClass()) != + // null) { + // aClass.getEntity().setName(EModelResolver.instance().getJavaClass(aClass.getModelEClass()).getName()); + // } + + // get the inheritance from the supertype or use the global inheritance + // setting + // Note only an 'entitied' root gets an inheritance annotation. This is + // according to the spec. + final InheritanceType inheritanceType; + if (aClass.getInheritance() != null) { + inheritanceType = aClass.getInheritance().getStrategy(); + } else { + // get the inheritance from the supers, if defined there + final Inheritance inheritanceFromSupers = getInheritanceFromSupers(aClass); + inheritanceType = inheritanceFromSupers != null ? inheritanceFromSupers.getStrategy() + : optionDefaultInheritanceMapping; + // if this is the root then add a specific inheritance annotation + if (isInheritanceRoot) { + final Inheritance inheritance = getFactory().createInheritance(); + inheritance.setStrategy(inheritanceType); + inheritance.setEModelElement(eclass); + aClass.setInheritance(inheritance); + } + } + + // add PrimaryKeyJoinColumn in case of a joined + if (!isInheritanceRoot && inheritanceType.equals(InheritanceType.JOINED) + && aClass.getPrimaryKeyJoinColumns().size() == 0) { + ArrayList<String> idFeatures = new ArrayList<String>(); + PAnnotatedEClass aSuperClass = null; + for (EClass eSuperClass : aClass.getModelEClass().getESuperTypes()) { + aSuperClass = getAnnotatedModel().getPAnnotated(eSuperClass); + idFeatures.addAll(StrategyUtil.getIDFeaturesNames(aSuperClass, getPersistenceOptions() + .getDefaultIDFeatureName())); + if (!idFeatures.isEmpty()) { + break; + } + } + + for (String idFeature : idFeatures) { + final PrimaryKeyJoinColumn pkjc = getFactory().createPrimaryKeyJoinColumn(); + pkjc.setName(getSqlNameStrategy().getPrimaryKeyJoinColumnName(aSuperClass, idFeature)); + aClass.getPrimaryKeyJoinColumns().add(pkjc); + } + } + + // add the table annotation or the name annotation of the table + // only do this if this is the root in case of singletable or when this + // is the joined table strategy + if (aClass.getTable() == null + && ((isInheritanceRoot && inheritanceType.equals(InheritanceType.SINGLE_TABLE)) + || inheritanceType.equals(InheritanceType.JOINED) || inheritanceType + .equals(InheritanceType.TABLE_PER_CLASS))) { + final Table table = getFactory().createTable(); + table.setEModelElement(eclass); + // name is set in next step + aClass.setTable(table); + } + if (aClass.getTable() != null && aClass.getTable().getName() == null) { + aClass.getTable().setName(getSqlNameStrategy().getTableName(aClass)); + } + + if (addDiscriminator(aClass)) { + // For hibernate as well as jpox the discriminator column is only + // required for single table, the ejb3 spec does not make a clear + // statement about the requirement to also have a discriminator + // column for joined + if (isInheritanceRoot && aClass.getDiscriminatorColumn() == null + && inheritanceType.equals(InheritanceType.SINGLE_TABLE)) { + // note defaults of primitive types are all defined in the model + final DiscriminatorColumn dc = getFactory().createDiscriminatorColumn(); + dc.setEModelElement(eclass); + dc.setName(getSqlNameStrategy().getDiscriminatorColumnName()); + aClass.setDiscriminatorColumn(dc); + } + if (aClass.getDiscriminatorColumn() != null) { + if (aClass.getDiscriminatorColumn().getColumn() == null) { + final DiscriminatorColumn dc = aClass.getDiscriminatorColumn(); + final Column col = getFactory().createColumn(); + dc.setColumn(col); + col.setName(dc.getName()); + col.setIndex(aClass.getTable().getName() + dc.getName()); + col.setNullable(false); + } + if (aClass.getDiscriminatorColumn().getColumn().getName() == null) { + aClass.getDiscriminatorColumn().getColumn().setName(aClass.getDiscriminatorColumn().getName()); + } + } + + // add a discriminator value + if (aClass.getDiscriminatorValue() == null && inheritanceType.equals(InheritanceType.SINGLE_TABLE)) { + final DiscriminatorValue dv = getFactory().createDiscriminatorValue(); + + final DiscriminatorColumn dc = getDiscriminatorColumn(aClass); + if (dc != null && dc.getDiscriminatorType() != null + && dc.getDiscriminatorType().getValue() == DiscriminatorType.INTEGER_VALUE) { + + // use the entityname to translate to an int value, + // hopefully hashcode is more or less unique... + final String entityName = getEntityName(eclass); + log + .warn("Generating an integer discriminator value for entity " + + entityName + + ". The hashcode of the entityName is used as the discriminatorvalue. This may not be unique! To ensure uniques you should set a @DiscriminatorValue annotation"); + dv.setValue("" + entityName.hashCode()); + } else { + dv.setValue(getEntityName(eclass)); + } + dv.setEModelElement(eclass); + aClass.setDiscriminatorValue(dv); + } + } + + // Add default PkJoinColumns for SecondaryTables. + for (SecondaryTable secondaryTable : aClass.getSecondaryTables()) { + final EList<PrimaryKeyJoinColumn> pkJoinColumns = secondaryTable.getPkJoinColumns(); + if (pkJoinColumns.size() == 0) { + // No PkJoinColumns configured for this secondary table, so + // populate with defaults based on the ID + // attributes of the primary table. + final List<PAnnotatedEStructuralFeature> aIdFeatures = aClass.getPaIdFeatures(); + for (PAnnotatedEStructuralFeature idef : aIdFeatures) { + final PrimaryKeyJoinColumn pkJoinColumn = PannotationFactory.eINSTANCE.createPrimaryKeyJoinColumn(); + pkJoinColumn.setName(getSqlNameStrategy().getSecondaryTablePrimaryKeyJoinColumnName(idef)); + pkJoinColumns.add(pkJoinColumn); + } + } + } + + for (PAnnotatedEStructuralFeature aStructuralFeature : aClass.getPaEStructuralFeatures()) { + eFeatureAnnotator.annotate(aStructuralFeature); + } + return true; + } + + protected boolean addDiscriminator(PAnnotatedEClass aClass) { + return true; + } + + // finds the DiscriminatorColumn in the aClass or its super entities + protected DiscriminatorColumn getDiscriminatorColumn(PAnnotatedEClass aClass) { + if (aClass.getDiscriminatorColumn() != null) { + return aClass.getDiscriminatorColumn(); + } + + // or use aClass.getPaMappedSupers()? + if (aClass.getPaSuperEntity() != null) { + return getDiscriminatorColumn(aClass.getPaSuperEntity()); + } + return null; + } + + /** Sets the {@link EFeatureAnnotator} */ + @Override + protected void initialize() { + super.initialize(); + eFeatureAnnotator = createAnnotator(EFeatureAnnotator.class); + } + + /** + * Returns the inheritance of the passed annotated class or from one of its super annotated class + */ + protected Inheritance getInheritanceFromSupers(PAnnotatedEClass childPA) { + if (childPA == null) { + return null; + } + if (childPA.getInheritance() != null) { + return childPA.getInheritance(); + } + return getInheritanceFromSupers(childPA.getPaSuperEntity()); + } + + /** Set the super entity */ + protected void setSuperEntity(PAnnotatedEClass aClass) { + assert (aClass.getPaSuperEntity() == null); + final EClass eclass = aClass.getModelEClass(); + if (eclass.getESuperTypes().size() == 0) { + return; + } + // check for overridden using extends + if (aClass.getEntity() != null && aClass.getEntity().getExtends() != null) { + final EClass superEClass = aClass.getPaModel().getEClass(aClass.getEntity().getExtends()); + final PAnnotatedEClass superAClass = aClass.getPaModel().getPAnnotated(superEClass); + if (!processedAClasses.contains(superAClass)) { + annotate(superAClass); + } + aClass.setPaSuperEntity(superAClass); + return; + } + + final PAnnotatedEClass superAClass = aClass.getPaModel().getPAnnotated(eclass.getESuperTypes().get(0)); + if (superAClass.getEntity() != null || superAClass.getMappedSuperclass() != null) { + aClass.setPaSuperEntity(superAClass); + } + } + + /** Returns fals for jpox and true for hibernate */ + protected boolean isMappableAnnotatedClass(PAnnotatedEClass aClass) { + + final EClass eclass = aClass.getModelEClass(); + + if (!mapInterfaceEClass() && eclass.isInterface()) { + log.debug("Not mapping interfaces and this is an interface eclass, ignore it"); + return false; + } + + if (aClass.getTransient() != null) { + return false; // not mappable + } + + if (!getPersistenceOptions().isSetEntityAutomatically() && aClass.getEntity() == null + && aClass.getEmbeddable() == null) { + log.debug("Entities are not added automatically and this eclass: " + aClass.getModelEClass().getName() + + " does not have an entity/embeddable annotation."); + return false; + } + + // ignore these + if (!mapMappedSuperEClass() && aClass.getMappedSuperclass() != null) { + if (aClass.getEntity() != null) { + log + .warn("EClass " + + eclass.getName() + + " has entity as well as mappedsuperclass annotation, following mappedsuperclass annotation, therefore ignoring it for the mapping"); + } + return false; + } + + return true; + } + + /** + * Map Interface EClasses, default false, overridden by hibernate to return true + */ + protected boolean mapInterfaceEClass() { + return false; + } + + /** Map a mapped superclass, this differs for jpox and hibernate */ + protected boolean mapMappedSuperEClass() { + return true; + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.annotations.mapper.AbstractAnnotator# setPersistenceOptions(org.eclipse + * .emf.teneo.PersistenceOptions) + */ + @Override + public void setPersistenceOptions(PersistenceOptions persistenceOptions) { + super.setPersistenceOptions(persistenceOptions); + if (persistenceOptions.getInheritanceMapping() != null) { + InheritanceType it = InheritanceType.get(persistenceOptions.getInheritanceMapping()); + if (it == InheritanceType.JOINED) { + optionDefaultInheritanceMapping = InheritanceType.JOINED; + log.debug("Option inheritance: joined"); + } else if (it == InheritanceType.SINGLE_TABLE) { + optionDefaultInheritanceMapping = InheritanceType.SINGLE_TABLE; + log.debug("Option inheritance: single"); + } else if (it == InheritanceType.TABLE_PER_CLASS) { + optionDefaultInheritanceMapping = InheritanceType.TABLE_PER_CLASS; + log.debug("Option inheritance: table per class"); + } else { + throw new IllegalArgumentException("Inheritance mapping option: " + + persistenceOptions.getInheritanceMapping() + " is not supported"); + } + } + } + +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/EDataTypeAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/EDataTypeAnnotator.java new file mode 100755 index 000000000..2fe3c346a --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/EDataTypeAnnotator.java @@ -0,0 +1,39 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: EDataTypeAnnotator.java,v 1.6 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEDataType; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Annotates an EDataType, does nothing in this implementation. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.6 $ + */ + +public class EDataTypeAnnotator extends AbstractAnnotator implements ExtensionPoint { + + // The logger + protected static final Log log = LogFactory.getLog(EDataTypeAnnotator.class); + + /** Annotate it */ + public void annotate(PAnnotatedEDataType aDataType) { + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/EFeatureAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/EFeatureAnnotator.java new file mode 100755 index 000000000..a9ff50c5c --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/EFeatureAnnotator.java @@ -0,0 +1,272 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: EFeatureAnnotator.java,v 1.15 2010/07/15 07:46:30 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EAnnotation; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.teneo.Constants; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.annotations.pannotation.Transient; +import org.eclipse.emf.teneo.extension.ExtensionPoint; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * Sets the annotation on an efeature. In fact determines which efeature annotator to use (one-to-many, many-to-many + * etc.). + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.15 $ + */ + +public class EFeatureAnnotator extends AbstractAnnotator implements ExtensionPoint { + + // The logger + protected static final Log log = LogFactory.getLog(EFeatureAnnotator.class); + + // the annotators + protected OneToManyAttributeAnnotator otmAttributeAnnotator; + protected SingleAttributeAnnotator singleAttributeAnnotator; + protected BidirectionalManyToManyAnnotator bidirectionalManyToManyAnnotator; + protected UnidirectionalManyToManyAnnotator unidirectionalManyToManyAnnotator; + protected OneToManyReferenceAnnotator oneToManyReferenceAnnotator; + protected OneToOneReferenceAnnotator oneToOneReferenceAnnotator; + protected ManyToOneReferenceAnnotator manyToOneReferenceAnnotator; + + @Override + protected void initialize() { + super.initialize(); + otmAttributeAnnotator = createAnnotator(OneToManyAttributeAnnotator.class); + otmAttributeAnnotator.setEFeatureAnnotator(this); + singleAttributeAnnotator = createAnnotator(SingleAttributeAnnotator.class); + singleAttributeAnnotator.setEFeatureAnnotator(this); + bidirectionalManyToManyAnnotator = createAnnotator(BidirectionalManyToManyAnnotator.class); + bidirectionalManyToManyAnnotator.setEFeatureAnnotator(this); + unidirectionalManyToManyAnnotator = createAnnotator(UnidirectionalManyToManyAnnotator.class); + unidirectionalManyToManyAnnotator.setEFeatureAnnotator(this); + oneToManyReferenceAnnotator = createAnnotator(OneToManyReferenceAnnotator.class); + oneToManyReferenceAnnotator.setEFeatureAnnotator(this); + oneToOneReferenceAnnotator = createAnnotator(OneToOneReferenceAnnotator.class); + oneToOneReferenceAnnotator.setEFeatureAnnotator(this); + manyToOneReferenceAnnotator = createAnnotator(ManyToOneReferenceAnnotator.class); + manyToOneReferenceAnnotator.setEFeatureAnnotator(this); + } + + /** Process the features of the eclass */ + public void annotate(PAnnotatedEStructuralFeature aStructuralFeature) { + EStructuralFeature eStructuralFeature = aStructuralFeature.getModelEStructuralFeature(); + + boolean errorOccured = true; + try { + // a feature is transient if: + // - transient is true and it is an eattribute or + // - transient is true and it does not have an opposite + // - transietn is true and it's opposite is not a containment + // relation + // - it refers to an eclass which is transient + boolean isTransient = eStructuralFeature.isTransient() + && (eStructuralFeature instanceof EAttribute + || ((EReference) eStructuralFeature).getEOpposite() == null + || !((EReference) eStructuralFeature).getEOpposite().isContainment() || ((EReference) eStructuralFeature) + .getEOpposite().isTransient()); + + // check if the refered to eclass is transient if so then this + // efeature is + // also transient + if (!isTransient && eStructuralFeature instanceof EReference) { + final PAnnotatedEReference aReference = (PAnnotatedEReference) aStructuralFeature; + if (aReference.getTransient() != null) { + final Transient trans = getFactory().createTransient(); + trans.setEModelElement(eStructuralFeature); + aStructuralFeature.setTransient(trans); + } else if (hasTransientAnnotation(aReference.getEReferenceType())) { + final Transient trans = getFactory().createTransient(); + trans.setEModelElement(eStructuralFeature); + aStructuralFeature.setTransient(trans); + } else if (aReference.getAReferenceType() != null) { + isTransient = aReference.getAReferenceType().getTransient() != null; + } + } + + // don't do anything with the explicitly transient + if (aStructuralFeature.getTransient() != null) { + return; + } + + if (aStructuralFeature.getTransient() == null + && ((!mapVolitatileFeature() && eStructuralFeature.isVolatile()) || isTransient)) { + log.debug("Structural feature " + eStructuralFeature.getName() + + " is transient, therefore adding transient annotation"); + final Transient trans = getFactory().createTransient(); + trans.setEModelElement(eStructuralFeature); + aStructuralFeature.setTransient(trans); + } + + // process transients further because they can be part of a + // featuremap, the specific mapper should + // handle transient + // Note that this means that transient features will still have + // additional annotations such as basic etc. + // if (aStructuralFeature.getTransient() != null) return; + if (aStructuralFeature instanceof PAnnotatedEAttribute) { + final PAnnotatedEAttribute aAttribute = (PAnnotatedEAttribute) aStructuralFeature; + if (((PAnnotatedEAttribute) aStructuralFeature).getVersion() != null) { + return; + } + + final Class<?> instanceClass = eStructuralFeature.getEType().getInstanceClass(); + boolean isMany = false; + // instanceClass will be null for enums + // Lob-annotated attributes must not be treated as one-to-many. + // eattributes with a hibernate type annotations should not be + // treated as a list + if (instanceClass != null && aAttribute.getLob() == null) { + isMany = eStructuralFeature.isMany() || instanceClass.isArray() + || Collection.class.isAssignableFrom(instanceClass) + || Set.class.isAssignableFrom(instanceClass) || List.class.isAssignableFrom(instanceClass); + // note this causes a featuremap within a featuremap to get the + // basic annotation! + isMany = isMany && !StoreUtil.isElementOfAGroup(eStructuralFeature); + } + + if (isMany) { + otmAttributeAnnotator.annotate(aAttribute); + } else { + singleAttributeAnnotator.annotate(aAttribute); + } + + if (aAttribute.getColumn() != null && aAttribute.getColumn().getName() == null) { + aAttribute.getColumn().setName(getSqlNameStrategy().getColumnName(aAttribute, null)); + } + + } else if (aStructuralFeature instanceof PAnnotatedEReference) { + + final PAnnotatedEReference aReference = (PAnnotatedEReference) aStructuralFeature; + + // detect the type of relation + // note using the emf model it can not be checked if a relation + // is a + // uni-manytoone (2.1.8.3.2) or a uni onetoone (2.1.8.3.1) + // neither can a uni-manytomany (2.1.8.5.2) be detected + // because there is no eopposite. However this can be + // specified manually, the system as a default will choose + // uni-manytoone + + final EReference eReference = (EReference) aStructuralFeature.getModelElement(); + final EReference eOpposite = eReference.getEOpposite(); + + // elements of a group are never multi-occurence because the + // multi-occurence is + // handled by the containing featuremap + final boolean isMany = eReference.isMany() && !StoreUtil.isElementOfAGroup(eReference); + final boolean isOppositeMany = eOpposite != null && eOpposite.isMany() + && !StoreUtil.isElementOfAGroup(eOpposite); + + final boolean mtmBidirectionalRelation = isMany && eOpposite != null && isOppositeMany; + final boolean mtmUnidirectionalRelation = isMany && eOpposite == null + && aReference.getManyToMany() != null; + final boolean otmBidirectionalRelation = isMany && eOpposite != null && !isOppositeMany; + final boolean otmUnidirectionalRelation = isMany && eOpposite == null; + + // note as a default if the system has to choose between oto uni + // or mto uni then it will + // place a mto + final boolean otoBidirectionalRelation = aReference.getManyToOne() == null && !isMany + && eOpposite != null && !isOppositeMany; + final boolean otoUnidirectionalRelation = aReference.getManyToOne() == null && !isMany + && eOpposite == null + && (aReference.getOneToOne() != null || !aReference.getPrimaryKeyJoinColumns().isEmpty()); + final boolean mtoBidirectionalRelation = !isMany && eOpposite != null && isOppositeMany; + final boolean mtoUnidirectionalRelation = !isMany && eOpposite == null && !otoUnidirectionalRelation; + + if (mtmBidirectionalRelation) { + bidirectionalManyToManyAnnotator.annotate(aReference); + } else if (mtmUnidirectionalRelation) { + unidirectionalManyToManyAnnotator.annotate(aReference); + } else if (otmBidirectionalRelation || otmUnidirectionalRelation) { + oneToManyReferenceAnnotator.annotate(aReference); + } else if (aReference.getManyToOne() == null && (otoBidirectionalRelation || otoUnidirectionalRelation)) { + oneToOneReferenceAnnotator.annotate(aReference); + } else if (mtoBidirectionalRelation) { + manyToOneReferenceAnnotator.annotate(aReference); + } else if (mtoUnidirectionalRelation) { + manyToOneReferenceAnnotator.annotate(aReference); + } + + // handle column naming at this level + if (aReference.getColumn() != null && aReference.getColumn().getName() == null) { + aReference.getColumn().setName(getSqlNameStrategy().getColumnName(aReference, null)); + } + + } else { + throw new IllegalArgumentException("This type of StructuralFeature is not supported: " + + aStructuralFeature.getClass().getName()); + } + errorOccured = false; + } finally { + + // check that at least one ann was set + if (aStructuralFeature instanceof PAnnotatedEAttribute) { + PAnnotatedEAttribute pae = (PAnnotatedEAttribute) aStructuralFeature; + assert (errorOccured || pae.getBasic() != null || pae.getVersion() != null || pae.getId() != null + || pae.getTransient() != null || pae.getOneToMany() != null); + } else { + PAnnotatedEReference par = (PAnnotatedEReference) aStructuralFeature; + assert (errorOccured || par.getTransient() != null || par.getOneToMany() != null + || par.getManyToMany() != null || par.getManyToOne() != null || par.getOneToOne() != null); + } + } + } + + /** Map the feature if it is volatile, default is false */ + protected boolean mapVolitatileFeature() { + return false; + } + + /** + * @return the manyToOneReferenceAnnotator + */ + public ManyToOneReferenceAnnotator getManyToOneReferenceAnnotator() { + return manyToOneReferenceAnnotator; + } + + // checks for the presence of the @Transient annotation + // without requiring the refered class to be processed + private boolean hasTransientAnnotation(EClass eClass) { + final EAnnotation eAnnotation = eClass.getEAnnotation(Constants.ANNOTATION_SOURCE_TENEO_JPA); + if (eAnnotation == null) { + return false; + } + for (String value : eAnnotation.getDetails().values()) { + if (value.contains("@Transient")) { + return true; + } + } + return false; + } + +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/ManyToOneReferenceAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/ManyToOneReferenceAnnotator.java new file mode 100755 index 000000000..c372ef3f5 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/ManyToOneReferenceAnnotator.java @@ -0,0 +1,155 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: ManyToOneReferenceAnnotator.java,v 1.18 2010/03/25 00:12:45 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.annotations.pannotation.FetchType; +import org.eclipse.emf.teneo.annotations.pannotation.JoinColumn; +import org.eclipse.emf.teneo.annotations.pannotation.ManyToOne; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Annotates an ereference. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.18 $ + */ + +public class ManyToOneReferenceAnnotator extends BaseEFeatureAnnotator implements ExtensionPoint { + + // The logger + protected static final Log log = LogFactory.getLog(ManyToOneReferenceAnnotator.class); + + /** Annotate it */ + public void annotate(PAnnotatedEReference aReference) { + final String logStr = aReference.getModelEReference().getName() + "/" + + aReference.getModelEReference().getEContainingClass().getName(); + + if (aReference.getOneToMany() != null || aReference.getManyToMany() != null || aReference.getOneToOne() != null) { + throw new StoreMappingException("The feature/eclass " + logStr + " should be a ManyToOne but " + + "it already has a OneToMany, ManyToMany or OneToOne annotation"); + } + + final EReference eReference = (EReference) aReference.getModelElement(); + + ManyToOne mto = aReference.getManyToOne(); + if (mto == null) { + log.debug("EReference + " + logStr + " does not have a manytoone annotation, adding one"); + mto = getFactory().createManyToOne(); + aReference.setManyToOne(mto); + // removed unsettable because it is not used to define optional, it + // is used + // to allow distinction between the default value set or a feature + // which has not been + // set, this is used in validation + // mto.setOptional(!eReference.isRequired() || + // eReference.isUnsettable() || + // eReference.getEOpposite() != null); + mto.setOptional(!eReference.isRequired() || eReference.getEOpposite() != null || eReference.isUnsettable()); + mto.setEModelElement(eReference); + } else { + log.debug("EReference + " + logStr + " does have a manytoone annotation, using it"); + } + + if (!mto.isSetFetch()) { + mto.setFetch(getFetch(aReference.getAReferenceType())); + } + + if (eReference.isContainment() || getPersistenceOptions().isSetDefaultCascadeOnNonContainment()) { + setCascade(mto.getCascade(), eReference.isContainment()); + } + + // NOTE: Sometimes EMF generated getters/setters have a + // very generic type (EObject), if the type can be derived here then + // this should + // be added here + if (mto.getTargetEntity() == null) { + mto.setTargetEntity(getEntityName(eReference.getEReferenceType())); + } + + if (getPersistenceOptions().isSetForeignKeyNames() && aReference.getForeignKey() == null) { + aReference.setForeignKey(createFK(aReference)); + } + + if (getPersistenceOptions().isMapEmbeddableAsEmbedded() + && aReference.getAReferenceType().getEmbeddable() != null) { + aReference.setEmbedded(getFactory().createEmbedded()); + } + + // create a set of joincolumns, note that if this is a two-way relation + // then + // the other side will use the name of the ereference as second + // parameter, + // matching the joincolumns on the other side + if (aReference.getJoinColumns() == null || aReference.getJoinColumns().isEmpty()) { + if (aReference.getAReferenceType() != null) { + // == null if the reference is to a high level type such as an + // eobject + + // Set the join columns to not insertable/updatable if this is + // the many-to-one side + // of a bidirectional relation with a one-to-many list + // (indexed!) on the other side. + boolean hasJoinTable = false; + boolean isInsertableUpdatable = true; + if (eReference.getEOpposite() != null && !eReference.getEOpposite().isTransient()) { + final PAnnotatedEReference aOpposite = getAnnotatedModel().getPAnnotated(eReference.getEOpposite()); + + hasJoinTable = (!aOpposite.getModelEReference().isContainment() && getPersistenceOptions() + .isJoinTableForNonContainedAssociations()) + || aOpposite.getJoinTable() != null; + + if (!hasJoinTable && aOpposite.getOneToMany() != null && aOpposite.getOneToMany().isList() && !aOpposite.getOneToMany().getFetch().equals(FetchType.EXTRA)) { + isInsertableUpdatable = false; + } + // if the refered to is stored as an eav then do the update of the columns from here. + if (aReference.getAReferenceType().getEavMapping() != null) { + isInsertableUpdatable = true; + } + } + // old: + // isInsertableUpdatable = eReference.getEOpposite() == null || + // eReference.getEOpposite().isTransient() + + // NOTE that currently in all cases if there is an opposite + // Teneo assumes + // that it is managed from the other side. In reality this only + // needs to + // be done if the other side is indexed. + // NOTE: otm/mto with join table is not supported at the moment! + if (!hasJoinTable) { + final List<String> names = getSqlNameStrategy().getManyToOneJoinColumnNames(aReference); + aReference.getJoinColumns().addAll( + getJoinColumns(names, mto.isOptional(), isInsertableUpdatable, mto)); + } + } + } else { + // if nullable was not set explicitly then use the mto optional + // feature + for (JoinColumn jc : aReference.getJoinColumns()) { + if (!jc.isSetNullable()) { + jc.setNullable(mto.isOptional()); + } + } + } + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/OneToManyAttributeAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/OneToManyAttributeAnnotator.java new file mode 100755 index 000000000..3237024ba --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/OneToManyAttributeAnnotator.java @@ -0,0 +1,153 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: OneToManyAttributeAnnotator.java,v 1.11 2010/03/28 09:20:25 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.ecore.util.FeatureMapUtil; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; +import org.eclipse.emf.teneo.annotations.pannotation.CascadeType; +import org.eclipse.emf.teneo.annotations.pannotation.EnumType; +import org.eclipse.emf.teneo.annotations.pannotation.Enumerated; +import org.eclipse.emf.teneo.annotations.pannotation.FetchType; +import org.eclipse.emf.teneo.annotations.pannotation.JoinTable; +import org.eclipse.emf.teneo.annotations.pannotation.OneToMany; +import org.eclipse.emf.teneo.annotations.pannotation.TemporalType; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Annotates a one-to-many attribute (an eattribute with ismany=true), an + * example is a list of primitives (list of ints). + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.11 $ + */ + +public class OneToManyAttributeAnnotator extends BaseEFeatureAnnotator + implements ExtensionPoint { + + // The logger + protected static final Log log = LogFactory + .getLog(OneToManyAttributeAnnotator.class); + + private TemporalType optionDefaultTemporal = null; + + /** Process the features of the eclass */ + public void annotate(PAnnotatedEAttribute aAttribute) { + final String logStr = aAttribute.getModelEAttribute().getName() + + "/" + + aAttribute.getModelEAttribute().getEContainingClass() + .getName(); + + log.debug("EAttribute " + logStr + " needs a onetomany"); + + final EAttribute eAttribute = (EAttribute) aAttribute.getModelElement(); + + OneToMany otm = aAttribute.getOneToMany(); + final boolean otmWasSet = otm != null; // otm was set manually + if (otm == null) { + log.debug("One to many not present adding one"); + otm = getFactory().createOneToMany(); + aAttribute.setOneToMany(otm); + otm.setEModelElement(eAttribute); + + if (getPersistenceOptions().isFetchContainmentEagerly()) { + otm.setFetch(FetchType.EAGER); + } else if (getPersistenceOptions().isFetchAssociationExtraLazy()) { + otm.setFetch(FetchType.EXTRA); + } + } else { + log + .debug("One to many present adding default information if required"); + } + + if (getPersistenceOptions().isSetForeignKeyNames() + && aAttribute.getForeignKey() == null) { + aAttribute.setForeignKey(createFK(aAttribute)); + } + + // handle list of enums + if (eAttribute.getEType() instanceof EEnum + && aAttribute.getEnumerated() == null) { + final Enumerated enumerated = getFactory().createEnumerated(); + enumerated.setValue(EnumType.STRING); + enumerated.setEModelElement(eAttribute); + aAttribute.setEnumerated(enumerated); + } + + if (aAttribute.getTemporal() == null) { + setTemporal(aAttribute, optionDefaultTemporal); + } + + // set cascade if not set + if (otm.getCascade().isEmpty()) { + otm.getCascade().add(CascadeType.ALL); + } + + if (otm.getTargetEntity() == null) { + otm.setTargetEntity(getTargetTypeName(aAttribute)); + } + + if (aAttribute.getJoinTable() == null) { + // note not optional because lists of simple types are embedded + final JoinTable jt = getFactory().createJoinTable(); + jt.setName(getSqlNameStrategy().getJoinTableName(aAttribute)); + aAttribute.setJoinTable(jt); + } + + if (aAttribute.getJoinColumns().size() == 0) { + final List<String> names = getSqlNameStrategy() + .getOneToManyEAttributeJoinColumns(aAttribute); + aAttribute.getJoinColumns().addAll( + getJoinColumns(names, FeatureMapUtil + .isFeatureMap(eAttribute), true, otm)); + } + + // set unique and indexed + if (!otmWasSet) { + log + .debug("Setting indexed and unique on otm from eAttribute.isOrdered/isUnique " + + "because otm was not set manually"); + otm.setIndexed(eAttribute.isOrdered()); + otm.setUnique(eAttribute.isUnique()); + } + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.annotations.mapper.AbstractAnnotator# + * setPersistenceOptions(org.eclipse.emf.teneo.PersistenceOptions) + */ + @Override + public void setPersistenceOptions(PersistenceOptions persistenceOptions) { + super.setPersistenceOptions(persistenceOptions); + + optionDefaultTemporal = TemporalType.get(persistenceOptions + .getDefaultTemporalValue()); + if (optionDefaultTemporal == null) { + throw new StoreMappingException("Temporal value not found: " + + persistenceOptions.getDefaultTemporalValue()); + } + } + +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/OneToManyReferenceAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/OneToManyReferenceAnnotator.java new file mode 100755 index 000000000..722c2b177 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/OneToManyReferenceAnnotator.java @@ -0,0 +1,348 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: OneToManyReferenceAnnotator.java,v 1.20 2010/03/25 00:12:44 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.annotations.pannotation.FetchType; +import org.eclipse.emf.teneo.annotations.pannotation.JoinColumn; +import org.eclipse.emf.teneo.annotations.pannotation.JoinTable; +import org.eclipse.emf.teneo.annotations.pannotation.OneToMany; +import org.eclipse.emf.teneo.extension.ExtensionPoint; +import org.eclipse.emf.teneo.mapping.strategy.EntityNameStrategy; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * Annotates an ereference. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.20 $ + */ + +public class OneToManyReferenceAnnotator extends BaseEFeatureAnnotator + implements ExtensionPoint { + + // The logger + protected static final Log log = LogFactory + .getLog(OneToManyReferenceAnnotator.class); + + /** Annotate it */ + public void annotate(PAnnotatedEReference aReference) { + final String logStr = aReference.getModelEReference().getName() + + "/" + + aReference.getModelEReference().getEContainingClass() + .getName(); + + if (aReference.getManyToMany() != null + || aReference.getOneToOne() != null + || aReference.getManyToOne() != null) { + throw new StoreMappingException( + "The feature/eclass " + + logStr + + " should be a OneToMany but " + + "it already has a ManyToMany, OneToOne or ManyToOne annotation"); + } + + final EReference eReference = (EReference) aReference.getModelElement(); + OneToMany otm = aReference.getOneToMany(); + final boolean otmWasSet = otm != null; // otm was set manually + if (otm == null) { + log.debug("EReference + " + logStr + + " does not have a onetomany annotation, adding one"); + otm = getFactory().createOneToMany(); + aReference.setOneToMany(otm); + otm.setEModelElement(eReference); + + if (eReference.isContainment() + && getPersistenceOptions().isFetchContainmentEagerly()) { + otm.setFetch(FetchType.EAGER); + } else if (getPersistenceOptions().isFetchAssociationExtraLazy()) { + otm.setFetch(FetchType.EXTRA); + } + } else { + log.debug("EReference + " + logStr + + " has onetomany, check if defaults should be set"); + } + + // don't set mappedBy explicitly anymore + // mappedBy is not set anymore because it controls inverse + // see bugzilla 242479 + // if (otm.getMappedBy() == null && eReference.getEOpposite() != null) { + // otm.setMappedBy(eReference.getEOpposite().getName()); + // } + + if (getPersistenceOptions().isMapEmbeddableAsEmbedded() + && aReference.getAReferenceType().getEmbeddable() != null) { + aReference.setEmbedded(getFactory().createEmbedded()); + } + + if (getPersistenceOptions().isSetForeignKeyNames() + && aReference.getForeignKey() == null) { + // See bugzilla 211798: handle a specific case when this is a + // bidirectional + // one-to-many/many-to-one. In that case the foreign key name has to + // be + // the same on both sides and is set on the many-side. So use the + // annotated reference from the other side to ensure that the same + // foreign key name + // is used. + if (eReference.getEOpposite() != null + && !eReference.getEOpposite().isMany() + && !eReference.getEOpposite().isTransient()) { + final PAnnotatedEReference aOpposite = aReference.getPaModel() + .getPAnnotated(eReference.getEOpposite()); + if (aOpposite != null && aOpposite.getTransient() == null) { + // don't do anything as otherwise hibernate will create two + // fk's with the same name + + // if (aOpposite.getForeignKey() != null) { + // final ForeignKey fk = getFactory().createForeignKey(); + // fk.setName(aOpposite.getForeignKey().getName()); + // aReference.setForeignKey(fk); + // } else { + // aReference.setForeignKey(createFK(aOpposite)); + // } + } else { + aReference.setForeignKey(createFK(aReference)); + } + } else { + aReference.setForeignKey(createFK(aReference)); + } + } + + if (eReference.isContainment() + || getPersistenceOptions() + .isSetDefaultCascadeOnNonContainment()) { + setCascade(otm.getCascade(), eReference.isContainment()); + } + + // handle a special case, an emap which is mapped as a real map and + // which has an + // enumerate as the key + // Disabled for now as the hibernate map-key does not support enumerates + // as the type + // for the key when mapping as a true map + // if (false && StoreUtil.isMap(eReference) && + // getPersistenceOptions().isMapEMapAsTrueMap()) { + // final EStructuralFeature keyFeature = + // aReference.getEReferenceType().getEStructuralFeature("key"); + // if (keyFeature instanceof EAttribute) { + // final EAttribute keyAttribute = (EAttribute) keyFeature; + // final PAnnotatedEAttribute aKeyAttribute = + // aReference.getPaModel().getPAnnotated(keyAttribute); + // if (keyAttribute.getEType() instanceof EEnum && + // aKeyAttribute.getEnumerated() == null) { + // final Enumerated enumerated = getFactory().createEnumerated(); + // enumerated.setValue(EnumType.STRING); + // enumerated.setEModelElement(keyAttribute); + // aKeyAttribute.setEnumerated(enumerated); + // } + // } + // } + + // NOTE Sometimes EMF generated getters/setters have a + // very generic type (EObject), if the type can be derived here then + // this should + // be added here + if (otm.getTargetEntity() == null) { + otm.setTargetEntity(getEntityName(eReference.getEReferenceType())); + } + + // set unique and indexed + if (!otmWasSet) { + log + .debug("Setting indexed and unique from ereference because otm was not set manually!"); + // note force a join table in case of idbag! + otm.setIndexed(!getPersistenceOptions().alwaysMapListAsBag() + && !getPersistenceOptions().alwaysMapListAsIdBag() + && eReference.isOrdered() + && aReference.getOrderBy() == null); + // in case of containment it is always unique + // in case optionidbag then ignore the unique attribute on the + // ereference + otm + .setUnique(eReference.isContainment() + || (!getPersistenceOptions().alwaysMapListAsIdBag() && eReference + .isUnique())); + + if (aReference.getModelEReference().getEOpposite() != null) { + log + .debug("Setting unique because is bidirectional (has eopposite) otm"); + otm.setUnique(true); + } + } else if (!otm.isUnique() && !eReference.isUnique() + && aReference.getModelEReference().getEOpposite() != null) { + log + .warn("The EReference " + + logStr + + " is not unique (allows duplicates) but it is bi-directional, this is not logical"); + } + + // only use a jointable if the relation is non unique + final boolean isEObject = EntityNameStrategy.EOBJECT_ECLASS_NAME + .compareTo(otm.getTargetEntity()) == 0; + // in case of eobject always a join table is required + boolean mapJoinTable = aReference.getJoinTable() != null + || isEObject + || (getPersistenceOptions() + .isJoinTableForNonContainedAssociations() && !eReference + .isContainment()) || !otm.isUnique(); + + // also always map join table if the one refered to is an EAV + mapJoinTable |= (aReference.getAReferenceType() != null && aReference + .getAReferenceType().getEavMapping() != null); + + if (mapJoinTable) { + JoinTable joinTable = aReference.getJoinTable(); + if (joinTable == null) { + joinTable = getFactory().createJoinTable(); + aReference.setJoinTable(joinTable); + } + joinTable.setEModelElement(eReference); + + // see remark in manytomany about naming of jointables + if (joinTable.getName() == null) { + joinTable.setName(getSqlNameStrategy().getJoinTableName( + aReference)); + } + + // note joincolumns in jointable can be generated automatically by + // hib/jpox. need to explicitly do this in case of + // composite id + if (joinTable.getJoinColumns().size() == 0) { + final List<String> names = getSqlNameStrategy() + .getJoinTableJoinColumns(aReference, false); + joinTable.getJoinColumns().addAll( + getJoinColumns(names, false, true, otm)); + } + if (joinTable.getInverseJoinColumns().size() == 0 + && aReference.getAReferenceType() != null) { + final List<String> names = getSqlNameStrategy() + .getJoinTableJoinColumns(aReference, true); + // todo: should the inverse join columns not be + joinTable.getInverseJoinColumns().addAll( + getJoinColumns(names, false, true, otm)); + } + } else if (aReference.getJoinColumns() == null + || aReference.getJoinColumns().isEmpty()) { // add + boolean borrowJoinColumnsOtherSide = false; + + final EReference eOther = getOpposite(aReference); + if (eOther != null) { + final PAnnotatedEReference aOther = aReference.getPaModel() + .getPAnnotated(eOther); + + // map the other side, before checking if there are joincolumns + getEFeatureAnnotator().getManyToOneReferenceAnnotator() + .annotate(aOther); + + if (aOther.getJoinColumns() != null + && !aOther.getJoinColumns().isEmpty()) { + borrowJoinColumnsOtherSide = true; + for (JoinColumn jc : aOther.getJoinColumns()) { + aReference.getJoinColumns().add( + (JoinColumn) EcoreUtil.copy(jc)); + } + // repair updatable/insertable + for (JoinColumn jc : aReference.getJoinColumns()) { + jc.setUpdatable(true); + jc.setInsertable(true); + } + } + } + if (!borrowJoinColumnsOtherSide) { + final List<String> names = getSqlNameStrategy() + .getOneToManyEReferenceJoinColumns(aReference); + aReference.getJoinColumns().addAll( + getJoinColumns(names, aReference.getEmbedded() == null, + true, otm)); + } + + // In case of a bidirectional relation without a join table + // do a special thing: if this is a list (with an index) then the + // association is always + // managed from this side of the relation. This means that + // update/insert of the + // joincolumns + // on the other side is set to false. + // See the hibernate manual: 6.3.3. Bidirectional associations with + // indexed collections + boolean thisEAVMapped = aReference.getPaEClass().getEavMapping() != null; + if (otm.isList() && eOther != null && !thisEAVMapped) { + final PAnnotatedEReference aOpposite = getAnnotatedModel() + .getPAnnotated(eOther); + if (aReference.getTransient() == null) { + if (aOpposite.getJoinColumns().size() > 0) { + for (JoinColumn jc : aOpposite.getJoinColumns()) { + if (otm.getFetch().equals(FetchType.EXTRA)) { + jc.setInsertable(true); + jc.setUpdatable(true); + } else { + jc.setInsertable(false); + jc.setUpdatable(false); + } + } + } + } + } + } + } + + protected EReference getOpposite(PAnnotatedEReference aReference) { + final EReference eReference = (EReference) aReference.getModelElement(); + if (eReference.getEOpposite() != null) { + return eReference.getEOpposite(); + } + + // now handle a special case, the aReference is a map + // and there is a mapped by and a one to many + if (aReference.getOneToMany() == null + || aReference.getOneToMany().getMappedBy() == null) { + return null; + } + + final EClass eclass = eReference.getEReferenceType(); + if (getPersistenceOptions().isMapEMapAsTrueMap() + && StoreUtil.isMapEntry(eclass)) { + EStructuralFeature feature = eclass.getEStructuralFeature("value"); + if (feature instanceof EReference) { + final String mappedBy = aReference.getOneToMany().getMappedBy(); + final EReference valueERef = (EReference) feature; + final EClass valueEClass = valueERef.getEReferenceType(); + final EStructuralFeature ef = valueEClass + .getEStructuralFeature(mappedBy); + if (ef == null || ef instanceof EAttribute) { + return null; + } + + return (EReference) ef; + } else { + return null; + } + } else { + return null; + } + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/OneToOneReferenceAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/OneToOneReferenceAnnotator.java new file mode 100755 index 000000000..f9f8c406c --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/OneToOneReferenceAnnotator.java @@ -0,0 +1,123 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: OneToOneReferenceAnnotator.java,v 1.11 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.annotations.pannotation.OneToOne; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Annotates an ereference. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.11 $ + */ + +public class OneToOneReferenceAnnotator extends BaseEFeatureAnnotator implements ExtensionPoint { + + // The logger + protected static final Log log = LogFactory.getLog(OneToOneReferenceAnnotator.class); + + /** Annotate it */ + public void annotate(PAnnotatedEReference aReference) { + final String logStr = + aReference.getModelEReference().getName() + "/" + + aReference.getModelEReference().getEContainingClass().getName(); + + if (aReference.getOneToMany() != null || aReference.getManyToMany() != null || + aReference.getManyToOne() != null) { + throw new StoreMappingException("The feature/eclass " + logStr + " should be a OneToOne but " + + "it already has a OneToMany, ManyToMany or ManyToOne annotation"); + } + + final EReference eReference = (EReference) aReference.getModelElement(); + + OneToOne oto = aReference.getOneToOne(); + if (oto == null) { + log.debug("EReference + " + logStr + " does not have a onetoone annotation, adding one"); + oto = getFactory().createOneToOne(); + aReference.setOneToOne(oto); + // removed unsettable because it is not used to define optional, it + // is used + // to allow distinction between the default value set or a feature + // which has not been + // set, this is used in validation + // oto.setOptional(!eReference.isRequired() || + // eReference.isUnsettable()); + oto.setOptional(!eReference.isRequired()); + oto.setEModelElement(eReference); + } else { + log.debug("EReference + " + logStr + " has an onetoone annotation setting defaults if required"); + } + + if (!oto.isSetFetch()) { + oto.setFetch(getFetch(aReference.getAReferenceType())); + } + + // determine where to put the mapped-by + if (oto.getMappedBy() == null && setMappedBy(eReference)) { + oto.setMappedBy(eReference.getEOpposite().getName()); + } + + if (getPersistenceOptions().isSetForeignKeyNames() && aReference.getForeignKey() == null) { + // See bugzilla 211798: handle a specific case when this is a + // bidirectional + // association. In that case the foreign key name has to be + // the same on both sides and is set on the many-side. So use the + // annotated reference from the other side to ensure that the same + // foreign key name + // is used. + if (eReference.getEOpposite() != null && !eReference.getEOpposite().isTransient()) { + final PAnnotatedEReference aOpposite = aReference.getPaModel().getPAnnotated(eReference.getEOpposite()); + if (aOpposite != null && aOpposite.getTransient() == null) { + // don't do anything as otherwise hibernate will create two + // fk's with the same name + + // if (aOpposite.getForeignKey() != null) { + // final ForeignKey fk = getFactory().createForeignKey(); + // fk.setName(aOpposite.getForeignKey().getName()); + // aReference.setForeignKey(fk); + // } else { + // aReference.setForeignKey(createFK(aOpposite)); + // } + } else { + aReference.setForeignKey(createFK(aReference)); + } + } else { + aReference.setForeignKey(createFK(aReference)); + } + } + + setCascade(oto.getCascade(), eReference.isContainment()); + + if (getPersistenceOptions().isMapEmbeddableAsEmbedded() && + aReference.getAReferenceType().getEmbeddable() != null) { + aReference.setEmbedded(getFactory().createEmbedded()); + } + + // Note: Sometimes EMF generated getters/setters have a + // very generic type (EObject), if the type can be derived here then + // this should + // be added here + if (oto.getTargetEntity() == null) { + oto.setTargetEntity(getEntityName(eReference.getEReferenceType())); + } + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/PersistenceFileProvider.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/PersistenceFileProvider.java new file mode 100644 index 000000000..d851a8e98 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/PersistenceFileProvider.java @@ -0,0 +1,50 @@ +/** + * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights + * reserved. This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal </copyright> $Id: + * PersistenceMappingBuilder.java,v 1.10 2007/02/08 23:12:35 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.io.InputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Provides files to the mapping as well as to the runtime layers. It can be customized with an own + * implementation by replacing the class in the ExtensionManager. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.3 $ + */ +public class PersistenceFileProvider implements ExtensionPoint { + + /** The logger */ + protected static final Log log = LogFactory.getLog(PersistenceFileProvider.class); + + /** + * Returns an InputStream with the file content, note if the file does not exist then null may + * be returned. This implementation searches for the file in the classpath using the path + * parameters. + * + * Custom implementations of this class may use any other method to find the file. + * + * @param clz + * the class to use when reading the file through a classloader + * @param path + * the path to the file (incl. the filename and extension) + * + * @return an InputStream if found, or null otherwise + */ + public InputStream getFileContent(Class<?> clz, String path) { + if (clz == null) { + return this.getClass().getClassLoader().getResourceAsStream(path); + } else { + return clz.getResourceAsStream(path); + } + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/PersistenceMappingBuilder.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/PersistenceMappingBuilder.java new file mode 100755 index 000000000..f60660343 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/PersistenceMappingBuilder.java @@ -0,0 +1,296 @@ +/** + * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights + * reserved. This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal </copyright> $Id: + * PersistenceMappingBuilder.java,v 1.10 2007/02/08 23:12:35 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.ecore.xml.type.XMLTypePackage; +import org.eclipse.emf.teneo.PackageRegistryProvider; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.StoreAnnotationsException; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEDataType; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEPackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.annotations.parser.EAnnotationParserImporter; +import org.eclipse.emf.teneo.annotations.xml.XmlPersistenceMapper; +import org.eclipse.emf.teneo.extension.ExtensionManager; +import org.eclipse.emf.teneo.extension.ExtensionPoint; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * Receives a list of ecore files and generates a mapping model using different strategies. The mapping model is + * returned. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.14 $ + */ +public class PersistenceMappingBuilder implements ExtensionPoint { + + /** The logger */ + protected static final Log log = LogFactory.getLog(PersistenceMappingBuilder.class); + + /** The instance to use */ + public static final PersistenceMappingBuilder INSTANCE = new PersistenceMappingBuilder(); + + /** + * Receives a list of ecore files and returns a Mapping + */ + public PAnnotatedModel buildMapping(String[] ecoreFiles, PersistenceOptions po, ExtensionManager extensionManager) { + return buildMapping(StoreUtil.readEPackages(ecoreFiles), po, extensionManager); + } + + /** + * Builds a persistence mapping for one or more epackages + * + * @Deprecated use the method with the List<EPackage> parameter + */ + public PAnnotatedModel buildMapping(EPackage[] epackages, PersistenceOptions po, ExtensionManager extensionManager) { + return buildMapping(Arrays.asList(epackages), po, extensionManager); + } + + /** + * Builds a persistence mapping for one or more epackages + */ + public PAnnotatedModel buildMapping(List<EPackage> epacks, PersistenceOptions po, ExtensionManager extensionManager) { + // read the subepackages + List<EPackage> epackages = new ArrayList<EPackage>(); + for (EPackage epack : epacks) { + resolveSubPackages(epack, epackages); + } + + if (po.isAutoAddReferencedEPackages()) { + final List<EPackage> allEPackages = new ArrayList<EPackage>(); + for (EPackage ePackage : epackages) { + addAllUsedEPackages(ePackage, allEPackages); + } + if (epackages.contains(EcorePackage.eINSTANCE)) { + allEPackages.add(EcorePackage.eINSTANCE); + } + if (epackages.contains(XMLTypePackage.eINSTANCE)) { + allEPackages.add(XMLTypePackage.eINSTANCE); + } + epackages = allEPackages; + } + + // DCB: Introduce indirection so that extensions to annotation + // processing mechanism + // can provide their own model builder. + BasicPamodelBuilder pamodelBuilder = extensionManager.getExtension(BasicPamodelBuilder.class); + log.debug("Creating pamodel for the following epackages"); + for (EPackage element : epackages) { + log.debug(element.getName()); + pamodelBuilder.addRecurse(element); + } + + if (po.isMapDocumentRoot()) { + // use the ecore package which is present in the package registry + final EPackage ecorePackage = PackageRegistryProvider.getInstance().getPackageRegistry().getEPackage( + EcorePackage.eNS_URI); + final EClassifier eClassifier = ecorePackage.getEClassifier(EcorePackage.eINSTANCE + .getEStringToStringMapEntry().getName()); + pamodelBuilder.addSpecificEClass((EClass) eClassifier); + } + + log.debug("Create base pannotated model"); + PAnnotatedModel pam = pamodelBuilder.getPAnnotatedModel(); + + log.debug("Deprecated eannotations with http://annotations.elver.org or http://ejb.elver.org are ignored."); + // if (po.isIgnoreEAnnotations()) { + // log.debug("Ignoring eannotations"); + // } else { + // log.debug("Import eannotations"); + // // DCB: Introduce indirection so that extensions to annotation + // processing mechanism + // // can provide their own model builder. + // EannotationPamodelBuilder epb = getAnnotationModelBuilder(); + // epb.setPAnnotatedModel(pam); + // epb.processCurrentPAnnotatedModel(); + // } + + if (po.isIgnoreEAnnotations()) { + log.debug("Ignoring annotations"); + } else { + log.debug("Parse annotations"); + final EAnnotationParserImporter parserImporter = extensionManager + .getExtension(EAnnotationParserImporter.class); + parserImporter.setExtraAnnotationSources(po); + parserImporter.process(pam); + } + + if (po.getPersistenceXmlPath() != null) { + try { + final PersistenceFileProvider fileProvider = extensionManager + .getExtension(PersistenceFileProvider.class); + final InputStream in = fileProvider.getFileContent(null, po.getPersistenceXmlPath()); + if (in == null) { + throw new RuntimeException("Could not find persistence XML resource in classpath: \"" + + po.getPersistenceXmlPath() + "\"."); + } + final XmlPersistenceMapper xmlPersistenceMapper = extensionManager + .getExtension(XmlPersistenceMapper.class); + xmlPersistenceMapper.setXmlMapping(in); + xmlPersistenceMapper.applyPersistenceMapping(pam); + in.close(); + final InputStream[] iss = getAdditionalXMLMappings(); + for (InputStream element : iss) { + xmlPersistenceMapper.setXmlMapping(element); + xmlPersistenceMapper.applyPersistenceMapping(pam); + element.close(); + } + } catch (IOException e) { + throw new StoreAnnotationsException("Exception while loading xml persistence mappings", e); + } + } + + // now the annotations on the edatatype should be copied to the + // annotations on the + // eattribute, overwrite may not occur! + processEDataTypeAnnotations(pam); + + log.debug("Add default annotations"); + // DCB: Introduce indirection so that extensions to annotation + // processing mechanism + // can provide their own default annotation. + pam.setInitialized(true); + extensionManager.getExtension(AnnotationGenerator.class).map(pam, po); + + log.debug("Returning created pamodel"); + return pam; + } + + private void resolveSubPackages(EPackage epack, List<EPackage> epacks) { + if (!epacks.contains(epack)) { + epacks.add(epack); + } + + for (EPackage subEPackage : epack.getESubpackages()) { + resolveSubPackages(subEPackage, epacks); + } + } + + private void addAllUsedEPackages(EPackage eCurrentEPackage, List<EPackage> ePackages) { + if (eCurrentEPackage == null) { + return; + } + if (ePackages.contains(eCurrentEPackage)) { + return; + } + // do not resolve these + if (eCurrentEPackage instanceof EcorePackage || eCurrentEPackage instanceof XMLTypePackage) { + return; + } + // prevent recursion + ePackages.add(eCurrentEPackage); + + // note super epackage can be null, handled in first if above + addAllUsedEPackages(eCurrentEPackage.getESuperPackage(), ePackages); + for (EPackage subPackage : eCurrentEPackage.getESubpackages()) { + addAllUsedEPackages(subPackage, ePackages); + } + + // now capture each type + for (EClassifier eClassifier : eCurrentEPackage.getEClassifiers()) { + addAllUsedEPackages(eClassifier, ePackages); + } + } + + private void addAllUsedEPackages(EClassifier eClassifier, List<EPackage> ePackages) { + if (eClassifier instanceof EClass) { + addAllUsedEPackages((EClass) eClassifier, ePackages); + } else { + addAllUsedEPackages((EDataType) eClassifier, ePackages); + } + } + + private void addAllUsedEPackages(EClass eClass, List<EPackage> ePackages) { + addAllUsedEPackages(eClass.getEPackage(), ePackages); + for (EClass eSuperClass : eClass.getESuperTypes()) { + // apparently there is a cycle in one of the XSD/XML packages, this prevents this. + if (!ePackages.contains(eSuperClass.getEPackage())) { + addAllUsedEPackages(eSuperClass, ePackages); + } + } + for (EStructuralFeature eFeature : eClass.getEStructuralFeatures()) { + if (!ePackages.contains(eFeature.getEType().getEPackage())) { + addAllUsedEPackages(eFeature.getEType(), ePackages); + } + } + } + + private void addAllUsedEPackages(EDataType eDataType, List<EPackage> ePackages) { + if (eDataType == null) { + return; + } + addAllUsedEPackages(eDataType.getEPackage(), ePackages); + addAllUsedEPackages(ExtendedMetaData.INSTANCE.getBaseType(eDataType), ePackages); + } + + /** + * For each pannotated eattribute find the pannotated edatatype and copy the values of the estructuralfeature if not + * yet set in the eattribute + */ + protected void processEDataTypeAnnotations(PAnnotatedModel pam) { + log.debug("Copying annotations on edatatypes over eattribute annotations!"); + for (PAnnotatedEPackage pep : pam.getPaEPackages()) { + for (PAnnotatedEClass pec : pep.getPaEClasses()) { + for (PAnnotatedEStructuralFeature pef : pec.getPaEStructuralFeatures()) { + if (pef instanceof PAnnotatedEAttribute) { + final PAnnotatedEAttribute pea = (PAnnotatedEAttribute) pef; + final EDataType et = pea.getModelEAttribute().getEAttributeType(); + final PAnnotatedEDataType ped = pam.getPAnnotated(et); + if (ped == null) { + continue; // not an explicit modeled edatatype + } + for (EStructuralFeature esf : ped.eClass().getEAllStructuralFeatures()) { + final EStructuralFeature asf = pea.eClass().getEStructuralFeature(esf.getName()); + if (asf != null && !pea.eIsSet(asf) && ped.eIsSet(esf)) { + log.debug("Copying value for feature " + esf.getName() + " from edatatype " + + et.getName() + " to " + pea.getModelEAttribute().getName()); + + final Object obj = ped.eGet(esf); + if (obj instanceof Collection<?>) { + pea.eSet(asf, EcoreUtil.copyAll((Collection<?>) obj)); + } else if (obj instanceof EObject) { + pea.eSet(asf, EcoreUtil.copy((EObject) obj)); + } else { + throw new StoreAnnotationsException("Class " + obj.getClass().getName() + + " not supported should be eobject or collection"); + } + } + } + } + } + } + } + } + + /** Additional inputstreams for xml mappings */ + protected InputStream[] getAdditionalXMLMappings() { + return new InputStream[0]; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/SingleAttributeAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/SingleAttributeAnnotator.java new file mode 100755 index 000000000..4979d1e98 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/SingleAttributeAnnotator.java @@ -0,0 +1,153 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: SingleAttributeAnnotator.java,v 1.12 2009/09/14 21:40:14 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; +import org.eclipse.emf.teneo.annotations.pannotation.Basic; +import org.eclipse.emf.teneo.annotations.pannotation.EnumType; +import org.eclipse.emf.teneo.annotations.pannotation.Enumerated; +import org.eclipse.emf.teneo.annotations.pannotation.GeneratedValue; +import org.eclipse.emf.teneo.annotations.pannotation.GenerationType; +import org.eclipse.emf.teneo.annotations.pannotation.Id; +import org.eclipse.emf.teneo.annotations.pannotation.TemporalType; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Annotates a single attribute, a primitive type such as a long or int. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.12 $ + */ + +public class SingleAttributeAnnotator extends BaseEFeatureAnnotator implements ExtensionPoint { + + // The logger + protected static final Log log = LogFactory.getLog(SingleAttributeAnnotator.class); + + private TemporalType optionDefaultTemporal = null; + + /** Process the features of the eclass */ + public void annotate(PAnnotatedEAttribute aAttribute) { + + log.debug(" Adding default annotations for EAttribute " + aAttribute.getModelElement().getName()); + + final EAttribute eAttribute = (EAttribute) aAttribute.getModelElement(); + + // this is done before adding the id because an enumerated can also be + // an id + if (eAttribute.getEType() instanceof EEnum && aAttribute.getEnumerated() == null) { + final Enumerated enumerated = getFactory().createEnumerated(); + enumerated.setValue(EnumType.STRING); + enumerated.setEModelElement(eAttribute); + aAttribute.setEnumerated(enumerated); + } + + if (getPersistenceOptions().isIDFeatureAsPrimaryKey() && eAttribute.isID() && aAttribute.getId() == null) { + // bugzilla 249246 + if (aAttribute.getPaEClass().getPaSuperEntity() != null + && aAttribute.getPaEClass().getPaSuperEntity().getMappedSuperclass() == null) { + log + .warn("The eclass " + + aAttribute.getPaEClass().getModelEClass().getName() + + " has an efeature (" + + aAttribute.getModelEAttribute().getName() + + ")" + + " which has type ID, Teneo will not annotate this efeature with @Id because it is an efeature of a subtype"); + } else { + final Id id = getFactory().createId(); + id.setEModelElement(eAttribute); + aAttribute.setId(id); + addColumnConstraints(aAttribute); + + if (getPersistenceOptions().isSetGeneratedValueOnIDFeature() + && aAttribute.getGeneratedValue() == null + && (Number.class.isAssignableFrom(eAttribute.getEAttributeType().getInstanceClass()) + || eAttribute.getEAttributeType().getInstanceClass() == long.class || eAttribute + .getEAttributeType().getInstanceClass() == int.class)) { + final GeneratedValue gv = getFactory().createGeneratedValue(); + gv.setStrategy(GenerationType.AUTO); + aAttribute.setGeneratedValue(gv); + } + + return; // after id do not add basic + } + } else if (aAttribute.getId() != null) { + addColumnConstraints(aAttribute); + return; // after id do not do basic + } + + if (aAttribute.getTemporal() == null) { + setTemporal(aAttribute, optionDefaultTemporal); + } + + if (aAttribute.getBasic() == null) { + // primitive defaults are set in the model itself + final Basic basic = getFactory().createBasic(); + basic.setEModelElement(eAttribute); + + // NOTE: the ejb3 spec says that for primitivie optional does not + // apply, this is + // confusing why having this then? If this applies then for each + // basic and nullable + // field a column annotation has to be added to force nullability + + // removed unsettable because it is not used to define optional, it + // is used + // to allow distinction between the default value set or a feature + // which has not been + // set, this is used in validation + // basic.setOptional(!eAttribute.isRequired() || + // eAttribute.isUnsettable()); + if (aAttribute.getColumn() != null) { + basic.setOptional(aAttribute.getColumn().isNullable()); + } else { + basic.setOptional(!eAttribute.isRequired() || eAttribute.isUnsettable()); + } + aAttribute.setBasic(basic); + } + + if (aAttribute.getId() != null) { + aAttribute.getBasic().setOptional(false); + if (aAttribute.getColumn() != null && aAttribute.getColumn().isNullable()) { + log.warn("The column of a primary key property is null, this will often result in database errors!"); + } + } + addColumnConstraints(aAttribute); + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.annotations.mapper.AbstractAnnotator# setPersistenceOptions(org.eclipse + * .emf.teneo.PersistenceOptions) + */ + @Override + public void setPersistenceOptions(PersistenceOptions persistenceOptions) { + super.setPersistenceOptions(persistenceOptions); + + optionDefaultTemporal = TemporalType.get(persistenceOptions.getDefaultTemporalValue()); + if (optionDefaultTemporal == null) { + throw new StoreMappingException("Temporal value not found: " + persistenceOptions.getDefaultTemporalValue()); + } + } + +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/StoreMappingException.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/StoreMappingException.java new file mode 100755 index 000000000..ac7434da2 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/StoreMappingException.java @@ -0,0 +1,51 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: StoreMappingException.java,v 1.6 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import org.eclipse.emf.teneo.TeneoException; + +/** + * This exception is the base class of all exceptions which occur in the mapping process. This class + * offers automatic logging to commons logging. Note that this class extends RuntimeException, so no + * forced throws and catch statements. Although there are very differing views on this topic but it + * is our experience that to many checked exceptions only distract the programmer and have no added + * value. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.6 $ + */ + +public class StoreMappingException extends TeneoException { + /** + * Serial id + */ + private static final long serialVersionUID = 4685665979865102829L; + + /** + * The constructor, logs the exception also + */ + public StoreMappingException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * The constructor, logs the exception also + */ + public StoreMappingException(String msg) { + super(msg); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/UnidirectionalManyToManyAnnotator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/UnidirectionalManyToManyAnnotator.java new file mode 100755 index 000000000..ea2611405 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/mapper/UnidirectionalManyToManyAnnotator.java @@ -0,0 +1,91 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: UnidirectionalManyToManyAnnotator.java,v 1.9 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.mapper; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.annotations.pannotation.JoinTable; +import org.eclipse.emf.teneo.annotations.pannotation.ManyToMany; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Annotates a many-to-many which is handled from one side. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.9 $ + */ + +public class UnidirectionalManyToManyAnnotator extends BaseEFeatureAnnotator implements ExtensionPoint { + + // The logger + protected static final Log log = LogFactory.getLog(UnidirectionalManyToManyAnnotator.class); + + /** Process the features of the eclass */ + public void annotate(PAnnotatedEReference aReference) { + final String featureLogStr = + aReference.getModelEReference().getName() + "/" + + aReference.getModelEReference().getEContainingClass().getName(); + + if (aReference.getOneToMany() != null || aReference.getOneToOne() != null || aReference.getManyToOne() != null) { + throw new StoreMappingException("The feature/eclass " + featureLogStr + " should be a ManyToMany but " + + "it already has a OneToMany, OneToOne or ManyToOne annotation"); + } + + final EReference eReference = (EReference) aReference.getModelElement(); + + // note that mtm is always present because this case can not be + // discovered by Teneo + final ManyToMany mtm = aReference.getManyToMany(); + log.debug("ManyToMany present check if default information should be added"); + mtm.setEModelElement(eReference); + + if (eReference.isContainment() || getPersistenceOptions().isSetDefaultCascadeOnNonContainment()) { + setCascade(mtm.getCascade(), eReference.isContainment()); + } + + if (mtm.getTargetEntity() == null) { + mtm.setTargetEntity(getEntityName(eReference.getEReferenceType())); + } + + // with a unidirectional mtm the join is always placed here + JoinTable joinTable = aReference.getJoinTable(); + if (joinTable == null) { + joinTable = getFactory().createJoinTable(); + aReference.setJoinTable(joinTable); + } + joinTable.setEModelElement(eReference); + + if (getPersistenceOptions().isSetForeignKeyNames() && aReference.getForeignKey() == null) { + aReference.setForeignKey(createFK(aReference)); + } + + // note that here not the eclass name is used for the opposite side but + // the name of the targetentity + // because that's the one which is known here + if (joinTable.getName() == null) { + joinTable.setName(getSqlNameStrategy().getJoinTableName(aReference)); + } + if (joinTable.getJoinColumns() == null) { + final List<String> names = getSqlNameStrategy().getJoinTableJoinColumns(aReference, false); + joinTable.getJoinColumns().addAll(getJoinColumns(names, false, true, mtm)); + } + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/AnnotationParser.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/AnnotationParser.java new file mode 100755 index 000000000..bc5cface6 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/AnnotationParser.java @@ -0,0 +1,264 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: AnnotationParser.java,v 1.3 2008/04/06 13:44:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.emf.ecore.ENamedElement; + +/** + * Parses an annotation and creates a tree of parserNodes. + * + * See AnnotationTokenizer for a short description on the type of parsed tokens. + * + * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> + */ +public class AnnotationParser { + + /** The StringTokenizer being used */ + private AnnotationTokenizer annotationTokenizer; + + /** The list of root nodes */ + private List<NamedParserNode> parserNodes = new ArrayList<NamedParserNode>(); + + private long expectedToken = Long.MAX_VALUE; + + /** Parses the content and returns a list of parsernodes */ + public List<NamedParserNode> parse(ENamedElement eNamedElement, String content) { + annotationTokenizer = new AnnotationTokenizer(eNamedElement, content); + parserNodes.clear(); + int token; + while (annotationTokenizer.getCurrentToken() != AnnotationTokenizer.T_EOF && + (token = annotationTokenizer.nextToken()) != AnnotationTokenizer.T_EOF) { + if (token != AnnotationTokenizer.T_TYPENAME) { + throw new AnnotationParserException("Only typenames are allowed at the root of the " + + "annotation, see _ for the error " + annotationTokenizer.getErrorText()); + } + parseTypeName(null); + } + return parserNodes; + } + + /** Adds a child to the parent */ + private void addToParent(NamedParserNode parent, NamedParserNode child) { + if (parent == null) { + return; + } + if (parent instanceof ComplexNode) { + ((ComplexNode) parent).getChildren().add(child); + } else if (parent instanceof ArrayValueNode) { + ((ArrayValueNode) parent).getChildren().add(child); + } else if (parent instanceof ReferenceValueNode) { + ((ReferenceValueNode) parent).setValue(child); + } + } + + /** Parse a type name (a complex type) */ + private void parseTypeName(NamedParserNode pn) { + final ComplexNode cn = new ComplexNode(); + cn.setName(annotationTokenizer.getLexeme()); + addToParent(pn, cn); + if (pn == null) { + parserNodes.add(cn); + } + + // now parse the next token + final int token = annotationTokenizer.nextToken(); + + switch (token) { + case AnnotationTokenizer.T_EOF: + return; + case AnnotationTokenizer.T_CONTENTSTART: + parseContent(cn); + break; + case AnnotationTokenizer.T_TYPENAME: + parseTypeName(null); // the next one + break; + case AnnotationTokenizer.T_COMMA: // in case of array + if (!(pn instanceof ArrayValueNode)) { + throw new AnnotationParserException( + "Encountered comma but not within an array definition, see _ for error location " + + annotationTokenizer.getErrorText()); + } + return; + default: + throw new AnnotationParserException("Unknown token, see _ for error position: " + + annotationTokenizer.getErrorText()); + } + } + + /** Parse the content of a typeName */ + private void parseContent(NamedParserNode pn) { + // content can either be an array or a set of values + + expectedToken = + AnnotationTokenizer.T_ARRAYSTART + AnnotationTokenizer.T_IDENTIFIER + AnnotationTokenizer.T_VALUE; + + boolean finished = false; + while (!finished) { + final int token = annotationTokenizer.nextToken(); + checkToken(token); + switch (token) { + case AnnotationTokenizer.T_COMMA: + expectedToken = AnnotationTokenizer.T_IDENTIFIER; + break; + case AnnotationTokenizer.T_EOF: + throw new AnnotationParserException("Unexcepted end to annotation string, " + + annotationTokenizer.getErrorText()); + case AnnotationTokenizer.T_CONTENTEND: + return; + case AnnotationTokenizer.T_VALUE: + final String theValue = annotationTokenizer.getLexeme(); + final PrimitiveValueNode valueNode = new PrimitiveValueNode(); + valueNode.setName("value"); + valueNode.setValue(theValue); + addToParent(pn, valueNode); + if (annotationTokenizer.nextToken() != AnnotationTokenizer.T_CONTENTEND) { + throw new AnnotationParserException("After this value a closing ) should follow " + + annotationTokenizer.getErrorText()); + } + return; + case AnnotationTokenizer.T_IDENTIFIER: + final String identifier = annotationTokenizer.getLexeme(); + // next token must be an is + int nextToken = annotationTokenizer.nextToken(); + // in case of simple annotations with just a value member + if (nextToken == AnnotationTokenizer.T_CONTENTEND) { + final PrimitiveValueNode vn = new PrimitiveValueNode(); + vn.setName("value"); + vn.setValue(identifier); + addToParent(pn, vn); + return; + } + if (nextToken != AnnotationTokenizer.T_IS) { + throw new AnnotationParserException( + "No = character after identifier, see _ for error position " + + annotationTokenizer.getErrorText()); + } + nextToken = annotationTokenizer.nextToken(); + // if (nextToken == AnnotationTokenizer.T_VALUE) { + // final String value = annotationTokenizer.getLexeme(); + // final PrimitiveValueNode vn = new PrimitiveValueNode(); + // vn.setName(identifier); + // vn.setValue(value); + // addToParent(pn, vn); + // } + if (nextToken == AnnotationTokenizer.T_VALUE) { + final String value = annotationTokenizer.getLexeme(); + final PrimitiveValueNode vn = new PrimitiveValueNode(); + vn.setName(identifier); + vn.setValue(value); + addToParent(pn, vn); + } else if (nextToken == AnnotationTokenizer.T_IDENTIFIER) { + final String value = annotationTokenizer.getLexeme(); + final PrimitiveValueNode vn = new PrimitiveValueNode(); + vn.setName(identifier); + vn.setValue(value); + addToParent(pn, vn); + } else if (nextToken == AnnotationTokenizer.T_TYPENAME) { + final ReferenceValueNode rvn = new ReferenceValueNode(); + rvn.setName(identifier); + parseTypeName(rvn); + addToParent(pn, rvn); + } else if (nextToken == AnnotationTokenizer.T_ARRAYSTART) { + final ArrayValueNode avn = new ArrayValueNode(); + avn.setName(identifier); + parseArray(avn); + addToParent(pn, avn); + } else if (annotationTokenizer.nextToken() != AnnotationTokenizer.T_VALUE) { + throw new AnnotationParserException("No value token after =, see _ for error position " + + annotationTokenizer.getErrorText()); + } + expectedToken = + AnnotationTokenizer.T_COMMA + AnnotationTokenizer.T_IDENTIFIER + + AnnotationTokenizer.T_CONTENTEND; + break; + case AnnotationTokenizer.T_ARRAYSTART: + // special case in which the main type is just a list of other + // types + // for example SecondaryTables which is just a list of + // SecondaryTable + parseArray(pn); + ((ComplexNode) pn).setList(true); + expectedToken = + AnnotationTokenizer.T_COMMA + AnnotationTokenizer.T_IDENTIFIER + + AnnotationTokenizer.T_CONTENTEND; + break; + } + } + } + + /** Parse an array */ + private void parseArray(NamedParserNode pn) { + // content can either be an array or a set of values + final ArrayValueNode an = new ArrayValueNode(); + addToParent(pn, an); + boolean finished = false; + + expectedToken = AnnotationTokenizer.T_TYPENAME + AnnotationTokenizer.T_VALUE + AnnotationTokenizer.T_IDENTIFIER; + + while (!finished) { + final int token = annotationTokenizer.nextToken(); + checkToken(token); + switch (token) { + case AnnotationTokenizer.T_EOF: + throw new AnnotationParserException("Unexcepted end to annotation string, " + + annotationTokenizer.getErrorText()); + case AnnotationTokenizer.T_TYPENAME: + parseTypeName(an); + + expectedToken = AnnotationTokenizer.T_ARRAYEND + AnnotationTokenizer.T_COMMA; + + break; + case AnnotationTokenizer.T_VALUE: + String value = annotationTokenizer.getLexeme(); + if (value != null && value.length() > 1 && value.charAt(0) == '"' && + value.charAt(value.length() - 1) == '"') { + value = value.substring(1, value.length() - 1); + } + an.getChildren().add(value); + expectedToken = AnnotationTokenizer.T_ARRAYEND + AnnotationTokenizer.T_COMMA; + break; + case AnnotationTokenizer.T_IDENTIFIER: + an.getChildren().add(annotationTokenizer.getLexeme()); + + expectedToken = + AnnotationTokenizer.T_IS + AnnotationTokenizer.T_ARRAYEND + AnnotationTokenizer.T_COMMA; + break; + case AnnotationTokenizer.T_COMMA: + expectedToken = + AnnotationTokenizer.T_TYPENAME + AnnotationTokenizer.T_VALUE + + AnnotationTokenizer.T_IDENTIFIER; + break; + case AnnotationTokenizer.T_ARRAYEND: + expectedToken = Long.MAX_VALUE; + return; + } + } + throw new AnnotationParserException("Unexpected end of array. " + annotationTokenizer.getErrorText()); + } + + protected void checkToken(int currentToken) { + if ((currentToken & expectedToken) == 0) { + final String msg = + "Found " + annotationTokenizer.getCurrentTokenName() + " but expected one of : " + + annotationTokenizer.getTokenNames(expectedToken); + throw new AnnotationParserException(msg + ". " + annotationTokenizer.getErrorText()); + } + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/AnnotationParserException.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/AnnotationParserException.java new file mode 100755 index 000000000..5477096d7 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/AnnotationParserException.java @@ -0,0 +1,40 @@ +/** + * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights + * reserved. This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal </copyright> $Id: + * AnnotationParserException.java,v 1.4 2007/02/08 23:12:34 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +import org.eclipse.emf.teneo.TeneoException; + +/** + * Is thrown in the org.eclipse.emf.teneo.annotations.parser package. Takes care of logging the + * cause. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.2 $ + */ + +public class AnnotationParserException extends TeneoException { + /** + * Serial id + */ + private static final long serialVersionUID = 4685665979865102829L; + + /** + * The constructor, logs the exception also + */ + public AnnotationParserException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * The constructor, logs the exception also + */ + public AnnotationParserException(String msg) { + super(msg); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/AnnotationTokenizer.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/AnnotationTokenizer.java new file mode 100755 index 000000000..744aee878 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/AnnotationTokenizer.java @@ -0,0 +1,357 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: AnnotationTokenizer.java,v 1.12 2009/03/30 07:53:05 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +import java.util.HashMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.teneo.annotations.StoreAnnotationsException; + +/** + * Tokenizes a java annotation. The main tokens are: - TypeName - Identifier - Value - Array + * + * For example the following java annotation + * + * @GenericGenerator(name="hibseq", strategy = "hilo", parameters = { + * @Parameter(name="table", value = "hilo_table"), + * @Parameter(name="column", value="the_hilo_column")} ) + * + * Here GenericGenerator is a TypeName, name and strategy are Identifiers + * and "hilo_table" is a value, the array is the part between the {}. + * + * There is a special case where the typename is actually a list of + * values, e.g. SecondaryTables. These are treated as a special type of + * TypeName which is translated into a ComplexNode with isList=true. This + * is currently only supported at the top level. + * + * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> + */ +class AnnotationTokenizer { + /** Log it */ + private final static Log log = LogFactory.getLog(AnnotationTokenizer.class); + + /** Special Tokens */ + static final int T_EOF = 4096; + + private static final int T_EOL = 8192; + + private static final int T_UNKNOWN = 16384; + + /** + * Annotation tokens + */ + static final int T_TYPENAME = 2; + + static final int T_IDENTIFIER = 4; + + static final int T_ARRAYSTART = 8; + + static final int T_ARRAYEND = 16; + + static final int T_VALUE = 32; + + static final int T_IS = 64; + + static final int T_CONTENTSTART = 128; + + static final int T_CONTENTEND = 256; + + static final int T_COMMA = 512; + + /** Data */ + private char[] data; + + /** Length */ + private int length; + + /** Points to the start of the current token */ + + private int tokBeg; + + /** Ponts to the end of the current token. */ + private int tokEnd; + + /** The last returned token */ + private int currentToken = T_EOF - 1; + + private HashMap<Integer, String> constantToName = new HashMap<Integer, String>(); + + /** + * Constructor + */ + + AnnotationTokenizer(ENamedElement eNamedElement, String source) { + setSource(source.toCharArray()); + constantToName.put(2, "Annotation"); + constantToName.put(4, "Attribute Name"); + constantToName.put(8, "Array Start ({)"); + constantToName.put(16, "Array End (})"); + constantToName.put(32, "Value (e.g. String, int)"); + constantToName.put(64, "= character"); + constantToName.put(128, "Annotation content start ('(')"); + constantToName.put(256, "Annotation content end (')')"); + constantToName.put(512, "Comma (,)"); + + constantToName.put(1024, "Carriage Return"); + constantToName.put(2048, "Line Feed"); + constantToName.put(4096, "EOF"); + constantToName.put(8192, "EOL"); + constantToName.put(16384, "Unknown"); + } + + public String getCurrentTokenName() { + final String name = constantToName.get(currentToken); + if (name == null) { + throw new StoreAnnotationsException("Illegal token " + currentToken); + } + return name; + } + + public String getTokenNames(long tokens) { + final StringBuffer sb = new StringBuffer(); + for (Integer key : constantToName.keySet()) { + + if ((tokens & key.intValue()) > 0) { + if (sb.length() > 0) { + sb.append(", "); + } + sb.append(constantToName.get(key)); + } + } + return sb.toString(); + } + + /** + * Sets the source to be tokenized form a character array. + */ + + private void setSource(char[] iSource) { + length = iSource.length; + // Append three null-characters as sentinel since three + // look-ahead characters are required (e.g. for the '>>>=' token). + data = new char[length + 3]; + System.arraycopy(iSource, 0, data, 0, length); + + data[length] = 0; // Append the sentinel characters. + data[length + 1] = 0; + data[length + 2] = 0; + + tokBeg = 0; + tokEnd = 0; + + log.debug(dump()); + } + + /** + * Returns the next token. + */ + + final int nextToken() { + currentToken = getNextToken(); + return currentToken; + } + + /** Return the curren token */ + final int getCurrentToken() { + return currentToken; + } + + /** + * Returns the next token. + */ + + final int getNextToken() { + int lCur = tokEnd; + + Loop: for (;;) { + char lChar = data[lCur]; // Grab next character. + + switch (lChar) { + case ' ': // Skip leading whitespace! + case '\n': // new line + case '\r': // Carriage Return. + case '\f': // Line Feed. + case '\t': { + lCur++; + continue Loop; // --> Keep on skipping leading whitespace! + } + + case 0: // End of buffer. + { + if (lCur == length) // Guard against embedded nulls in the + // Source. + { + // EOBuf may only occur at the first non whitespace char. + + return T_EOF; // --> End of file. + } + throw new AnnotationParserException("Char is 0 but end not reached " + lCur + " " + length); + } + + // TYPENAME + case '@': { + ++lCur; // get rid of the @ + tokBeg = lCur; // Save starting point of current lexeme. + + do { + lChar = data[++lCur]; + } while (lChar == '-' || lChar == '_' || lChar == '/' || lChar == '@' || + ('0' <= lChar && lChar <= '9') || lChar == ':' || ('a' <= lChar && lChar <= 'z') || + ('A' <= lChar && lChar <= 'Z')); + + tokEnd = lCur; // Save endpoint of current lexeme. + + return T_TYPENAME; // --> Identifier. + } + // VALUE with double quotes + case '"': { + // after the dollar the identifier part needs to be found + tokBeg = lCur; // Save starting point of current lexeme. + + do { + lChar = data[++lCur]; + } while (lChar == ',' || lChar == '-' || lChar == '.' || lChar == ' ' || lChar == '_' || + lChar == '/' || lChar == '`' || lChar == '@' || lChar == ':' || lChar == '=' || + lChar == '(' || lChar == ')' || lChar == '{' || lChar == '}' || lChar == '\'' || + lChar == '#' || lChar == '&' || lChar == '<' || lChar == '>' || lChar == '$' || + lChar == ';' || lChar == '%' || lChar == '*' || lChar == '\'' || + ('0' <= lChar && lChar <= '9') || ('a' <= lChar && lChar <= 'z') || lChar == '?' || + ('A' <= lChar && lChar <= 'Z')); + + if (lChar != '"') { + final AnnotationParserException e = + new AnnotationParserException( + "Value not closed with double quote, see the _ for the location " + getErrorText()); + tokEnd = lCur + 1; // prevent infinite looping + throw e; + } + tokEnd = lCur + 1; + return T_VALUE; + } + case '(': { + tokBeg = lCur; + tokEnd = lCur + 1; + return T_CONTENTSTART; + } + case ')': { + tokBeg = lCur; + tokEnd = lCur + 1; + return T_CONTENTEND; + } + case '{': { + tokBeg = lCur; + tokEnd = lCur + 1; + return T_ARRAYSTART; + } + case '}': { + tokBeg = lCur; + tokEnd = lCur + 1; + return T_ARRAYEND; + } + case ',': { + tokBeg = lCur; + tokEnd = lCur + 1; + return T_COMMA; + } + case '=': { + tokBeg = lCur; + tokEnd = lCur + 1; + return T_IS; + } + default: // the rest must be identifiers + { + // after the dollar the identifier part needs to be found + tokBeg = lCur; // Save starting point of current lexeme. + + do { + lChar = data[++lCur]; + } while (lChar == '.' || lChar == '-' || lChar == '_' || lChar == '/' || lChar == '@' || + ('0' <= lChar && lChar <= '9') || ('a' <= lChar && lChar <= 'z') || + ('A' <= lChar && lChar <= 'Z')); + + tokEnd = lCur; // Save endpoint of current lexeme. + + return T_IDENTIFIER; // --> Identifier. + } + } + } + } + + /** + * Returns the current lexeme. + */ + + final String getLexeme() { + return new String(data, tokBeg, tokEnd - tokBeg); + } + + /** + * Returns an error version of the query with a _ at the error location. + */ + final String getErrorText() { + // final StringBuffer result = new StringBuffer(); + // result.append("E Element: " + eNamedElement.getName() + "\n"); + // result.append("Begin: " + tokBeg + "\n"); + // result.append("End: " + tokEnd + "\n"); + // result.append("Length: " + data.length + "\n"); + // result.append("first part: " + new String(data, 0, tokEnd) + "\n"); + // result.append("Last part: " + new String(data, tokEnd, data.length - + // tokEnd - 2) + "\n"); + + return new String(data, 0, tokEnd) + "_" + new String(data, tokEnd, data.length - tokEnd - 2) + + "\nCurrent lexeme: " + getLexeme(); + } + + /** + * Dumps the tokens. + */ + + final String dump() { + final StringBuffer result = new StringBuffer(); + int oldTokBeg = tokBeg; + int oldTokEnd = tokEnd; + int oldCurrentToken = currentToken; + + // Reset pointers. + tokBeg = 0; + tokEnd = 0; + + boolean lFinished = false; + int lTok = 0; + while (!lFinished) { + try { + lTok = nextToken(); + if (lTok != T_EOL) // Don't log End-of-line tokens. + { + result.append("Tok: " + lTok + ": '" + getLexeme() + "'\n"); + } + } catch (AnnotationParserException e) { + result.append("Tok: " + T_UNKNOWN + ": " + getLexeme() + "'"); + throw e; + } + lFinished = lTok == T_EOF; + } + + // Restore state. + tokBeg = oldTokBeg; + tokEnd = oldTokEnd; + currentToken = oldCurrentToken; + return result.toString(); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ArrayValueNode.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ArrayValueNode.java new file mode 100755 index 000000000..1cfd3659f --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ArrayValueNode.java @@ -0,0 +1,65 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: ArrayValueNode.java,v 1.3 2009/03/30 07:53:05 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * An array node contains a list of child values. + * + * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> + */ +class ArrayValueNode extends NamedParserNode { + /** Log it */ + private final static Log log = LogFactory.getLog(ArrayValueNode.class); + + /** The value */ + private List<Object> children = new ArrayList<Object>(); + + /** Returns the list */ + List<Object> getChildren() { + return children; + } + + /** Translate into a list of eobjects */ + List<Object> convert(EClassResolver ecr) { + log.debug("Converting array value node"); + + final ArrayList<Object> result = new ArrayList<Object>(); + for (Object ob : children) { + if (ob instanceof String) { + result.add(ob); + } else if (ob instanceof ComplexNode) { + final ComplexNode cn = (ComplexNode) ob; + result.add(cn.convert(ecr)); + } else if (ob instanceof ReferenceValueNode) { + final ReferenceValueNode rvn = (ReferenceValueNode) ob; + result.add(rvn.convert(ecr)); + } else if (ob instanceof ArrayValueNode) { + final ArrayValueNode avn = (ArrayValueNode) ob; + result.addAll((List<Object>) avn.convert(ecr)); + } else { + throw new AnnotationParserException("Type " + ob.getClass().getName() + " not supported here"); + } + } + return result; + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ComplexNode.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ComplexNode.java new file mode 100755 index 000000000..0f818d71a --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ComplexNode.java @@ -0,0 +1,178 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: ComplexNode.java,v 1.5 2011/02/21 06:40:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.teneo.annotations.pannotation.PAnnotation; + +/** + * Models a real type (a complex type in xml schema speak), an EClass. + * + * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> + */ +class ComplexNode extends NamedParserNode { + + /** Log it */ + private final static Log log = LogFactory.getLog(ComplexNode.class); + + /** The child nodes */ + private List<NamedParserNode> children = new ArrayList<NamedParserNode>(); + + /** Is set if this is a list */ + private boolean isList = false; + + /** Returns the list of children */ + List<NamedParserNode> getChildren() { + return children; + } + + /** Translate into an eclass */ + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + Object convert(EClassResolver ecr) { + log.debug("Converting " + getName() + " to EObject "); + + // special case in which the main type is just a list of other types + // for example SecondaryTables which is just a list of SecondaryTable + // TODO: repair this hard link to a separate type! + final EClass eClass = ecr.getEClass(getName()); + if (isList() && eClass == null) { + assert (children.size() == 1); + assert (children.get(0) instanceof ArrayValueNode); + return ((ArrayValueNode) children.get(0)).convert(ecr); + } + + if (eClass == null) { + throw new AnnotationParserException("No eclass found with name " + + getName()); + } + final EObject eobj = EcoreUtil.create(eClass); + ((PAnnotation) eobj).setGenerated(false); + + for (NamedParserNode child : children) { + final EStructuralFeature efeature = ecr.getEStructuralFeature( + eClass, child.getName()); + if (child instanceof PrimitiveValueNode) { + final PrimitiveValueNode pvn = (PrimitiveValueNode) child; + log.debug("Primitive child: " + pvn.getName() + ": " + + pvn.getValue()); + if (!(efeature instanceof EAttribute)) { + throw new AnnotationParserException("The EFeature " + + efeature.getName() + "/" + eClass.getName() + + " is not an eattribute but a " + + efeature.getClass().getName()); + } + final EClassifier eType = efeature.getEType(); + if (!efeature.isMany()) { + eobj.eSet( + efeature, + ParserUtil.convertValue((EDataType) eType, + pvn.getValue())); + } else { + final String[] sources = pvn.getValue().split("\\s+"); + log.debug("Child is many, splitting content into " + + sources.length + " parts"); + final List<Object> referenced = new ArrayList<Object>( + sources.length); + for (String source : sources) { + referenced.add(ParserUtil.convertValue( + (EDataType) eType, source)); + } + final List currentList = (List) eobj.eGet(efeature); + currentList.addAll(referenced); + } + } else if (child instanceof ArrayValueNode + && efeature instanceof EAttribute) { + final EAttribute eattr = (EAttribute) efeature; + if (!eattr.isMany()) { + throw new AnnotationParserException("The EFeature " + + efeature.getName() + "/" + eClass.getName() + + " is not ismany"); + } + log.debug("Array child with primitive values"); + List<Object> list = ((ArrayValueNode) child).convert(ecr); + List<Object> convertedList = new ArrayList<Object>(); + for (Object object : list) { + final String val = (String) object; + log.debug("Value " + val); + convertedList.add(ParserUtil.convertValue( + (EDataType) eattr.getEType(), val)); + } + final List currentList = (List) eobj.eGet(efeature); + currentList.addAll(convertedList); + } else if (child instanceof ArrayValueNode) { + if (!(efeature instanceof EReference)) { + throw new AnnotationParserException("The EFeature " + + efeature.getName() + "/" + eClass.getName() + + " is not an ereference but a " + + efeature.getClass().getName()); + } + final EReference eref = (EReference) efeature; + if (!eref.isMany()) { + throw new AnnotationParserException("The EFeature " + + efeature.getName() + "/" + eClass.getName() + + " is not ismany"); + } + log.debug("Array child"); + eobj.eSet(eref, ((ArrayValueNode) child).convert(ecr)); + } else if (child instanceof ReferenceValueNode) { + if (!(efeature instanceof EReference)) { + throw new AnnotationParserException("The EFeature " + + efeature.getName() + "/" + eClass.getName() + + " is not an ereference but a " + + efeature.getClass().getName()); + } + final EReference eref = (EReference) efeature; + log.debug("Reference child " + child.getName()); + if (eref.isMany()) { + ((List) eobj.eGet(eref)).add(((ReferenceValueNode) child) + .convert(ecr)); + } else { + eobj.eSet(eref, ((ReferenceValueNode) child).convert(ecr)); + } + } + } + return eobj; + } + + /** + * @return the isList + */ + public boolean isList() { + return isList; + } + + /** + * @param isList + * the isList to set + */ + public void setList(boolean isList) { + this.isList = isList; + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/EAnnotationParserImporter.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/EAnnotationParserImporter.java new file mode 100755 index 000000000..ad1f05d75 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/EAnnotationParserImporter.java @@ -0,0 +1,244 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: EAnnotationParserImporter.java,v 1.8 2010/02/04 11:02:59 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EAnnotation; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.teneo.Constants; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEDataType; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEModelElement; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEPackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.annotations.pannotation.PannotationPackage; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Walks over the pamodel and the paepackages and translates eannotations to pannotation types and sets the + * corresponding values in the pamodel. + * + * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> + */ +public class EAnnotationParserImporter implements EClassResolver, ExtensionPoint { + /** Log it */ + private final static Log log = LogFactory.getLog(EAnnotationParserImporter.class); + + /** annotation parser */ + private AnnotationParser annotationParser = new AnnotationParser(); + + /** The prefix to distinguish jpa annotations from hb or jpox annotations */ + private static final String JPA_PREFIX = "jpa:"; + + private String[] extraAnnotationsSources = new String[] {}; + + /** Parse an pamodel */ + public void process(PAnnotatedModel paModel) { + for (PAnnotatedEPackage pap : paModel.getPaEPackages()) { + log.debug("Processing package " + pap.getModelEPackage().getName()); + processAnnotatedModelElement(pap, pap.eClass().getEPackage()); + + // and now the eclasses + process(pap); + } + } + + /** Process package */ + protected void process(PAnnotatedEPackage pap) { + for (PAnnotatedEClass pac : pap.getPaEClasses()) { + processAnnotatedModelElement(pac, pac.getModelEClass().getEPackage()); + process(pac); + } + for (PAnnotatedEDataType pac : pap.getPaEDataTypes()) { + processAnnotatedModelElement(pac, pac.getModelEDataType().getEPackage()); + } + } + + /** Process the efeatures */ + protected void process(PAnnotatedEClass pac) { + log.debug("Processing eclass " + pac.getModelEClass().getName()); + for (PAnnotatedEStructuralFeature paf : pac.getPaEStructuralFeatures()) { + processAnnotatedModelElement(paf, paf.getModelEStructuralFeature().eClass().getEPackage()); + } + } + + /** Process a type with its eannotations */ + @SuppressWarnings("unchecked") + protected void processAnnotatedModelElement(PAnnotatedEModelElement pee, EPackage epack) { + log.debug("Processing " + pee.getModelElement().getName()); + final ArrayList<NamedParserNode> parsedNodes = new ArrayList<NamedParserNode>(); + for (EAnnotation annotation : pee.getModelElement().getEAnnotations()) { + parsedNodes.addAll(process(annotation, pee.getModelElement())); + } + + // now also do the annotations on the edatatype (if any) + /* + * if (pee.getAnnotatedElement() instanceof EAttribute) { final EAttribute eattr = + * (EAttribute)pee.getAnnotatedElement(); final EDataType edt = (EDataType)eattr.getEType(); for (Iterator it = + * edt.getEAnnotations().iterator(); it.hasNext();) { parsedNodes.addAll(process((EAnnotation)it.next(), + * pee.getAnnotatedElement())); } } + */ + + // now the parsed nodes should be translated into features of the + // enamedelement + // this is done multiplelevel + log.debug("Number of parsed typename annotations " + parsedNodes.size()); + for (NamedParserNode namedParserNode : parsedNodes) { + final ComplexNode cn = (ComplexNode) namedParserNode; + if (cn.isList()) { + // find the efeature + final EStructuralFeature ef = getEStructuralFeature(pee.eClass(), cn.getName()); + pee.eSet(ef, cn.convert(this)); + } else { + EObject eobj = (EObject) cn.convert(this); + boolean found = false; + for (EReference eref : pee.eClass().getEAllReferences()) { + if (eref.getEReferenceType().isInstance(eobj)) { + log.debug("Found EReference " + eref.getName() + " for " + eobj.eClass().getName()); + if (eref.isMany()) { + ((List<EObject>) pee.eGet(eref)).add(eobj); + } else { + pee.eSet(eref, eobj); + } + found = true; + break; + } + } + if (!found) { + throw new AnnotationParserException("The eclass: " + pee.eClass().getName() + + " does not have an efeature for " + eobj.eClass().getName()); + } + } + } + + // now for each eobject find which eref stores it! + // log.debug("Find efeature for each created eobject"); + // final ArrayList objects = new ArrayList(); + // for (Iterator it = objects.iterator(); it.hasNext();) { + // EObject eobj = (EObject)it.next(); + // log.debug("EClass " + eobj.eClass().getName()); + // } + } + + /** Processes EAnnotations */ + private ArrayList<NamedParserNode> process(EAnnotation ea, ENamedElement ene) { + final ArrayList<NamedParserNode> result = new ArrayList<NamedParserNode>(); + + if (!isValidSource(ea.getSource())) { + return result; + } + + log.debug("Processing annotations "); + for (Map.Entry<String, String> pAnnotationDetails : ea.getDetails().entrySet()) { + final String fName = pAnnotationDetails.getKey(); + // todo externalize + if (fName.compareToIgnoreCase("appinfo") == 0 || fName.compareToIgnoreCase("value") == 0) { + log.debug("Annotation content: \n " + pAnnotationDetails.getValue()); + final String content = removeCommentLines(pAnnotationDetails.getValue()); + result.addAll(annotationParser.parse(ene, content)); + } + } + return result; + } + + // removes the lines which start with a // + private String removeCommentLines(String content) { + if (content.indexOf("//") == -1) { + return content; + } + final String[] lines = content.split("\n"); + final StringBuilder sb = new StringBuilder(); + for (String line : lines) { + if (line.trim().startsWith("//")) { + continue; + } + if (sb.length() > 0) { + sb.append("\n"); + } + sb.append(line); + } + return sb.toString(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.annotations.parser.EClassResolver#getEClass(java .lang.String) + */ + public EClass getEClass(String name) { + if (name.startsWith(JPA_PREFIX)) { + return getEClass(name.substring(JPA_PREFIX.length())); + } + return (EClass) PannotationPackage.eINSTANCE.getEClassifier(name); + } + + /** Is a valid source */ + protected boolean isValidSource(String source) { + if (source == null) { + return false; + } + + if (extraAnnotationsSources.length > 0) { + for (String annotationSource : extraAnnotationsSources) { + if (source.equals(annotationSource)) { + return true; + } + } + } + + return source.startsWith(Constants.ANNOTATION_SOURCE_TENEO_JPA) + || source.startsWith(Constants.ANNOTATION_SOURCE_TENEO_MAPPING); + } + + /** Find the efeature */ + public EStructuralFeature getEStructuralFeature(EClass eClass, String name) { + return ParserUtil.getEStructuralFeature(eClass, name); + } + + public void setExtraAnnotationSources(PersistenceOptions po) { + if (po.getExtraAnnotationSources() != null && po.getExtraAnnotationSources().trim().length() > 0) { + extraAnnotationsSources = po.getExtraAnnotationSources().split(","); + for (int i = 0; i < extraAnnotationsSources.length; i++) { + extraAnnotationsSources[i] = extraAnnotationsSources[i].trim(); + if (extraAnnotationsSources[i].startsWith(Constants.ANNOTATION_SOURCE_TENEO_JPA) + || extraAnnotationsSources[i].startsWith(Constants.ANNOTATION_SOURCE_TENEO_MAPPING)) { + log + .warn("Extra annotation source (" + + extraAnnotationsSources[i] + + ") starts with the default Teneo annotation source: " + + Constants.ANNOTATION_SOURCE_TENEO_JPA + + " or " + + Constants.ANNOTATION_SOURCE_TENEO_MAPPING + + ". Annotations which should sometimes be " + + "ignored or disabled should not start with these values as they are always considered by Teneo"); + } + } + } + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/EClassResolver.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/EClassResolver.java new file mode 100755 index 000000000..1e045e59d --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/EClassResolver.java @@ -0,0 +1,34 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: EClassResolver.java,v 1.2 2008/02/28 07:08:33 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * Finds an eclass using a certain string. + * + * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> + */ +public interface EClassResolver { + + /** Return an eclass, returns null if not found */ + EClass getEClass(String name); + + /** Find the efeature */ + EStructuralFeature getEStructuralFeature(EClass eClass, String name); +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/NamedParserNode.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/NamedParserNode.java new file mode 100755 index 000000000..59cd322b4 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/NamedParserNode.java @@ -0,0 +1,46 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: NamedParserNode.java,v 1.2 2008/02/28 07:08:33 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +/** + * Main class of the nodes which are the result of the annotation parser. + * + * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> + */ +abstract class NamedParserNode { + + /** The name parsed */ + private String name = "value"; + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** Translate to pamodel/pannotation */ + abstract Object convert(EClassResolver ecr); +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ParserUtil.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ParserUtil.java new file mode 100755 index 000000000..4e4555e85 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ParserUtil.java @@ -0,0 +1,66 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: ParserUtil.java,v 1.3 2009/03/30 07:53:05 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.EcoreUtil; + +/** + * Util class + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.3 $ + */ + +public class ParserUtil { + + /** + * @return Returns the result of converting the String value to the given data type. + * @throws EAnnotationImportException + */ + public static Object convertValue(EDataType eType, String value) { + try { + return EcoreUtil.createFromString(eType, value); + } catch (IllegalArgumentException e) { + // now try without the first dot + if (value != null && value.indexOf('.') != -1) { + try { + return EcoreUtil.createFromString(eType, value.substring(1 + value.indexOf('.'))); + } catch (IllegalArgumentException x) { + throw new AnnotationParserException("Cannot convert '" + value + "' to '" + eType.getName() + + "' type", e); + } + } + throw new AnnotationParserException("Cannot convert '" + value + "' to '" + eType.getName() + "' type", e); + } + } + + /** Get a structuralfeature */ + public static EStructuralFeature getEStructuralFeature(EClass eClass, String name) { + try { + for (EStructuralFeature ef : eClass.getEAllStructuralFeatures()) { + if (ef.getName().compareToIgnoreCase(name) == 0) return ef; + } + throw new AnnotationParserException("No efeature " + name + " for eclass " + eClass.getName()); + } catch (IllegalArgumentException e) { + throw new AnnotationParserException("Cannot convert '" + name + "' to an efeature for eclass " + + eClass.getName()); + } + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/PrimitiveValueNode.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/PrimitiveValueNode.java new file mode 100755 index 000000000..afa5fd48e --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/PrimitiveValueNode.java @@ -0,0 +1,58 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: PrimitiveValueNode.java,v 1.5 2008/06/10 10:19:43 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +/** + * Simple value node. + * + * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> + */ +class PrimitiveValueNode extends NamedParserNode { + + /** The value */ + private String value; + + /** + * @return the value + */ + public String getValue() { + return value; + } + + /** + * @param value + * the value to set + */ + public void setValue(String value) { + value = value.replaceAll(">", ">"); + value = value.replaceAll("<", "<"); + + // correct a small mistake in the tokenizer + if (value != null && value.length() > 1 && value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') { + this.value = value.substring(1, value.length() - 1); + } else { + this.value = value; + } + } + + /** Translate into an etype */ + @Override + Object convert(EClassResolver ecr) { + return value; + } + +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ReferenceValueNode.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ReferenceValueNode.java new file mode 100755 index 000000000..66c6d84cd --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/parser/ReferenceValueNode.java @@ -0,0 +1,58 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: ReferenceValueNode.java,v 1.3 2009/03/30 07:53:05 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.parser; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Node which has a complex type as its value. + * + * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> + */ +class ReferenceValueNode extends NamedParserNode { + /** Log it */ + private final static Log log = LogFactory.getLog(ArrayValueNode.class); + + /** The value */ + private NamedParserNode value; + + /** + * @return the value + */ + public NamedParserNode getValue() { + return value; + } + + /** + * @param value + * the value to set + */ + public void setValue(NamedParserNode value) { + this.value = value; + } + + /** Translate into a list of eobjects */ + Object convert(EClassResolver ecr) { + log.debug("Converting reference node " + getName()); + if (!(value instanceof ComplexNode)) { + throw new AnnotationParserException("A reference annotation value may only " + "contain a typename"); + } + final ComplexNode cn = (ComplexNode) value; + return cn.convert(ecr); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/ParseXMLAnnotationsException.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/ParseXMLAnnotationsException.java new file mode 100755 index 000000000..bb8f19400 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/ParseXMLAnnotationsException.java @@ -0,0 +1,55 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: ParseXMLAnnotationsException.java,v 1.2 2008/02/28 07:08:33 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.xml; + +import org.eclipse.emf.teneo.annotations.StoreAnnotationsException; + +/** + * Is thrown in the org.eclipse.emf.teneo.annotations.xml package. Takes care of logging the cause. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.2 $ + */ + +public class ParseXMLAnnotationsException extends StoreAnnotationsException { + + /** + * Serial Version ID + */ + private static final long serialVersionUID = -8670363508437586401L; + + /** + * The constructor, logs the exception also + */ + public ParseXMLAnnotationsException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * The constructor, logs the exception also + */ + public ParseXMLAnnotationsException(String msg) { + super(msg); + } + + /** + * The constructor, logs the exception also + */ + public ParseXMLAnnotationsException(Throwable t) { + super(t.getMessage(), t); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/PersistenceMappingSchemaGenerator.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/PersistenceMappingSchemaGenerator.java new file mode 100755 index 000000000..8015a4ddc --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/PersistenceMappingSchemaGenerator.java @@ -0,0 +1,592 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: PersistenceMappingSchemaGenerator.java,v 1.7 2010/02/04 11:03:02 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.xml; + +import java.io.FileWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.ecore.EAnnotation; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.ecore.EEnumLiteral; +import org.eclipse.emf.ecore.EModelElement; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.teneo.annotations.StoreAnnotationsException; +import org.eclipse.emf.teneo.annotations.pamodel.PamodelPackage; +import org.eclipse.emf.teneo.annotations.pannotation.PannotationPackage; +import org.eclipse.emf.teneo.simpledom.Attribute; +import org.eclipse.emf.teneo.simpledom.Document; +import org.eclipse.emf.teneo.simpledom.Element; + +/** + * Parses the pamodel and pannotation model to generate a xsd. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.7 $ + */ + +public class PersistenceMappingSchemaGenerator { + + /** The source name used to set to which estructural feature a tag belongs */ + public static final String ESTRUCTURAL_FEATURE_SOURCE_NAME = "teneo/internal/EStructuralFeatureName"; + + /** Used to set efeatures in the ecore model to be ignored */ + public static final String PERSISTENCE_MAPPING_SOURCE = "teneo/internal/PersistenceMapping"; + + /** Is set on efeatures to denote that they are not supported */ + private static final String UNSUPPORTED_SOURCE = "teneo/internal/Unsupported"; + + public static final String XML_SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema"; + + /** The main method, ugly but effective */ + public static void main(String[] args) { + final PersistenceMappingSchemaGenerator pmsg = new PersistenceMappingSchemaGenerator(); + pmsg.setAnnotationEPackages(new EPackage[] { PannotationPackage.eINSTANCE }); + pmsg.setModelEPackage(PamodelPackage.eINSTANCE); + try { + final FileWriter fw = new FileWriter("/home/mtaal/mytmp/persistence-mapping.xsd"); + fw.write(pmsg.generate()); + fw.close(); + } catch (Exception e) { + throw new StoreAnnotationsException("Exception while generating mapping.xsd", e); + } + } + + /** The pamodel */ + private EPackage modelEPackage; + + /** And the annotations packages */ + private EPackage[] annotationEPackages; + + /** Mapping from ecore simple types to schema simple types */ + private final Map<String, String> schemaTypeNamesByAnnotationType = new HashMap<String, String>(); + + /** Target name space */ + private String nameSpace = "http://www.eclipse.org/emft/teneo"; + + /** Initialize some main things */ + private void initialize() { + schemaTypeNamesByAnnotationType.put("EBoolean", "xsd:boolean"); + schemaTypeNamesByAnnotationType.put("EInt", "xsd:int"); + schemaTypeNamesByAnnotationType.put("ELong", "xsd:long"); + schemaTypeNamesByAnnotationType.put("EString", "xsd:string"); + } + + /** Generate into a string */ + public String generate() { + + initialize(); + + final Document doc = new Document(); + + // The root Element + final Element root = new Element("xsd:schema").addAttribute("targetNamespace", nameSpace).addAttribute( + "elementFormDefault", "qualified").addAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema") + .addAttribute("xmlns", nameSpace); + + root.addElement("xsd:element").addAttribute("name", "persistence-mapping").addAttribute("type", + "PersistenceMapping"); + root.addElement("xsd:complexType").addAttribute("name", "PersistenceMapping").addElement("xsd:sequence") + .addAttribute("minOccurs", "1").addAttribute("maxOccurs", "unbounded").addElement("xsd:element") + .addAttribute("name", "epackage").addAttribute("type", "EPackage"); + + // first determine which types have only one string field, these are handled + // slightly different because this makes the xml easier + for (EPackage annotationEPackage : annotationEPackages) { + final List<EClassifier> eclassifiers = new ArrayList<EClassifier>(annotationEPackage.getEClassifiers()); + for (EClassifier eClassifier : eclassifiers) { + String schemaTypeName = eClassifier.getName(); + if (eClassifier instanceof EClass) { + EClass eClass = (EClass) eClassifier; + + // Annotation types with a single feature are converted to simple type + // references in the schema. + if (oneMappableFeature(eClass)) { + final EStructuralFeature eStructuralFeature = eClass.getEStructuralFeatures().get(0); + final EClassifier eType = eStructuralFeature.getEType(); + schemaTypeName = schemaTypeNamesByAnnotationType.get(eType.getName()); + if (schemaTypeName == null) { + schemaTypeName = eType.getName(); + } + schemaTypeNamesByAnnotationType.put(eClassifier.getName(), schemaTypeName); + continue; + } + } + schemaTypeNamesByAnnotationType.put(eClassifier.getName(), schemaTypeName); + } + } + + // process the annotations first to get the correct typing + final List<Element> annotationList = new ArrayList<Element>(); + for (EPackage annotationEPackage : annotationEPackages) { + annotationList.addAll(processAnnotationEPackage(annotationEPackage)); + } + + root.addElement(createEPackageElement()); + root.addElement(createEClassElement()); + root.addElement(createEAttributeElement()); + root.addElement(createEReferenceElement()); + root.addElement(createPropertyElement()); + root.addElement(createEDataTypeElement()); + root.getChildren().addAll(annotationList); + + doc.setRoot(root); + return doc.emitXML(); + } + + /** process annotation packages */ + private List<Element> processAnnotationEPackage(EPackage epackage) { + final ArrayList<Element> elemList = new ArrayList<Element>(); + final List<EClassifier> eclassifiers = new ArrayList<EClassifier>(epackage.getEClassifiers()); + Collections.sort(eclassifiers, new ENamedElementComparator()); + + for (EClassifier eClassifier : eclassifiers) { + if (isIgnorable(eClassifier) || isUnsupported(eClassifier)) { + continue; + } + + String schemaTypeName = eClassifier.getName(); + if (eClassifier instanceof EClass) { + final EClass eClass = (EClass) eClassifier; + if (eClass.isAbstract()) { + continue; + } + + final List<EStructuralFeature> eStructuralFeatures = eClass.getEAllStructuralFeatures(); + if (eStructuralFeatures.isEmpty()) { + continue; + } + + // Annotation types with a single feature are converted to simple type references in + // the schema. + if (oneMappableFeature(eClass)) { + /* + * final EStructuralFeature eStructuralFeature = (EStructuralFeature) + * eClass.getEStructuralFeatures().get(0); final EClassifier eType = eStructuralFeature.getEType(); + * schemaTypeName = (String) schemaTypeNamesByAnnotationType.get(eType.getName()); if + * (schemaTypeName == null) { schemaTypeName = eType.getName(); } + * schemaTypeNamesByAnnotationType.put(eClassifier.getName(), schemaTypeName); + */ + continue; + } + + final Element complexTypeElement = createSchemaComplexType(eClass.getName()); + elemList.add(complexTypeElement); + + final Element choiceElement = complexTypeElement.addElement("xsd:choice"); + processStructuralFeatures(choiceElement, eStructuralFeatures); + if (choiceElement.getChildren().size() == 0) { + complexTypeElement.getChildren().remove(choiceElement); + } else if (choiceElement.getChildren().size() == 1) { + complexTypeElement.remove(choiceElement); + final Element sequenceElement = complexTypeElement.addElement("xsd:sequence"); + complexTypeElement.remove(sequenceElement); + sequenceElement.add(choiceElement.getChildren().get(0)); + complexTypeElement.add(0, sequenceElement); + } else { + addMinMaxOccurs(choiceElement, "0", "unbounded"); + } + } else if (eClassifier instanceof EEnum) { + elemList.add(processEnum((EEnum) eClassifier)); + } else { + throw new RuntimeException("Unexpected EClassifier: " + eClassifier.eClass().getName()); + } + + schemaTypeNamesByAnnotationType.put(eClassifier.getName(), schemaTypeName); + } + return elemList; + } + + /** If more than one mappable structuralfeature */ + private boolean oneMappableFeature(EClass eclass) { + int cnt = 0; + for (EStructuralFeature ef : eclass.getEStructuralFeatures()) { + if (!isIgnorable(ef) && !isUnsupported(ef)) { + cnt++; + } + } + return cnt == 1; + } + + /** Process an enum */ + private Element processEnum(EEnum eEnum) { + final Element simpleTypeElement = createSchemaSimpleType(eEnum.getName(), null); + final Element restrictionElement = new Element("xsd:restriction"); + restrictionElement.addAttribute(new Attribute("base", "xsd:token")); + simpleTypeElement.addElement(restrictionElement); + final List<EEnumLiteral> literals = eEnum.getELiterals(); + for (EEnumLiteral literal : literals) { + final Element enumerationElement = new Element("xsd:enumeration"); + restrictionElement.addElement(enumerationElement); + enumerationElement.addAttribute(new Attribute("value", literal.getLiteral())); + } + return simpleTypeElement; + } + + /** Do the epackage */ + private Element createEPackageElement() { + final Element epackElement = new Element("xsd:complexType").addAttribute("name", "EPackage"); + final Element choiceElement = epackElement.addElement("xsd:choice"); + processStructuralFeatures(choiceElement, getPAnnotatedEPackage().getEAllStructuralFeatures()); + choiceElement.addElement("xsd:element").addAttribute("name", "eclass").addAttribute("type", "EClass"); + choiceElement.addElement("xsd:element").addAttribute("name", "edatatype").addAttribute("type", "EDataType"); + + addMinMaxOccurs(choiceElement, "0", "unbounded"); + + // add the namespace-uri attribute + epackElement.addElement("xsd:attribute").addAttribute("name", "namespace-uri").addAttribute("type", + "xsd:anyURI").addAttribute("use", "required"); + return epackElement; + } + + /** Do the eClass */ + private Element createEClassElement() { + final Element eclassElement = new Element("xsd:complexType").addAttribute("name", "EClass"); + final Element choiceElement = eclassElement.addElement("xsd:choice"); + processStructuralFeatures(choiceElement, getPAnnotatedEClass().getEAllStructuralFeatures()); + + choiceElement.addElement("xsd:element").addAttribute("name", "eattribute").addAttribute("type", "EAttribute"); + choiceElement.addElement("xsd:element").addAttribute("name", "ereference").addAttribute("type", "EReference"); + choiceElement.addElement("xsd:element").addAttribute("name", "property").addAttribute("type", "Property"); + choiceElement.addElement("xsd:element").addAttribute("name", "edatatype").addAttribute("type", "EDataType"); + + addMinMaxOccurs(choiceElement, "0", "unbounded"); + + eclassElement.addElement("xsd:attribute").addAttribute("name", "name").addAttribute("type", "xsd:token") + .addAttribute("use", "required"); + return eclassElement; + } + + /** Do the eReference element */ + private Element createEReferenceElement() { + final Element erefElement = new Element("xsd:complexType").addAttribute("name", "EReference"); + final Element choiceElement = erefElement.addElement("xsd:choice"); + processStructuralFeatures(choiceElement, getPAnnotatedEReference().getEAllStructuralFeatures()); + addMinMaxOccurs(choiceElement, "0", "unbounded"); + erefElement.addElement("xsd:attribute").addAttribute("name", "name").addAttribute("type", "xsd:token") + .addAttribute("use", "required"); + return erefElement; + } + + /** Do the eAttribute */ + private Element createEAttributeElement() { + final Element eattrElement = new Element("xsd:complexType").addAttribute("name", "EAttribute"); + final Element choiceElement = eattrElement.addElement("xsd:choice"); + processStructuralFeatures(choiceElement, getPAnnotatedEAttribute().getEAllStructuralFeatures()); + addMinMaxOccurs(choiceElement, "0", "unbounded"); + eattrElement.addElement("xsd:attribute").addAttribute("name", "name").addAttribute("type", "xsd:token") + .addAttribute("use", "required"); + return eattrElement; + } + + /** Do the eDataType */ + private Element createEDataTypeElement() { + final Element eattrElement = new Element("xsd:complexType").addAttribute("name", "EDataType"); + final Element choiceElement = eattrElement.addElement("xsd:choice"); + processStructuralFeatures(choiceElement, getPAnnotatedEDataType().getEAllStructuralFeatures()); + addMinMaxOccurs(choiceElement, "0", "unbounded"); + eattrElement.addElement("xsd:attribute").addAttribute("name", "name").addAttribute("type", "xsd:token") + .addAttribute("use", "required"); + return eattrElement; + } + + /** Do the property (comb. of ereference and eattribute */ + private Element createPropertyElement() { + final Element propertyElement = new Element("xsd:complexType").addAttribute("name", "Property"); + final Element choiceElement = propertyElement.addElement("xsd:choice"); + final List<EStructuralFeature> features = new ArrayList<EStructuralFeature>(getPAnnotatedEAttribute() + .getEAllStructuralFeatures()); + features.removeAll(getPAnnotatedEReference().getEAllStructuralFeatures()); + features.addAll(getPAnnotatedEReference().getEAllStructuralFeatures()); + + processStructuralFeatures(choiceElement, features); + addMinMaxOccurs(choiceElement, "0", "unbounded"); + propertyElement.addElement("xsd:attribute").addAttribute("name", "name").addAttribute("type", "xsd:token") + .addAttribute("use", "required"); + return propertyElement; + } + + /** Walk through a pamodel type and add references to each type to the passed element */ + private void processStructuralFeatures(Element mainElement, List<EStructuralFeature> eStructuralFeatures) { + final List<EStructuralFeature> eFeatures = new ArrayList<EStructuralFeature>(eStructuralFeatures); + Collections.sort(eFeatures, new ENamedElementComparator()); + for (EStructuralFeature ef : eFeatures) { + processStructuralFeature(mainElement, ef); + } + } + + /** + * Processes the EStructuralFeatures of a Pamodel EClass. + */ + private void processStructuralFeature(Element parentElement, EStructuralFeature eStructuralFeature) { + final EClassifier eType = eStructuralFeature.getEType(); + + if (isIgnorable(eStructuralFeature) || isIgnorable(eType) || isUnsupported(eType)) { + return; + } + + final int minOccurs = (eStructuralFeature.isRequired() ? 1 : 0); + + // Determine the element name. + final EAnnotation eAnnotation = eStructuralFeature.getEAnnotation(PERSISTENCE_MAPPING_SOURCE); + String elementName = null; + if (eAnnotation != null) { + elementName = eAnnotation.getDetails().get("elementName"); + } + if (elementName == null) { + // No explicit XML element name specified, so derive from the name instead. + elementName = eStructuralFeature.getName(); + if (eStructuralFeature.isMany() && elementName.endsWith("s")) { + elementName = elementName.substring(0, elementName.length() - 1); + } + } + + // check for double occurences, can occur when doing the property tag + // which combines ereference and eattribute features + final String xmlName = convertToXmlName(elementName); + for (Element otherElem : parentElement.getChildren()) { + String name; + if ((name = otherElem.getAttributeValue("name")) != null && name.compareTo(xmlName) == 0) { + return; + } + } + if (parentElement.element(convertToXmlName(elementName)) != null) { + return; + } + + String typeName = schemaTypeNamesByAnnotationType.get(eType.getName()); + if (typeName == null) { + typeName = eType.getName(); + } + + if (eStructuralFeature instanceof EReference) { + // EReferences are represented by child elements. + final Element element = createSchemaElement(elementName, typeName, eStructuralFeature.getName()); + element.addAttribute(new Attribute("minOccurs", String.valueOf(minOccurs))); + if (eStructuralFeature.isMany()) { + element.addAttribute(new Attribute("maxOccurs", "unbounded")); + } + parentElement.addElement(element); + } else { + // EAttributes are represented by attributes and optional child elements in case of many + // multiplicity. + final Element attributeElement = createSchemaAttribute(eStructuralFeature.getName(), typeName, + eStructuralFeature.getName()); + attributeElement.addAttribute(new Attribute("use", (minOccurs == 0 ? "optional" : "required"))); + parentElement.getParent().addElement(attributeElement); + if (eStructuralFeature.isMany()) { + final Element element = createSchemaElement(eStructuralFeature.getName(), typeName, eStructuralFeature + .getName()); + parentElement.addElement(element); + element.addAttribute(new Attribute("minOccurs", "0")); + element.addAttribute(new Attribute("maxOccurs", "unbounded")); + } + } + } + + /** Return the PAnnotatedEClass */ + protected EClass getPAnnotatedEPackage() { + return (EClass) modelEPackage.getEClassifier("PAnnotatedEPackage"); + } + + /** Return the PAnnotatedEClass */ + protected EClass getPAnnotatedEClass() { + return (EClass) modelEPackage.getEClassifier("PAnnotatedEClass"); + } + + /** Return the PAnnotatedEReference */ + protected EClass getPAnnotatedEReference() { + return (EClass) modelEPackage.getEClassifier("PAnnotatedEReference"); + } + + /** Return the PAnnotatedEAttribute */ + protected EClass getPAnnotatedEAttribute() { + return (EClass) modelEPackage.getEClassifier("PAnnotatedEAttribute"); + } + + /** Return the PAnnotatedEDataType */ + protected EClass getPAnnotatedEDataType() { + return (EClass) modelEPackage.getEClassifier("PAnnotatedEDataType"); + } + + /** + * Tests whether an EModelElement can be ignored for persistence mapping. + * + */ + protected static boolean isIgnorable(EModelElement eModelElement) { + final EAnnotation eAnnotation = eModelElement.getEAnnotation(PERSISTENCE_MAPPING_SOURCE); + boolean ignore = false; + if (eAnnotation != null) { + ignore = Boolean.valueOf(eAnnotation.getDetails().get("ignore")).booleanValue(); + } + return ignore; + } + + /** + * Creates an XML Schema element. (<xsd:element>) + */ + private Element createSchemaElement(String name, String type, String eStructuralFeatureName) { + final Element element = new Element("xsd:element"); + element.addAttribute(new Attribute("name", convertToXmlName(name))); + element.addAttribute(new Attribute("type", type)); + if (!name.equals(eStructuralFeatureName)) { + addAppInfoElement(element, eStructuralFeatureName); + } + return element; + } + + /** + * Creates an XML Schema complex type. (<xsd:complexType>) + */ + private Element createSchemaSimpleType(String name, String type) { + final Element element = new Element("xsd:simpleType"); + element.addAttribute(new Attribute("name", name)); + if (type != null) { + element.addAttribute(new Attribute("type", type)); + } + return element; + } + + /** + * Creates an XML Schema attribute element. (<xsd:attribute>) + */ + private Element createSchemaAttribute(String name, String type, String eStructuralFeatureName) { + final Element element = new Element("xsd:attribute"); + element.addAttribute(new Attribute("name", convertToXmlName(name))); + element.addAttribute(new Attribute("type", type)); + if (!name.equals(eStructuralFeatureName)) { + addAppInfoElement(element, eStructuralFeatureName); + } + return element; + } + + private static void addAppInfoElement(final Element element, String eStructuralFeatureName) { + final Element annotationElement = new Element("xsd:annotation"); + element.addElement(annotationElement); + final Element appInfoElement = new Element("xsd:appinfo"); + appInfoElement.addAttribute(new Attribute("source", ESTRUCTURAL_FEATURE_SOURCE_NAME)); + appInfoElement.setText(eStructuralFeatureName); + annotationElement.addElement(appInfoElement); + } + + /** + * Creates an XML Schema complex type. (<xsd:complexType>) + */ + private Element createSchemaComplexType(String name) { + final Element element = new Element("xsd:complexType"); + element.addAttribute(new Attribute("name", name)); + return element; + } + + /** + * Tests whether an EModelElement is unsupported. + * + */ + protected static boolean isUnsupported(EModelElement eModelElement) { + return (eModelElement.getEAnnotation(UNSUPPORTED_SOURCE) != null); + } + + /** Add minOccurs and maxOccurs */ + private void addMinMaxOccurs(Element elem, String min, String max) { + elem.addAttribute("minOccurs", min).addAttribute("maxOccurs", max); + } + + /** + * Converts mixed-case names to XML names. + * <p> + * Example: "generatedValue" -> "generated-value". + * + * @param name + * @return + */ + protected String convertToXmlName(String name) { + StringBuffer sb = new StringBuffer(); + for (int i = 0, n = name.length(); i < n; i++) { + char ch = name.charAt(i); + if (Character.isUpperCase(ch)) { + if (i > 0) { + sb.append('-'); + } + ch = Character.toLowerCase(ch); + } + sb.append(ch); + } + return sb.toString(); + } + + /** EFeature comparator */ + private class ENamedElementComparator implements Comparator<ENamedElement> { + /** Compare features */ + public int compare(ENamedElement e1, ENamedElement e2) { + return e1.getName().compareTo(e2.getName()); + } + } + + /** + * @return the annotationEPackages + */ + public EPackage[] getAnnotationEPackages() { + return annotationEPackages; + } + + /** + * @param annotationEPackages + * the annotationEPackages to set + */ + public void setAnnotationEPackages(EPackage[] annotationEPackages) { + this.annotationEPackages = annotationEPackages; + } + + /** + * @return the modelEPackage + */ + public EPackage getModelEPackage() { + return modelEPackage; + } + + /** + * @param modelEPackage + * the modelEPackage to set + */ + public void setModelEPackage(EPackage modelEPackage) { + this.modelEPackage = modelEPackage; + } + + /** + * @return the nameSpace + */ + public String getNameSpace() { + return nameSpace; + } + + /** + * @param nameSpace + * the nameSpace to set + */ + public void setNameSpace(String nameSpace) { + this.nameSpace = nameSpace; + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/XmlElementToEStructuralFeatureMapper.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/XmlElementToEStructuralFeatureMapper.java new file mode 100755 index 000000000..0c74259f9 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/XmlElementToEStructuralFeatureMapper.java @@ -0,0 +1,83 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: XmlElementToEStructuralFeatureMapper.java,v 1.4 2008/05/27 07:42:09 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.xml; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.eclipse.emf.teneo.annotations.StoreAnnotationsException; +import org.eclipse.emf.teneo.extension.ExtensionPoint; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * Helper class used internally for mapping XML element names to EStructuralFeature names. + */ +public class XmlElementToEStructuralFeatureMapper implements ExtensionPoint { + private Map<String, String> eStructuralFeatureNamesByXmlElementName = new HashMap<String, String>(); + + private String xmlElementName; + + private boolean appInfoValue; + + public void parseSchema(InputStream schema) { + try { + final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + saxParserFactory.setNamespaceAware(true); + final SAXParser saxParser = saxParserFactory.newSAXParser(); + saxParser.parse(this.getClass().getResourceAsStream("persistence-mapping.xsd"), new XmlContentHandler()); + } catch (Exception e) { + throw new StoreAnnotationsException("Exception while parsing xsd", e); + } + } + + public class XmlContentHandler extends DefaultHandler { + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (localName.equals("attribute") || localName.equals("element")) { + xmlElementName = attributes.getValue("name"); + } else if (localName.equals("appinfo") && + PersistenceMappingSchemaGenerator.ESTRUCTURAL_FEATURE_SOURCE_NAME.equals(attributes + .getValue("source"))) { + appInfoValue = true; + } + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + if (appInfoValue && xmlElementName != null) { + final String eStructuralFeatureName = new String(ch, start, length).trim(); + if (eStructuralFeatureName.length() > 0 && + !eStructuralFeatureNamesByXmlElementName.containsKey(xmlElementName)) { + eStructuralFeatureNamesByXmlElementName.put(xmlElementName, eStructuralFeatureName); + appInfoValue = false; + xmlElementName = null; + } + } + } + } + + public String getEStructuralFeatureName(String xmlElementName) { + return eStructuralFeatureNamesByXmlElementName.get(xmlElementName); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/XmlPersistenceContentHandler.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/XmlPersistenceContentHandler.java new file mode 100755 index 000000000..757f47508 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/XmlPersistenceContentHandler.java @@ -0,0 +1,530 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: XmlPersistenceContentHandler.java,v 1.11 2011/04/08 17:47:25 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.xml; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; +import java.util.regex.Pattern; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.teneo.PackageRegistryProvider; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEDataType; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEPackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.annotations.pannotation.PAnnotation; +import org.eclipse.emf.teneo.extension.ExtensionManager; +import org.eclipse.emf.teneo.extension.ExtensionManagerAware; +import org.eclipse.emf.teneo.extension.ExtensionPoint; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * SAX ContentHandler for processing XML persistence mapping. Used internally by + * {@link XmlPersistenceMapper}. + */ +public class XmlPersistenceContentHandler extends DefaultHandler implements + ExtensionPoint, ExtensionManagerAware { + + // Parse states + + // Document root. + private static final int ROOT = 0; + + // <persistence-mapping> + private static final int PERSISTENCE_MAPPING = 1; + + // <epackage> + private static final int EPACKAGE = 2; + + // Annotation element for an <epackage>. + private static final int EPACKAGE_ANNOTATION = 3; + + // <eclass> + private static final int ECLASS = 4; + + // Annotation element for an <eclass>. + private static final int ECLASS_ANNOTATION = 5; + + // <eattribute>, <ereference> or <property>. + private static final int ESTRUCTURALFEATURE = 6; + + // Annotation element for an <eattribute>, <ereference> or <property>. + private static final int ESTRUCTURALFEATURE_ANNOTATION = 7; + + // Annotation element inside another annotation. + private static final int NESTED_ANNOTATION = 8; + + // Value for an annotation element. + private static final int ANNOTATION_ATTRIBUTE = 9; + + // <eclass> + private static final int EDATATYPE = 10; + + // Annotation element for an <eclass>. + private static final int EDATATYPE_ANNOTATION = 11; + + // The pattern to split the XML element names against. + private static Pattern XML_NAME_PATTERN = Pattern.compile("-"); + + private static String convertXmlNameToEStructuralFeatureName(String xmlName) { + final String[] elementNameParts = XML_NAME_PATTERN.split(xmlName); + final StringBuffer featureName = new StringBuffer(); + for (int i = 0; i < elementNameParts.length; i++) { + String part = elementNameParts[i]; + if (i > 0) { + part = part.substring(0, 1).toUpperCase() + part.substring(1); + } + featureName.append(part); + } + return featureName.toString(); + } + + // The PAnnotatedModel that will be populated. + private PAnnotatedModel pAnnotatedModel; + + // The PAnnotatedEPackage to which the XML annotations will be applied. + private PAnnotatedEPackage pAnnotatedEPackage; + + // The current PAnnotatedEClass. + private PAnnotatedEClass pAnnotatedEClass; + + // The current PAnnotatedEDataType. + private PAnnotatedEDataType pAnnotatedEDataType; + + // The current PAnnotatedEStructuralFeature of pAnnotatedEClass. + private PAnnotatedEStructuralFeature pAnnotatedEStructuralFeature; + + // Stack of PAnnotations. + private Stack<PAnnotation> pAnnotations = new Stack<PAnnotation>(); + + // The current EAttribute of the current pAnnotation. Used only for + // EDataTypes. + private EAttribute pAnnotationEAttribute; + + // Stack of parse states. + private Stack<Integer> parseStates = new Stack<Integer>(); + + // prefix for extra efeature parsing + private String prefix; + + // ExtensionManager + private ExtensionManager extensionManager; + + /** The xml element to structural feature mapper */ + private XmlElementToEStructuralFeatureMapper xmlElementToEStructuralFeatureMapper; + + public XmlPersistenceContentHandler() { + parseStates.push(ROOT); + } + + /** Set the schema */ + public void setSchema(InputStream schema) { + xmlElementToEStructuralFeatureMapper = getExtensionManager() + .getExtension(XmlElementToEStructuralFeatureMapper.class); + xmlElementToEStructuralFeatureMapper.parseSchema(schema); + } + + /** + * Returns the current parse state. + */ + protected int getParseState() { + assert (parseStates.size() >= 1) : "Parse state stack must contain at least one element."; + return (parseStates.peek()).intValue(); + } + + protected PAnnotation getPAnnotation() { + return pAnnotations.peek(); + } + + /** + * Applies an annotation on an EObject based on the given XML element name. + * + */ + @SuppressWarnings("unchecked") + protected void applyAnnotation(EObject pAnnotatedEModelElement, + String elementName, Attributes attributes) throws SAXException { + final EStructuralFeature annotationEStructuralFeature = getEStructuralFeature( + pAnnotatedEModelElement, elementName); + if (annotationEStructuralFeature == null) { + throw new SAXException("Cannot handle element <" + elementName + + ">"); + } + + final PAnnotation pAnnotation = (PAnnotation) EcoreUtil + .create((EClass) annotationEStructuralFeature.getEType()); + pAnnotation.setGenerated(false); + pAnnotations.push(pAnnotation); + + if (annotationEStructuralFeature.isMany()) { + ((List<PAnnotation>) pAnnotatedEModelElement + .eGet(annotationEStructuralFeature)).add(pAnnotation); + } else { + pAnnotatedEModelElement.eSet(annotationEStructuralFeature, + pAnnotation); + } + + // Apply attributes to pAnnotation + for (int i = 0, n = attributes.getLength(); i < n; i++) { + final EAttribute eAttribute = (EAttribute) getEStructuralFeature( + pAnnotation, attributes.getLocalName(i)); + final EDataType eDataType = eAttribute.getEAttributeType(); + final Object valueObject = eDataType.getEPackage() + .getEFactoryInstance() + .createFromString(eDataType, attributes.getValue(i)); + if (eAttribute.isMany()) { + ((List<Object>) pAnnotation.eGet(eAttribute)).add(valueObject); + } else { + pAnnotation.eSet(eAttribute, valueObject); + } + } + + } + + /** + * Returns an estructuralfeature on the basis of the name, mainly does + * conversion of the xmlName to the efeaturename, the prefix returned from + * getPrefix is also used. todo: move prefix handling to + * XmlElementToEStructuralFeatureMapper. + */ + protected EStructuralFeature getEStructuralFeature( + EObject pAnnotatedEModelElement, String xmlName) { + String annotationEStructuralFeatureName = convertXmlNameToEStructuralFeatureName(xmlName); + EStructuralFeature annotationEStructuralFeature = pAnnotatedEModelElement + .eClass().getEStructuralFeature( + annotationEStructuralFeatureName); + if (annotationEStructuralFeature == null) { + annotationEStructuralFeatureName = xmlElementToEStructuralFeatureMapper + .getEStructuralFeatureName(xmlName); + annotationEStructuralFeature = pAnnotatedEModelElement.eClass() + .getEStructuralFeature(annotationEStructuralFeatureName); + } + // if still null then try with the prefix + if (annotationEStructuralFeature == null) { + // note if a prefix is added then the first character of the first + // part has to be + // upper-cased + String name = convertXmlNameToEStructuralFeatureName(xmlName); + annotationEStructuralFeatureName = prefix + + name.substring(0, 1).toUpperCase() + name.substring(1); + ; + annotationEStructuralFeature = pAnnotatedEModelElement.eClass() + .getEStructuralFeature(annotationEStructuralFeatureName); + } + return annotationEStructuralFeature; + } + + // -------------------------------------------------------------------- + // Implementation of ContentHandler interface. + // -------------------------------------------------------------------- + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + // Change parse state. + int newParseState; + switch (getParseState()) { + case ROOT: + newParseState = PERSISTENCE_MAPPING; + break; + case PERSISTENCE_MAPPING: + assert (localName.equals("epackage")); + newParseState = EPACKAGE; + break; + case EPACKAGE: + if (localName.equals("eclass")) { + newParseState = ECLASS; + } else if (localName.equals("edatatype")) { + newParseState = EDATATYPE; + } else { + newParseState = EPACKAGE_ANNOTATION; + } + break; + case ECLASS: + if (localName.equals("eattribute") + || localName.equals("ereference") + || localName.equals("property")) { + newParseState = ESTRUCTURALFEATURE; + } else { + newParseState = ECLASS_ANNOTATION; + } + break; + case ESTRUCTURALFEATURE: + newParseState = ESTRUCTURALFEATURE_ANNOTATION; + break; + case EDATATYPE: + newParseState = EDATATYPE_ANNOTATION; + break; + case EPACKAGE_ANNOTATION: + case ECLASS_ANNOTATION: + case ESTRUCTURALFEATURE_ANNOTATION: + case NESTED_ANNOTATION: { + final EStructuralFeature annotationEStructuralFeature = getEStructuralFeature( + getPAnnotation(), localName); + if (annotationEStructuralFeature.getEType() instanceof EClass) { + newParseState = NESTED_ANNOTATION; + } else { + newParseState = ANNOTATION_ATTRIBUTE; + } + break; + } + default: + throw new ParseXMLAnnotationsException( + "Invalid parse state encountered."); + } + parseStates.push(new Integer(newParseState)); + + // Act upon the new parse state. + switch (getParseState()) { + case EPACKAGE: { + final String namespaceUri = attributes.getValue("namespace-uri"); + final EPackage ePackage = PackageRegistryProvider.getInstance() + .getPackageRegistry().getEPackage(namespaceUri); + if (ePackage == null) { + throw new SAXException("Could not find EPackage \"" + + namespaceUri + "\"."); + } + pAnnotatedEPackage = pAnnotatedModel.getPAnnotated(ePackage); + if (pAnnotatedEPackage == null) { + throw new SAXException("Could not find PAnnotatedEPackage \"" + + namespaceUri + "\"."); + } + break; + } + case ECLASS: { + final String eClassName = attributes.getValue("name"); + final EClassifier eClassifier = pAnnotatedEPackage + .getModelEPackage().getEClassifier(eClassName); + if (eClassifier == null) { + throw new SAXException("Could not find EClass \"" + eClassName + + "\""); + } + if (!(eClassifier instanceof EClass)) { + throw new SAXException("EClassifier \"" + eClassName + + "\" is not an EClass."); + } + pAnnotatedEClass = pAnnotatedModel + .getPAnnotated((EClass) eClassifier); + break; + } + case EDATATYPE: { + final String eDataTypeName = attributes.getValue("name"); + final EDataType et = (EDataType) pAnnotatedEPackage + .getModelEPackage().getEClassifier(eDataTypeName); + if (et == null) { + throw new SAXException("Could not find EClass \"" + + eDataTypeName + "\""); + } + pAnnotatedEDataType = pAnnotatedModel.getPAnnotated(et); + break; + } + case ESTRUCTURALFEATURE: { + final String eStructuralFeatureName = attributes.getValue("name"); + final EClass eClass = pAnnotatedEClass.getModelEClass(); + final EStructuralFeature eStructuralFeature = eClass + .getEStructuralFeature(eStructuralFeatureName); + if (eStructuralFeature == null) { + throw new SAXException("Could not find EStructuralFeature \"" + + eStructuralFeatureName + "\" in EClass \"" + + eClass.getName() + "\"."); + } else if (localName.equals("eattribute") + && !(eStructuralFeature instanceof EAttribute)) { + throw new SAXException("EStructuralFeature \"" + + eStructuralFeatureName + "\" in EClass \"" + + eClass.getName() + "\" is not an EAttribute."); + } else if (localName.equals("ereference") + && !(eStructuralFeature instanceof EReference)) { + throw new SAXException("EStructuralFeature \"" + + eStructuralFeatureName + "\" in EClass \"" + + eClass.getName() + "\" is not an EReference."); + } + pAnnotatedEStructuralFeature = pAnnotatedModel + .getPAnnotated(eStructuralFeature); + break; + } + case EPACKAGE_ANNOTATION: + applyAnnotation(pAnnotatedEPackage, localName, attributes); + break; + case ECLASS_ANNOTATION: + applyAnnotation(pAnnotatedEClass, localName, attributes); + break; + case ESTRUCTURALFEATURE_ANNOTATION: + applyAnnotation(pAnnotatedEStructuralFeature, localName, attributes); + break; + case EDATATYPE_ANNOTATION: + applyAnnotation(pAnnotatedEDataType, localName, attributes); + break; + case NESTED_ANNOTATION: { + // final String eStructuralFeatureName = + // convertElementNameToEStructuralFeatureName(localName); + // final EReference annotationEStructuralFeature = (EReference) + // getPAnnotation().eClass() + // .getEStructuralFeature(eStructuralFeatureName); + applyAnnotation(getPAnnotation(), localName, attributes); + break; + } + case ANNOTATION_ATTRIBUTE: { + final String eStructuralFeatureName = convertXmlNameToEStructuralFeatureName(localName); + pAnnotationEAttribute = (EAttribute) getPAnnotation().eClass() + .getEStructuralFeature(eStructuralFeatureName); + break; + } + } + } + + @Override + @SuppressWarnings("unchecked") + public void characters(char[] ch, int start, int length) + throws SAXException { + final String value = new String(ch, start, length).trim(); + if (value.length() == 0) { + return; + } + switch (getParseState()) { + case EPACKAGE_ANNOTATION: + case ECLASS_ANNOTATION: + case ESTRUCTURALFEATURE_ANNOTATION: + case NESTED_ANNOTATION: { + // If we get here, we are dealing with a PAnnotation that has only + // one EAttribute. + // I.e. there are no + // child elements. Example: + // <discriminator-value>MyObject</discriminator-value> + final PAnnotation pAnnotation = getPAnnotation(); + assert (pAnnotation.eClass().getEStructuralFeatures().size() == 1); + final EAttribute eAttribute = (EAttribute) pAnnotation.eClass() + .getEStructuralFeatures().get(0); + final EDataType eAttributeType = eAttribute.getEAttributeType(); + final Object valueObject = eAttributeType.getEPackage() + .getEFactoryInstance() + .createFromString(eAttributeType, value); + if (eAttribute.isMany() && valueObject instanceof String) { + final String[] vals = ((String) valueObject).split(","); + final List<String> valsList = new ArrayList<String>(); + for (String val : vals) { + valsList.add(val); + } + pAnnotation.eSet(eAttribute, valsList); + } else { + pAnnotation.eSet(eAttribute, valueObject); + } + break; + } + case ANNOTATION_ATTRIBUTE: { + final EDataType eDataType = pAnnotationEAttribute + .getEAttributeType(); + final Object valueObject = eDataType.getEPackage() + .getEFactoryInstance().createFromString(eDataType, value); + if (pAnnotationEAttribute.isMany()) { + ((List<Object>) getPAnnotation().eGet(pAnnotationEAttribute)) + .add(valueObject); + } else { + getPAnnotation().eSet(pAnnotationEAttribute, valueObject); + } + break; + } + } + } + + @Override + public void endElement(String uri, String localName, String qName) + throws SAXException { + switch (getParseState()) { + case EPACKAGE_ANNOTATION: + case ECLASS_ANNOTATION: + case ESTRUCTURALFEATURE_ANNOTATION: + case NESTED_ANNOTATION: + pAnnotations.pop(); + break; + case ANNOTATION_ATTRIBUTE: + pAnnotationEAttribute = null; + break; + } + parseStates.pop(); + } + + // -------------------------------------------------------------------- + // Implementation of ErrorHandler interface. + // -------------------------------------------------------------------- + + @Override + public void error(SAXParseException e) throws SAXException { + throw e; + } + + @Override + public void fatalError(SAXParseException e) throws SAXException { + throw e; + } + + /** + * @return the extensionManager + */ + public ExtensionManager getExtensionManager() { + return extensionManager; + } + + /** + * @param extensionManager + * the extensionManager to set + */ + public void setExtensionManager(ExtensionManager extensionManager) { + this.extensionManager = extensionManager; + } + + /** + * @return the pAnnotatedModel + */ + public PAnnotatedModel getPAnnotatedModel() { + return pAnnotatedModel; + } + + /** + * @param annotatedModel + * the pAnnotatedModel to set + */ + public void setPAnnotatedModel(PAnnotatedModel annotatedModel) { + pAnnotatedModel = annotatedModel; + } + + /** + * @return the prefix + */ + public String getPrefix() { + return prefix; + } + + /** + * @param prefix + * the prefix to set + */ + public void setPrefix(String prefix) { + this.prefix = prefix; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/XmlPersistenceMapper.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/XmlPersistenceMapper.java new file mode 100755 index 000000000..f029ec7f3 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/XmlPersistenceMapper.java @@ -0,0 +1,144 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * </copyright> + * + * $Id: XmlPersistenceMapper.java,v 1.5 2008/05/27 07:42:09 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.annotations.xml; + +import java.io.IOException; +import java.io.InputStream; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.extension.ExtensionManager; +import org.eclipse.emf.teneo.extension.ExtensionManagerAware; +import org.eclipse.emf.teneo.extension.ExtensionPoint; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; + +/** + * Populates and configures a PAnnotatedModel using an XML persistence mapping file. + */ +public class XmlPersistenceMapper implements ExtensionPoint, ExtensionManagerAware { + + /** The inputStream containing the xml document */ + private InputStream xmlMapping; + + /** The logger */ + protected static final Log log = LogFactory.getLog(XmlPersistenceMapper.class); + + private ExtensionManager extensionManager; + + /** + * Sets the InputStream containing the XML mapping. + * + * @param xmlMapping + * The InputStream containing the XML persistence mapping. Closed automatically by + * {@link #applyPersistenceMapping(PAnnotatedModel)}. + */ + public void setXmlMapping(InputStream xmlMapping) { + if (xmlMapping == null) { + throw new IllegalArgumentException("XML mapping cannot be null."); + } + this.xmlMapping = xmlMapping; + } + + /** + * Applies the XML persistence mapping to a PAnnotatedModel. + * + * @throws IllegalStateException + * if the XML mapping was not configured. + * @throws RuntimeException + * If there was an error reading or parsing the XML file. + */ + public void applyPersistenceMapping(PAnnotatedModel pAnnotatedModel) { + if (xmlMapping == null) { + throw new IllegalStateException("XML mapping not configured."); + } + + SAXParser saxParser; + try { + final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + saxParserFactory.setNamespaceAware(true); + saxParserFactory.setValidating(true); + + saxParser = saxParserFactory.newSAXParser(); + } catch (ParserConfigurationException e) { + throw new ParseXMLAnnotationsException(e); + } catch (SAXException e) { + throw new ParseXMLAnnotationsException(e); + } + + try { + try { + saxParser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", + "http://www.w3.org/2001/XMLSchema"); + saxParser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaSource", this.getClass() + .getResourceAsStream("persistence-mapping.xsd")); + } catch (SAXNotRecognizedException s) { + log.warn("Properties schemaSource and/or schemaLanguage are not supported, setvalidating=false. " + + "Probably running 1.4 with an old crimson sax parser. Ignoring this and continuing with " + + "a non-validating and name-space-aware sax parser"); + final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + saxParserFactory.setNamespaceAware(true); + saxParserFactory.setValidating(false); + saxParser = saxParserFactory.newSAXParser(); + } + + final XmlPersistenceContentHandler xmlContentHandler = + extensionManager.getExtension(XmlPersistenceContentHandler.class); + xmlContentHandler.setPAnnotatedModel(pAnnotatedModel); + xmlContentHandler.setPrefix(getPrefix()); + xmlContentHandler.setSchema(this.getClass().getResourceAsStream("persistence-mapping.xsd")); + saxParser.parse(xmlMapping, xmlContentHandler); + } catch (SAXException e) { + throw new ParseXMLAnnotationsException(e); + } catch (IOException e) { + throw new ParseXMLAnnotationsException(e); + } catch (ParserConfigurationException e) { + throw new ParseXMLAnnotationsException(e); + } finally { + try { + xmlMapping.close(); + } catch (IOException e) { + // ignoring io exception + } + } + } + + /** Return a prefix which are used by a subpackage to make efeatures unique */ + protected String getPrefix() { + return ""; + } + + /** + * @return the extensionManager + */ + public ExtensionManager getExtensionManager() { + return extensionManager; + } + + /** + * @param extensionManager + * the extensionManager to set + */ + public void setExtensionManager(ExtensionManager extensionManager) { + this.extensionManager = extensionManager; + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/persistence-mapping.xsd b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/persistence-mapping.xsd new file mode 100755 index 000000000..e0a72dabf --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/persistence-mapping.xsd @@ -0,0 +1,433 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<xsd:schema targetNamespace="http://www.eclipse.org/emft/teneo" elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.eclipse.org/emft/teneo"> + <xsd:element name="persistence-mapping" type="PersistenceMapping"/> + <xsd:complexType name="PersistenceMapping"> + <xsd:sequence minOccurs="1" maxOccurs="unbounded"> + <xsd:element name="epackage" type="EPackage"/> + </xsd:sequence> + </xsd:complexType> + <xsd:complexType name="EPackage"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="sequence-generator" type="SequenceGenerator"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">sequenceGenerators</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="table-generator" type="TableGenerator"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">tableGenerators</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="transient" type="Transient"/> + <xsd:element name="eclass" type="EClass"/> + <xsd:element name="edatatype" type="EDataType"/> + </xsd:choice> + <xsd:attribute name="namespace-uri" type="xsd:anyURI" use="required"/> + </xsd:complexType> + <xsd:complexType name="EClass"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="association-override" type="AssociationOverride"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">associationOverrides</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="attribute-override" type="AttributeOverride"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">attributeOverrides</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="discriminator-column" type="DiscriminatorColumn"/> + <xsd:element name="discriminator-value" type="xsd:string"/> + <xsd:element name="embeddable" type="Embeddable"/> + <xsd:element name="entity" type="Entity"/> + <xsd:element name="id-class" type="xsd:string"/> + <xsd:element name="inheritance" type="InheritanceType"/> + <xsd:element name="mapped-superclass" type="MappedSuperclass"/> + <xsd:element name="primary-key-join-column" type="PrimaryKeyJoinColumn"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">primaryKeyJoinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="secondary-table" type="SecondaryTable"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">secondaryTables</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="table" type="Table"/> + <xsd:element name="table-generator" type="TableGenerator"/> + <xsd:element name="transient" type="Transient"/> + <xsd:element name="eattribute" type="EAttribute"/> + <xsd:element name="ereference" type="EReference"/> + <xsd:element name="property" type="Property"/> + <xsd:element name="edatatype" type="EDataType"/> + </xsd:choice> + <xsd:attribute name="name" type="xsd:token" use="required"/> + </xsd:complexType> + <xsd:complexType name="EAttribute"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="attribute-override" type="AttributeOverride"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">attributeOverrides</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="basic" type="Basic"/> + <xsd:element name="column" type="Column"/> + <xsd:element name="enumerated" type="EnumType"/> + <xsd:element name="foreign-key" type="xsd:string"/> + <xsd:element name="generated-value" type="GeneratedValue"/> + <xsd:element name="id" type="Id"/> + <xsd:element name="join-column" type="JoinColumn"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">joinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="join-table" type="JoinTable"/> + <xsd:element name="lob" type="Lob"/> + <xsd:element name="one-to-many" type="OneToMany"/> + <xsd:element name="sequence-generator" type="SequenceGenerator"/> + <xsd:element name="table-generator" type="TableGenerator"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">tableGenerators</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="temporal" type="TemporalType"/> + <xsd:element name="transient" type="Transient"/> + <xsd:element name="version" type="Version"/> + </xsd:choice> + <xsd:attribute name="name" type="xsd:token" use="required"/> + </xsd:complexType> + <xsd:complexType name="EReference"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="attribute-override" type="AttributeOverride"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">attributeOverrides</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="column" type="Column"/> + <xsd:element name="embedded" type="Embedded"/> + <xsd:element name="embedded-id" type="EmbeddedId"/> + <xsd:element name="foreign-key" type="xsd:string"/> + <xsd:element name="join-column" type="JoinColumn"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">joinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="join-table" type="JoinTable"/> + <xsd:element name="many-to-many" type="ManyToMany"/> + <xsd:element name="many-to-one" type="ManyToOne"/> + <xsd:element name="map-key" type="xsd:string"/> + <xsd:element name="one-to-many" type="OneToMany"/> + <xsd:element name="one-to-one" type="OneToOne"/> + <xsd:element name="order-by" type="xsd:string"/> + <xsd:element name="primary-key-join-column" type="PrimaryKeyJoinColumn"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">primaryKeyJoinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="sequence-generator" type="SequenceGenerator"/> + <xsd:element name="table-generator" type="TableGenerator"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">tableGenerators</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="transient" type="Transient"/> + </xsd:choice> + <xsd:attribute name="name" type="xsd:token" use="required"/> + </xsd:complexType> + <xsd:complexType name="Property"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="attribute-override" type="AttributeOverride"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">attributeOverrides</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="basic" type="Basic"/> + <xsd:element name="column" type="Column"/> + <xsd:element name="embedded" type="Embedded"/> + <xsd:element name="embedded-id" type="EmbeddedId"/> + <xsd:element name="enumerated" type="EnumType"/> + <xsd:element name="foreign-key" type="xsd:string"/> + <xsd:element name="generated-value" type="GeneratedValue"/> + <xsd:element name="id" type="Id"/> + <xsd:element name="join-column" type="JoinColumn"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">joinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="join-table" type="JoinTable"/> + <xsd:element name="lob" type="Lob"/> + <xsd:element name="many-to-many" type="ManyToMany"/> + <xsd:element name="many-to-one" type="ManyToOne"/> + <xsd:element name="map-key" type="xsd:string"/> + <xsd:element name="one-to-many" type="OneToMany"/> + <xsd:element name="one-to-one" type="OneToOne"/> + <xsd:element name="order-by" type="xsd:string"/> + <xsd:element name="primary-key-join-column" type="PrimaryKeyJoinColumn"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">primaryKeyJoinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="sequence-generator" type="SequenceGenerator"/> + <xsd:element name="table-generator" type="TableGenerator"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">tableGenerators</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="temporal" type="TemporalType"/> + <xsd:element name="transient" type="Transient"/> + <xsd:element name="version" type="Version"/> + </xsd:choice> + <xsd:attribute name="name" type="xsd:token" use="required"/> + </xsd:complexType> + <xsd:complexType name="EDataType"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="basic" type="Basic"/> + <xsd:element name="column" type="Column"/> + <xsd:element name="enumerated" type="EnumType"/> + <xsd:element name="generated-value" type="GeneratedValue"/> + <xsd:element name="id" type="Id"/> + <xsd:element name="lob" type="Lob"/> + <xsd:element name="temporal" type="TemporalType"/> + <xsd:element name="transient" type="Transient"/> + <xsd:element name="version" type="Version"/> + </xsd:choice> + <xsd:attribute name="name" type="xsd:token" use="required"/> + </xsd:complexType> +<xsd:complexType name="AssociationOverride"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="join-column" type="JoinColumn"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">joinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:choice> + <xsd:attribute name="name" type="xsd:string" use="required"/> +</xsd:complexType> +<xsd:complexType name="AttributeOverride"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="column" type="Column"/> + </xsd:choice> + <xsd:attribute name="name" type="xsd:string" use="required"/> +</xsd:complexType> +<xsd:complexType name="Basic"> + <xsd:attribute name="fetch" type="FetchType" use="optional"/> + <xsd:attribute name="optional" type="xsd:boolean" use="optional"/> +</xsd:complexType> +<xsd:simpleType name="CascadeType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="ALL"/> + <xsd:enumeration value="PERSIST"/> + <xsd:enumeration value="MERGE"/> + <xsd:enumeration value="REMOVE"/> + <xsd:enumeration value="REFRESH"/> + <xsd:enumeration value="NONE"/> + </xsd:restriction> +</xsd:simpleType> +<xsd:complexType name="Column"> + <xsd:attribute name="column-definition" type="xsd:string" use="optional"/> + <xsd:attribute name="insertable" type="xsd:boolean" use="optional"/> + <xsd:attribute name="length" type="xsd:int" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="nullable" type="xsd:boolean" use="optional"/> + <xsd:attribute name="precision" type="xsd:int" use="optional"/> + <xsd:attribute name="scale" type="xsd:int" use="optional"/> + <xsd:attribute name="table" type="xsd:string" use="optional"/> + <xsd:attribute name="unique" type="xsd:boolean" use="optional"/> + <xsd:attribute name="updatable" type="xsd:boolean" use="optional"/> +</xsd:complexType> +<xsd:complexType name="DiscriminatorColumn"> + <xsd:attribute name="column-definition" type="xsd:string" use="optional"/> + <xsd:attribute name="discriminator-type" type="DiscriminatorType" use="optional"/> + <xsd:attribute name="length" type="xsd:int" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:simpleType name="DiscriminatorType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="STRING"/> + <xsd:enumeration value="CHAR"/> + <xsd:enumeration value="INTEGER"/> + </xsd:restriction> +</xsd:simpleType> +<xsd:complexType name="Embeddable"/> +<xsd:complexType name="Embedded"/> +<xsd:complexType name="EmbeddedId"/> +<xsd:complexType name="Entity"> + <xsd:attribute name="extends" type="xsd:string" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:simpleType name="EnumType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="ORDINAL"/> + <xsd:enumeration value="STRING"/> + </xsd:restriction> +</xsd:simpleType> +<xsd:simpleType name="FetchType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="LAZY"/> + <xsd:enumeration value="EAGER"/> + <xsd:enumeration value="EXTRA"/> + </xsd:restriction> +</xsd:simpleType> +<xsd:complexType name="GeneratedValue"> + <xsd:attribute name="generator" type="xsd:string" use="optional"/> + <xsd:attribute name="strategy" type="GenerationType" use="optional"/> +</xsd:complexType> +<xsd:simpleType name="GenerationType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="TABLE"/> + <xsd:enumeration value="SEQUENCE"/> + <xsd:enumeration value="IDENTITY"/> + <xsd:enumeration value="AUTO"/> + </xsd:restriction> +</xsd:simpleType> +<xsd:complexType name="Id"/> +<xsd:simpleType name="InheritanceType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="SINGLE_TABLE"/> + <xsd:enumeration value="TABLE_PER_CLASS"/> + <xsd:enumeration value="JOINED"/> + </xsd:restriction> +</xsd:simpleType> +<xsd:complexType name="JoinColumn"> + <xsd:attribute name="column-definition" type="xsd:string" use="optional"/> + <xsd:attribute name="insertable" type="xsd:boolean" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="nullable" type="xsd:boolean" use="optional"/> + <xsd:attribute name="referenced-column-name" type="xsd:string" use="optional"/> + <xsd:attribute name="table" type="xsd:string" use="optional"/> + <xsd:attribute name="unique" type="xsd:boolean" use="optional"/> + <xsd:attribute name="updatable" type="xsd:boolean" use="optional"/> +</xsd:complexType> +<xsd:complexType name="JoinTable"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="inverse-join-column" type="JoinColumn"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">inverseJoinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="join-column" type="JoinColumn"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">joinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="unique-constraint" type="xsd:string"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">uniqueConstraints</xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:choice> + <xsd:attribute name="catalog" type="xsd:string" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="schema" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:complexType name="Lob"/> +<xsd:complexType name="ManyToMany"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="cascade" type="CascadeType"/> + </xsd:choice> + <xsd:attribute name="cascade" type="CascadeType" use="optional"/> + <xsd:attribute name="fetch" type="FetchType" use="optional"/> + <xsd:attribute name="indexed" type="xsd:boolean" use="optional"/> + <xsd:attribute name="mapped-by" type="xsd:string" use="optional"/> + <xsd:attribute name="target-entity" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:complexType name="ManyToOne"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="cascade" type="CascadeType"/> + </xsd:choice> + <xsd:attribute name="cascade" type="CascadeType" use="optional"/> + <xsd:attribute name="fetch" type="FetchType" use="optional"/> + <xsd:attribute name="optional" type="xsd:boolean" use="optional"/> + <xsd:attribute name="target-entity" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:complexType name="MappedSuperclass"/> +<xsd:complexType name="OneToMany"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="cascade" type="CascadeType"/> + </xsd:choice> + <xsd:attribute name="cascade" type="CascadeType" use="optional"/> + <xsd:attribute name="fetch" type="FetchType" use="optional"/> + <xsd:attribute name="indexed" type="xsd:boolean" use="optional"/> + <xsd:attribute name="mapped-by" type="xsd:string" use="optional"/> + <xsd:attribute name="target-entity" type="xsd:string" use="optional"/> + <xsd:attribute name="unique" type="xsd:boolean" use="optional"/> +</xsd:complexType> +<xsd:complexType name="OneToOne"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="cascade" type="CascadeType"/> + </xsd:choice> + <xsd:attribute name="cascade" type="CascadeType" use="optional"/> + <xsd:attribute name="fetch" type="FetchType" use="optional"/> + <xsd:attribute name="mapped-by" type="xsd:string" use="optional"/> + <xsd:attribute name="optional" type="xsd:boolean" use="optional"/> + <xsd:attribute name="target-entity" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:complexType name="PrimaryKeyJoinColumn"> + <xsd:attribute name="column-definition" type="xsd:string" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="referenced-column-name" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:complexType name="SecondaryTable"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="pk-join-column" type="PrimaryKeyJoinColumn"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">pkJoinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="unique-constraint" type="xsd:string"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">uniqueConstraints</xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:choice> + <xsd:attribute name="catalog" type="xsd:string" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="schema" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:complexType name="SequenceGenerator"> + <xsd:attribute name="allocation-size" type="xsd:int" use="optional"/> + <xsd:attribute name="initial-value" type="xsd:int" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="sequence-name" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:complexType name="Table"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="unique-constraint" type="xsd:string"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">uniqueConstraints</xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:choice> + <xsd:attribute name="catalog" type="xsd:string" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="schema" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:complexType name="TableGenerator"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="unique-constraint" type="xsd:string"> + <xsd:annotation> + <xsd:appinfo source="teneo/internal/EStructuralFeatureName">uniqueConstraints</xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:choice> + <xsd:attribute name="allocation-size" type="xsd:int" use="optional"/> + <xsd:attribute name="catalog" type="xsd:string" use="optional"/> + <xsd:attribute name="initial-value" type="xsd:int" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="pk-column-name" type="xsd:string" use="optional"/> + <xsd:attribute name="pk-column-value" type="xsd:string" use="optional"/> + <xsd:attribute name="schema" type="xsd:string" use="optional"/> + <xsd:attribute name="table" type="xsd:string" use="optional"/> + <xsd:attribute name="value-column-name" type="xsd:string" use="optional"/> +</xsd:complexType> +<xsd:simpleType name="TemporalType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="DATE"/> + <xsd:enumeration value="TIME"/> + <xsd:enumeration value="TIMESTAMP"/> + </xsd:restriction> +</xsd:simpleType> +<xsd:complexType name="Transient"/> +<xsd:complexType name="Version"/> +</xsd:schema>
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/persistence-mapping_previous.xsd b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/persistence-mapping_previous.xsd new file mode 100755 index 000000000..36cca8878 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/annotations/xml/persistence-mapping_previous.xsd @@ -0,0 +1,399 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsd:schema targetNamespace="http://www.elver.org/teneo/persistence" +elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema" +xmlns="http://www.elver.org/teneo/persistence"> + <xsd:element name="persistence-mapping" type="PersistenceMapping"> + <xsd:annotation> + <xsd:documentation>The root element of an instance document.</xsd:documentation> + </xsd:annotation> + </xsd:element> + <xsd:complexType name="PersistenceMapping"> + <xsd:sequence minOccurs="1" maxOccurs="unbounded"> + <xsd:element name="epackage" type="EPackage"/> + </xsd:sequence> + </xsd:complexType> + <xsd:complexType name="EPackage"> + <xsd:sequence> + <xsd:element name="sequence-generator" type="SequenceGenerator" minOccurs="0"/> + <xsd:element name="table-generator" type="TableGenerator" minOccurs="0"/> + <xsd:element name="eclass" type="EClass" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="namespace-uri" type="xsd:anyURI" use="required"/> + </xsd:complexType> + <xsd:complexType name="EClass"> + <xsd:sequence> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="association-override" type="AssociationOverride" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">associationOverrides</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="attribute-override" type="AttributeOverride" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">attributeOverrides</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="discriminator-column" type="DiscriminatorColumn" minOccurs="0"/> + <xsd:element name="discriminator-value" type="xsd:string" minOccurs="0"/> + <xsd:element name="embeddable" type="Embeddable" minOccurs="0"/> + <xsd:element name="id-class" type="xsd:string" minOccurs="0"/> + <xsd:element name="inheritance" type="InheritanceType" minOccurs="0"/> + <xsd:element name="mapped-superclass" type="MappedSuperclass" minOccurs="0"/> + <xsd:element name="primary-key-join-column" type="PrimaryKeyJoinColumn" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">primaryKeyJoinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="secondary-table" type="SecondaryTable" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">secondaryTables</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="table" type="Table" minOccurs="0"/> + <xsd:element name="table-generator" type="TableGenerator" minOccurs="0"/> + </xsd:choice> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="eattribute" type="EAttribute" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element name="ereference" type="EReference" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element name="property" type="Property" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:token" use="required"/> + </xsd:complexType> + <xsd:complexType name="EAttribute"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="attribute-override" type="AttributeOverride" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">attributeOverrides</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="basic" type="Basic" minOccurs="0"/> + <xsd:element name="column" type="Column" minOccurs="0"/> + <xsd:element name="enumerated" type="EnumType" minOccurs="0"/> + <xsd:element name="generated-value" type="GeneratedValue" minOccurs="0"/> + <xsd:element name="id" type="Id" minOccurs="0"/> + <xsd:element name="indexed" type="xsd:boolean" minOccurs="0"/> + <xsd:element name="join-column" type="JoinColumn" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">joinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="join-table" type="JoinTable" minOccurs="0"/> + <xsd:element name="lob" type="Lob" minOccurs="0"/> + <xsd:element name="one-to-many" type="OneToMany" minOccurs="0"/> + <xsd:element name="sequence-generator" type="SequenceGenerator" minOccurs="0"/> + <xsd:element name="table-generator" type="TableGenerator" minOccurs="0"/> + <xsd:element name="temporal" type="TemporalType" minOccurs="0"/> + <xsd:element name="unique" type="xsd:boolean" minOccurs="0"/> + <xsd:element name="version" type="Version" minOccurs="0"/> + </xsd:choice> + <xsd:attribute name="name" type="xsd:token" use="required"/> + </xsd:complexType> + <xsd:complexType name="EReference"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="attribute-override" type="AttributeOverride" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">attributeOverrides</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="column" type="Column" minOccurs="0"/> + <xsd:element name="embedded" type="Embedded" minOccurs="0"/> + <xsd:element name="embedded-id" type="EmbeddedId" minOccurs="0"/> + <xsd:element name="indexed" type="xsd:boolean" minOccurs="0"/> + <xsd:element name="join-column" type="JoinColumn" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">joinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="join-table" type="JoinTable" minOccurs="0"/> + <xsd:element name="many-to-many" type="ManyToMany" minOccurs="0"/> + <xsd:element name="many-to-one" type="ManyToOne" minOccurs="0"/> + <xsd:element name="one-to-many" type="OneToMany" minOccurs="0"/> + <xsd:element name="one-to-one" type="OneToOne" minOccurs="0"/> + <xsd:element name="order-by" type="xsd:string" minOccurs="0"/> + <xsd:element name="primary-key-join-column" type="PrimaryKeyJoinColumn" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">primaryKeyJoinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="sequence-generator" type="SequenceGenerator" minOccurs="0"/> + <xsd:element name="table-generator" type="TableGenerator" minOccurs="0"/> + <xsd:element name="unique" type="xsd:boolean" minOccurs="0"/> + </xsd:choice> + <xsd:attribute name="name" type="xsd:token" use="required"/> + </xsd:complexType> + <xsd:complexType name="Property"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="attribute-override" type="AttributeOverride" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">attributeOverrides</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="basic" type="Basic" minOccurs="0"/> + <xsd:element name="column" type="Column" minOccurs="0"/> + <xsd:element name="embedded" type="Embedded" minOccurs="0"/> + <xsd:element name="embedded-id" type="EmbeddedId" minOccurs="0"/> + <xsd:element name="enumerated" type="EnumType" minOccurs="0"/> + <xsd:element name="generated-value" type="GeneratedValue" minOccurs="0"/> + <xsd:element name="id" type="Id" minOccurs="0"/> + <xsd:element name="indexed" type="xsd:boolean" minOccurs="0"/> + <xsd:element name="join-column" type="JoinColumn" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">joinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="join-table" type="JoinTable" minOccurs="0"/> + <xsd:element name="lob" type="Lob" minOccurs="0"/> + <xsd:element name="many-to-many" type="ManyToMany" minOccurs="0"/> + <xsd:element name="many-to-one" type="ManyToOne" minOccurs="0"/> + <xsd:element name="one-to-many" type="OneToMany" minOccurs="0"/> + <xsd:element name="one-to-one" type="OneToOne" minOccurs="0"/> + <xsd:element name="order-by" type="xsd:string" minOccurs="0"/> + <xsd:element name="primary-key-join-column" type="PrimaryKeyJoinColumn" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">primaryKeyJoinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="sequence-generator" type="SequenceGenerator" minOccurs="0"/> + <xsd:element name="table-generator" type="TableGenerator" minOccurs="0"/> + <xsd:element name="temporal" type="TemporalType" minOccurs="0"/> + <xsd:element name="unique" type="xsd:boolean" minOccurs="0"/> + <xsd:element name="version" type="Version" minOccurs="0"/> + </xsd:choice> + <xsd:attribute name="name" type="xsd:token" use="required"/> + </xsd:complexType> + <xsd:complexType name="AssociationOverride"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="join-column" type="JoinColumn" minOccurs="1" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">joinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:choice> + <xsd:attribute name="name" type="xsd:string" use="required"/> + </xsd:complexType> + <xsd:complexType name="AttributeOverride"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="column" type="Column" minOccurs="1"/> + </xsd:choice> + <xsd:attribute name="name" type="xsd:string" use="required"/> + </xsd:complexType> + <xsd:complexType name="Basic"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + <xsd:attribute name="fetch" type="FetchType" use="optional"/> + <xsd:attribute name="optional" type="xsd:boolean" use="optional"/> + </xsd:complexType> + <xsd:complexType name="Column"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + <xsd:attribute name="column-definition" type="xsd:string" use="optional"/> + <xsd:attribute name="insertable" type="xsd:boolean" use="optional"/> + <xsd:attribute name="length" type="xsd:int" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="nullable" type="xsd:boolean" use="optional"/> + <xsd:attribute name="precision" type="xsd:int" use="optional"/> + <xsd:attribute name="scale" type="xsd:int" use="optional"/> + <xsd:attribute name="table" type="xsd:string" use="optional"/> + <xsd:attribute name="unique" type="xsd:boolean" use="optional"/> + <xsd:attribute name="updatable" type="xsd:boolean" use="optional"/> + </xsd:complexType> + <xsd:complexType name="DiscriminatorColumn"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + <xsd:attribute name="column-definition" type="xsd:string" use="optional"/> + <xsd:attribute name="discriminator-type" type="DiscriminatorType" use="optional"/> + <xsd:attribute name="length" type="xsd:int" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="Embeddable"/> + <xsd:complexType name="Embedded"/> + <xsd:complexType name="EmbeddedId"/> + <xsd:complexType name="GeneratedValue"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + <xsd:attribute name="generator" type="xsd:string" use="optional"/> + <xsd:attribute name="strategy" type="GenerationType" use="optional"/> + </xsd:complexType> + <xsd:complexType name="Id"/> + <xsd:complexType name="JoinColumn"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + <xsd:attribute name="column-definition" type="xsd:string" use="optional"/> + <xsd:attribute name="insertable" type="xsd:boolean" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="nullable" type="xsd:boolean" use="optional"/> + <xsd:attribute name="referenced-column-name" type="xsd:string" use="optional"/> + <xsd:attribute name="table" type="xsd:string" use="optional"/> + <xsd:attribute name="unique" type="xsd:boolean" use="optional"/> + <xsd:attribute name="updatable" type="xsd:boolean" use="optional"/> + </xsd:complexType> + <xsd:complexType name="JoinTable"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="inverse-join-column" type="JoinColumn" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">inverseJoinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="join-column" type="JoinColumn" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">joinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="unique-constraint" type="xsd:string" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">uniqueConstraints</xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:choice> + <xsd:attribute name="catalog" type="xsd:string" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="schema" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="Lob"/> + <xsd:complexType name="ManyToMany"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="cascade" type="CascadeType" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + <xsd:attribute name="cascade" type="CascadeType" use="optional"/> + <xsd:attribute name="fetch" type="FetchType" use="optional"/> + <xsd:attribute name="mapped-by" type="xsd:string" use="optional"/> + <xsd:attribute name="target-entity" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="ManyToOne"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="cascade" type="CascadeType" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + <xsd:attribute name="cascade" type="CascadeType" use="optional"/> + <xsd:attribute name="fetch" type="FetchType" use="optional"/> + <xsd:attribute name="optional" type="xsd:boolean" use="optional"/> + <xsd:attribute name="target-entity" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="MappedSuperclass"/> + <xsd:complexType name="OneToMany"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="cascade" type="CascadeType" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + <xsd:attribute name="cascade" type="CascadeType" use="optional"/> + <xsd:attribute name="fetch" type="FetchType" use="optional"/> + <xsd:attribute name="mapped-by" type="xsd:string" use="optional"/> + <xsd:attribute name="target-entity" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="OneToOne"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="cascade" type="CascadeType" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + <xsd:attribute name="cascade" type="CascadeType" use="optional"/> + <xsd:attribute name="fetch" type="FetchType" use="optional"/> + <xsd:attribute name="mapped-by" type="xsd:string" use="optional"/> + <xsd:attribute name="optional" type="xsd:boolean" use="optional"/> + <xsd:attribute name="target-entity" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="PrimaryKeyJoinColumn"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + <xsd:attribute name="column-definition" type="xsd:string" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="referenced-column-name" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="SecondaryTable"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="pk-join-column" type="PrimaryKeyJoinColumn" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">pkJoinColumns</xsd:appinfo> + </xsd:annotation> + </xsd:element> + <xsd:element name="unique-constraint" type="xsd:string" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">uniqueConstraints</xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:choice> + <xsd:attribute name="catalog" type="xsd:string" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="schema" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="SequenceGenerator"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + <xsd:attribute name="allocation-size" type="xsd:int" use="optional"/> + <xsd:attribute name="initial-value" type="xsd:int" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="sequence-name" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="Table"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="unique-constraint" type="xsd:string" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">uniqueConstraints</xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:choice> + <xsd:attribute name="catalog" type="xsd:string" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="optional"/> + <xsd:attribute name="schema" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="TableGenerator"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element name="unique-constraint" type="xsd:string" minOccurs="0" maxOccurs="unbounded"> + <xsd:annotation> + <xsd:appinfo source="http://annotation.elver.org/internal/EStructuralFeatureName">uniqueConstraints</xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:choice> + <xsd:attribute name="allocation-size" type="xsd:int" use="optional"/> + <xsd:attribute name="catalog" type="xsd:string" use="optional"/> + <xsd:attribute name="initial-value" type="xsd:int" use="optional"/> + <xsd:attribute name="name" type="xsd:string" use="required"/> + <xsd:attribute name="pk-column-name" type="xsd:string" use="optional"/> + <xsd:attribute name="pk-column-value" type="xsd:string" use="optional"/> + <xsd:attribute name="schema" type="xsd:string" use="optional"/> + <xsd:attribute name="table" type="xsd:string" use="optional"/> + <xsd:attribute name="value-column-name" type="xsd:string" use="optional"/> + </xsd:complexType> + <xsd:complexType name="Transient"/> + <xsd:complexType name="Version"/> + <xsd:simpleType name="CascadeType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="ALL"/> + <xsd:enumeration value="PERSIST"/> + <xsd:enumeration value="MERGE"/> + <xsd:enumeration value="REMOVE"/> + <xsd:enumeration value="REFRESH"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="DiscriminatorType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="STRING"/> + <xsd:enumeration value="CHAR"/> + <xsd:enumeration value="INTEGER"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="EnumType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="ORDINAL"/> + <xsd:enumeration value="STRING"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="FetchType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="LAZY"/> + <xsd:enumeration value="EAGER"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="GenerationType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="TABLE"/> + <xsd:enumeration value="SEQUENCE"/> + <xsd:enumeration value="IDENTITY"/> + <xsd:enumeration value="AUTO"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="InheritanceType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="SINGLE_TABLE"/> + <xsd:enumeration value="TABLE_PER_CLASS"/> + <xsd:enumeration value="JOINED"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="TemporalType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="DATE"/> + <xsd:enumeration value="TIME"/> + <xsd:enumeration value="TIMESTAMP"/> + </xsd:restriction> + </xsd:simpleType> +</xsd:schema> diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ClassClassLoaderStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ClassClassLoaderStrategy.java new file mode 100755 index 000000000..c2e620180 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ClassClassLoaderStrategy.java @@ -0,0 +1,87 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ClassClassLoaderStrategy.java,v 1.6 2009/03/30 07:53:05 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.classloader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Just returns the passed class loader. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.6 $ + */ + +public class ClassClassLoaderStrategy implements ClassLoaderStrategy { + /** The logger */ + private static Log log = LogFactory.getLog(ClassClassLoaderStrategy.class); + + /** The caller resolver */ + private static CallerResolver callerResolver; + + // Robust way of creating of the caller resolver + static { + try { + // This can fail if the current SecurityManager does not allow + // RuntimePermission ("createSecurityManager"): + callerResolver = new CallerResolver(); + } catch (SecurityException se) { + // set callerResolver to null and log + log.error("Class class loader resolver could not be created because of SecurityException " + + " just using the class loader of the classclassloader class, error msg: " + se.getMessage(), se); + callerResolver = null; + } + } + + /** + * Indexes into the current method call context with a given offset. + */ + private static Class<?> getCallerClass(int callerOffset) { + if (callerResolver == null) { + return ClassClassLoaderStrategy.class; + } + return callerResolver.getClassContext()[callerOffset]; + } + + /** + * Based on examples in + * http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load-p2.html + * + * A helper class to get the call context. It subclasses SecurityManager to make + * getClassContext() accessible. An instance of CallerResolver only needs to be created, not + * installed as an actual security manager. + */ + private static final class CallerResolver extends SecurityManager { + protected Class<?>[] getClassContext() { + final Class<?>[] clsContext = super.getClassContext(); + return clsContext; + } + } + + /** + * Just returns the classClassLoader + */ + public ClassLoader getClassLoader() { + /* + * 0: SecurityManager.getClassContext 1: getClassContext 2: getCallerClass 3: getClassLoader + * 4: ClassLoaderResolver.getClassLoader 5: app class + */ + final Class<?> clazz = getCallerClass(5); + return clazz.getClassLoader(); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ClassLoaderResolver.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ClassLoaderResolver.java new file mode 100755 index 000000000..26e48f475 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ClassLoaderResolver.java @@ -0,0 +1,76 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ClassLoaderResolver.java,v 1.6 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.classloader; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.teneo.TeneoException; + +/** + * Is responsible for determining which class loader to use. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.6 $ + */ + +public class ClassLoaderResolver { + /** The logger */ + private static Log log = LogFactory.getLog(ClassClassLoaderStrategy.class); + + /** The classloader strategy used */ + private static ClassLoaderStrategy classLoaderStrategy; + + /** Static initializer */ + static { + try { + setClassLoaderStrategy(new ContextClassLoaderStrategy()); + } catch (Exception e) { + throw new TeneoException("Exception when setting default class loader strategy", e); + } + } + + /** Returns a class based on the name */ + public static Class<?> classForName(String name) { + try { + return Class.forName(name, true, getClassLoader()); + } catch (Exception e) { + throw new StoreClassLoadException("Class for name exception ", e); + } + } + + /** Return a classloader */ + public static ClassLoader getClassLoader() { + return classLoaderStrategy.getClassLoader(); + } + + /** + * @return Returns the classLoaderStrategy. + */ + public static ClassLoaderStrategy getClassLoaderStrategy() { + return classLoaderStrategy; + } + + /** + * @param classLoaderStrategy + * The classLoaderStrategy to set. + */ + public static void setClassLoaderStrategy(ClassLoaderStrategy classLoaderStrategy) { + ClassLoaderResolver.classLoaderStrategy = classLoaderStrategy; + log.info("Class loader strategy set to: " + classLoaderStrategy.getClass().getName()); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ClassLoaderStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ClassLoaderStrategy.java new file mode 100755 index 000000000..85850f54e --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ClassLoaderStrategy.java @@ -0,0 +1,32 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ClassLoaderStrategy.java,v 1.6 2009/03/30 06:41:00 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.classloader; + +/** + * Interface for classes which determine which classloader to use. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.6 $ + */ + +public interface ClassLoaderStrategy { + /** + * Return the classloader + */ + public ClassLoader getClassLoader(); +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ContextClassLoaderStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ContextClassLoaderStrategy.java new file mode 100755 index 000000000..98393ed34 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/ContextClassLoaderStrategy.java @@ -0,0 +1,35 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ContextClassLoaderStrategy.java,v 1.3 2008/02/28 07:08:33 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.classloader; + +/** + * Returns the context class loader. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.3 $ + */ + +public class ContextClassLoaderStrategy implements ClassLoaderStrategy { + /** + * Returns the context class loader + */ + public ClassLoader getClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/StoreClassLoadException.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/StoreClassLoadException.java new file mode 100755 index 000000000..9b8e6f753 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/classloader/StoreClassLoadException.java @@ -0,0 +1,49 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: StoreClassLoadException.java,v 1.5 2008/02/28 07:08:33 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.classloader; + +import org.eclipse.emf.teneo.TeneoException; + +/** + * Is used to throw exception when getting a class in the classloader + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.5 $ + */ + +public class StoreClassLoadException extends TeneoException { + + /** + * Serial ID + */ + private static final long serialVersionUID = -952752250000575101L; + + /** + * The constructor, logs the exception also + */ + public StoreClassLoadException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * The constructor, logs the exception also + */ + public StoreClassLoadException(String msg) { + super(msg); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/ecore/EModelResolver.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/ecore/EModelResolver.java new file mode 100755 index 000000000..3ca0eebfe --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/ecore/EModelResolver.java @@ -0,0 +1,107 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: EModelResolver.java,v 1.9 2009/03/30 07:53:05 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.ecore; + +import java.util.List; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.teneo.ERuntime; + +/** + * The EModelResolver allows pluggable access to the underlying ecore model. It maps from + * eclass/efeature names to java member names or to real eclass names. This default implementation + * only returns null. + * + * @author <a href="mtaal@elver.org">Martin Taal</a> + */ +public abstract class EModelResolver { + + /** The default instance of an EcoreResolver */ + private static EModelResolver instance = null; + + /** Return the current ecore resolver */ + public static EModelResolver instance() { + if (instance == null) { + instance = ERuntime.INSTANCE; + } + return instance; + } + + /** Set an EcoreResolver */ + public static void setInstance(EModelResolver modelResolver) { + instance = modelResolver; + } + + /** Clear all internal datastructures */ + public abstract void clear(); + + /** + * @return the EClass for a java class, if not found then the superclass of the javaclass is + * tried + */ + public abstract EClass getEClass(Class<?> javaClass); + + /** Is the epackage registered */ + public abstract boolean isRegistered(EPackage epackage); + + /** Register the epackages */ + public abstract void register(EPackage[] epacks); + + /** @return all java classes and interfaces */ + public abstract List<Class<?>> getAllClassesAndInterfaces(); + + /** @return the java implementation class for an EClass */ + public abstract Class<?> getJavaClass(EClassifier eclassifier); + + /** @return the java interface class for an EClass */ + public abstract Class<?> getJavaInterfaceClass(EClass eclass); + + /** Returns true if the passed EClass has a javaClass representation. */ + public abstract boolean hasImplementationClass(EClassifier eclassifier); + + /** Returns the currently registered epackages */ + public abstract EPackage[] getEPackages(); + + /** Returns null */ + public abstract Object create(EClass eclass); + + /** Returns a java instance of an EClass defined by name */ + public Object create(EPackage epackage, String eclassName) { + final EClass eclass = (EClass) epackage.getEClassifier(eclassName); + if (eclass == null) { + throw new IllegalArgumentException("No EClass " + eclassName + " found in epackage " + epackage.getName()); + } + return create(eclass); + } + + /** + * Returns a java instance of an EClass defined by name, all epackages are searched for this + * eclass. + */ + public Object create(String eclassName) { + for (EPackage epackage : getEPackages()) { + final EClass eclass = (EClass) epackage.getEClassifier(eclassName); + if (eclass != null) { + return create(eclass); + } + } + throw new IllegalArgumentException("No EClass " + eclassName + " found."); + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/DatastoreAware.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/DatastoreAware.java new file mode 100755 index 000000000..e83eb2e4c --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/DatastoreAware.java @@ -0,0 +1,32 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: DatastoreAware.java,v 1.4 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +import org.eclipse.emf.teneo.DataStore; + +/** + * An extension implementing this interface will be able to 'receive' an instance of a Pamodel. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ + +public interface DatastoreAware { + /** Set the relevant pamodel */ + void setDatastore(DataStore dataStore); +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/DefaultExtensionManager.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/DefaultExtensionManager.java new file mode 100755 index 000000000..e92bf87aa --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/DefaultExtensionManager.java @@ -0,0 +1,286 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: DefaultExtensionManager.java,v 1.9 2009/05/22 21:35:10 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +import java.lang.reflect.Constructor; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.teneo.classloader.ClassLoaderResolver; + +/** + * Manages a set of extensions. Currently for each extension point there will always be only one extension instance. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.9 $ + */ + +public class DefaultExtensionManager implements ExtensionManager { + + private static Log log = LogFactory.getLog(DefaultExtensionManager.class); + + // Uses the default for now + private ConcurrentHashMap<String, Extension> extensionRegistry = new ConcurrentHashMap<String, Extension>(); + + // The instances of the extensions + private ConcurrentHashMap<String, ExtensionPoint> extensionInstances = new ConcurrentHashMap<String, ExtensionPoint>(); + + // The constructor cache + private ConcurrentHashMap<String, Constructor<?>> constructorCache = new ConcurrentHashMap<String, Constructor<?>>(); + + public DefaultExtensionManager() { + ExtensionUtil.registerDefaultExtensions(this); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.extension.ExtensionManager#registerExtension(org + * .eclipse.emf.teneo.extension.Extension) + */ + public void registerExtension(Extension extension) { + // check if there is already a non-default plugin registered with the + // same name + if (extension.isDefaultExtension()) { + final Extension currentExtension = extensionRegistry.get(extension.getPoint()); + if (currentExtension != null && !currentExtension.isDefaultExtension()) { + log.debug("Not registering extension " + extension); + log.debug("There is already a user plugin defined: " + currentExtension); + return; + } + } + if (extension.getPoint() == null) { + throw new TeneoExtensionException("Point of extension may not be null"); + } + if (extension.getClassName() == null) { + throw new TeneoExtensionException("Classname of extension: " + extension.getPoint() + " may not be null"); + } + log.debug("Registering " + extension); + extensionRegistry.put(extension.getPoint(), extension); + + // remove any instances for this extension + extensionInstances.remove(extension.getPoint()); + } + + public void registerExtension(String point, String className) { + final Extension currentExtension = extensionRegistry.get(point); + if (currentExtension == null) { + throw new TeneoExtensionException("No default extension found using point: " + point + + " is the point value correct?"); + } + final Extension newExtension = new Extension(); + newExtension.setPoint(point); + newExtension.setClassName(className); + newExtension.setDefaultExtension(false); + newExtension.setSingleton(currentExtension.isSingleton()); + registerExtension(newExtension); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.extension.ExtensionManager#getExtension(java.lang .String) + */ + public ExtensionPoint getExtension(String point, Object[] initArgs) { + log.debug("Searching extension " + point); + final Extension extension = extensionRegistry.get(point); + if (extension == null) { + throw new TeneoExtensionException("Extension point " + point + " not registered"); + } + + if (extension.isSingleton()) { + final ExtensionPoint extensionInstance = extensionInstances.get(point); + if (extensionInstance != null) { + log.debug("Found instance " + extensionInstance.getClass().getClass()); + return extensionInstance; + } + } + + // get the clz + // note that before the classloader was retrieved as an extension + // however this is not logical, always use the classloaderresolver for + // this + final Class<?> clz = ClassLoaderResolver.classForName(extension.getClassName()); + + // check if this class indeed implements ExtensionPoint + if (!(ExtensionPoint.class.isAssignableFrom(clz))) { + throw new TeneoExtensionException("The requested extension " + clz.getName() + + " does not implement the interface " + ExtensionPoint.class.getName()); + } + + try { + final boolean constructorUsed; + final ExtensionPoint extensionInstance; + if (initArgs == null || initArgs.length == 0) { // use default + // constructor + constructorUsed = false; + extensionInstance = (ExtensionPoint) clz.newInstance(); + } else { + log.debug("Initargs passed, using constructor for class " + clz.getName()); + constructorUsed = true; + final Constructor<?> constructor = getConstructor(clz, initArgs); + extensionInstance = (ExtensionPoint) constructor.newInstance(initArgs); + } + log.debug("Created extensionPoint instance: " + extensionInstance.getClass().getName()); + + if (extensionInstance instanceof ExtensionManagerAware) { + ((ExtensionManagerAware) extensionInstance).setExtensionManager(this); + } + + if (extensionInstance instanceof ExtensionInitializable) { + log.debug("Initializing extension " + extensionInstance.getClass().getName()); + ((ExtensionInitializable) extensionInstance).initializeExtension(); + } + + // note if a constructor is used instances are never cached because + // we assume + // that instances always differ + if (extension.isSingleton() && !constructorUsed) { + log.debug("Caching extension instance as singleton " + extension); + extensionInstances.put(point, extensionInstance); + + // now see if the extensioninstance also implements other + // extensionpoints + registerForAllExtensionPoints(extensionInstance.getClass(), extensionInstance); + } + if (extension.isSingleton() && constructorUsed) { + // disabled as it is not so meaningfull + // log.warn("The extension: " + extension.getPoint() + + // " is declared as a singleton but this getInstance call " + + // " passed initialization parameters so it is not cached, " + clz.getName()); + } + + return extensionInstance; + } catch (Exception e) { + throw new TeneoExtensionException("Exception while instantiating: " + extension.getClassName(), e); + } + } + + /** Return the constructor for a class and initialization arguments */ + protected Constructor<?> getConstructor(Class<?> clz, Object[] initArgs) throws NoSuchMethodException { + Constructor<?> result = null; + final Class<?>[] initTypes = new Class<?>[initArgs.length]; + int i = 0; + final StringBuffer keyStr = new StringBuffer(); + for (Object o : initArgs) { + if (keyStr.length() > 0) { + keyStr.append(","); + } + if (o == null) { + initTypes[i++] = null; + keyStr.append("null"); + } else { + initTypes[i++] = o.getClass(); + keyStr.append(o.getClass().getName()); + } + } + + final String key = clz.getName() + keyStr; + + if ((result = constructorCache.get(key)) != null) { + return result; + } + + for (Constructor<?> constructor : clz.getConstructors()) { + if (constructor.getParameterTypes().length != initTypes.length) { + continue; + } + int j = 0; + boolean found = true; + for (Class<?> paramType : constructor.getParameterTypes()) { + final Class<?> argumentType = initTypes[j++]; + if (argumentType == null && !Object.class.isAssignableFrom(paramType)) { + found = false; + break; + } else if (argumentType == null && Object.class.isAssignableFrom(paramType)) { + // just continue + } else if (!paramType.isAssignableFrom(argumentType)) { + found = false; + break; + } + } + if (found) { + result = constructor; + constructorCache.put(key, result); + break; + } + } + if (result == null) { + throw new TeneoExtensionException("No constructor found for : " + clz.getName() + + " and constructor argument types: " + keyStr); + } + return result; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.extension.ExtensionManager#getExtension(java.lang .Class) + */ + @SuppressWarnings("unchecked") + public <T> T getExtension(Class<T> clz) { + return (T) getExtension(clz.getName(), null); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.extension.ExtensionManager#getExtension(java.lang .Class) + */ + @SuppressWarnings("unchecked") + public <T> T getExtension(Class<T> clz, Object[] initArgs) { + return (T) getExtension(clz.getName(), initArgs); + } + + /** + * Registers an instance for all other extensionpoints it implements if no other instance was already registered for + * it. + */ + private void registerForAllExtensionPoints(Class<?> cls, ExtensionPoint extensionInstance) { + if (cls == null) { + return; + } + + // for its interfaces + for (Class<?> interf : cls.getInterfaces()) { + checkRegister(extensionRegistry.get(interf.getName()), extensionInstance); + } + + // and for the class itself + checkRegister(extensionRegistry.get(cls.getName()), extensionInstance); + + // and not the superclass, the check for null is done in the method + // itself + registerForAllExtensionPoints(cls.getSuperclass(), extensionInstance); + } + + // register the passed instance if it implements the extension and its class + // is registered for that extension + private void checkRegister(Extension extension, ExtensionPoint extensionInstance) { + if (extension == null) { + return; + } + if (extension.getClassName().compareTo(extensionInstance.getClass().getName()) == 0 && extension.isSingleton() + && extensionInstances.get(extension.getPoint()) == null) { + log.debug("Also registering extensioninstance: " + extensionInstance.getClass().getName() + + " for extension " + extension.getPoint()); + extensionInstances.put(extension.getPoint(), extensionInstance); + } + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/Extension.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/Extension.java new file mode 100755 index 000000000..8f85e3e6c --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/Extension.java @@ -0,0 +1,109 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: Extension.java,v 1.4 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +/** + * An Extension sets a certain ExtensionPoint in Teneo. Teneo will register default Extensions which + * can be overridden by a user. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ + +public class Extension { + + // the extension point id, normally a classname of an ExtensionPoint + private String point; + + // the classname of the extensionpoint implementor + private String className; + + // Is this a default extension registered by Teneo or a user plugin + private boolean defaultExtension = false; + + // Is this a singleton within one ExtensionManager, default is true + private boolean singleton = true; + + /** + * @return the point + */ + public String getPoint() { + return point; + } + + /** + * @param point + * the point to set + */ + public void setPoint(String point) { + this.point = point; + } + + /** + * @return the className + */ + public String getClassName() { + return className; + } + + /** + * @param className + * the className to set + */ + public void setClassName(String className) { + this.className = className; + } + + /** + * @return the defaultExtension + */ + public boolean isDefaultExtension() { + return defaultExtension; + } + + /** + * Default is false, this is the correct value for extensions created by users of Teneo. So + * normally this method does not need to be called. + * + * @param defaultExtension + * the defaultExtension to set + */ + public void setDefaultExtension(boolean defaultExtension) { + this.defaultExtension = defaultExtension; + } + + @Override + public String toString() { + return " point: " + getPoint() + " classname: " + getClassName() + " default: " + isDefaultExtension(); + } + + /** + * @return the singleton + */ + public boolean isSingleton() { + return singleton; + } + + /** + * @param singleton + * the singleton to set + */ + public void setSingleton(boolean singleton) { + this.singleton = singleton; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionInitializable.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionInitializable.java new file mode 100755 index 000000000..94d0b76a4 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionInitializable.java @@ -0,0 +1,31 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ExtensionInitializable.java,v 1.4 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +/** + * Defines an initialize method which is called after creating the object and after setting the + * extensionmanager (if the object is ExtensionManagerAware). + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ + +public interface ExtensionInitializable { + /** Initialize the object */ + void initializeExtension(); +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionManager.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionManager.java new file mode 100755 index 000000000..a8357a779 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionManager.java @@ -0,0 +1,60 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ExtensionManager.java,v 1.6 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +/** + * Manages a set of extensions. Currently for each extension point there will always be only one + * extension instance. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.6 $ + */ +public interface ExtensionManager { + + /** + * Register an extension. If there is already a non-default extension registered then it is not + * overwritten. + */ + public abstract void registerExtension(Extension extension); + + /** + * Return an instance of an extension, pass null if no constructor arguments are required for + * this extension + */ + public abstract ExtensionPoint getExtension(String point, Object[] initArgs); + + /** + * Convenience method which also performs the casting and uses the classname of the class + * parameter to search for the plugin. + */ + public abstract <T> T getExtension(Class<T> clz); + + /** + * Convenience method which also performs the casting and uses the classname of the class + * parameter to search for the plugin. + */ + public abstract <T> T getExtension(Class<T> clz, Object[] initArgs); + + /** + * Convenience method to register a user extension overriding a current extension. It will + * search for an existing extension using the point. If not found then an exception is thrown. + * In case a completely new extension is to be registered then use the registerExtension method. + * The singleton value from the existing extension is used. value is used from that extension. + */ + public void registerExtension(String point, String className); +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionManagerAware.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionManagerAware.java new file mode 100755 index 000000000..abab306e4 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionManagerAware.java @@ -0,0 +1,31 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ExtensionManagerAware.java,v 1.4 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +/** + * An extension implementing this interface will be able to 'receive' an instance of the + * ExtensionManager which created it. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ + +public interface ExtensionManagerAware { + /** Set the extensionManager */ + void setExtensionManager(ExtensionManager extensionManager); +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionManagerFactory.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionManagerFactory.java new file mode 100755 index 000000000..04675124b --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionManagerFactory.java @@ -0,0 +1,48 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ExtensionManagerFactory.java,v 1.4 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +/** + * Factory which creates ExtensionManagers. A customer factory can be set by calling setInstance(). + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ +public class ExtensionManagerFactory { + + private static ExtensionManagerFactory instance = new ExtensionManagerFactory(); + + /** + * @return the instance + */ + public static ExtensionManagerFactory getInstance() { + return instance; + } + + /** + * @param instance + * the instance to set + */ + public static void setInstance(ExtensionManagerFactory instance) { + ExtensionManagerFactory.instance = instance; + } + + public ExtensionManager create() { + return new DefaultExtensionManager(); + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionPoint.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionPoint.java new file mode 100755 index 000000000..29ed16a3d --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionPoint.java @@ -0,0 +1,30 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ExtensionPoint.java,v 1.4 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +/** + * Is a marker interface to mark a class to be replacable by a user extension. The classname or + * interface implementing this interface is also the name of the ExtensionPoint (the value of the + * point attribute in the extension). + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ + +public interface ExtensionPoint { +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionUtil.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionUtil.java new file mode 100755 index 000000000..315ad62d8 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/ExtensionUtil.java @@ -0,0 +1,302 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ExtensionUtil.java,v 1.17 2011/10/29 06:12:49 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.mapper.AnnotationGenerator; +import org.eclipse.emf.teneo.annotations.mapper.BasicPamodelBuilder; +import org.eclipse.emf.teneo.annotations.mapper.BidirectionalManyToManyAnnotator; +import org.eclipse.emf.teneo.annotations.mapper.EClassAnnotator; +import org.eclipse.emf.teneo.annotations.mapper.EDataTypeAnnotator; +import org.eclipse.emf.teneo.annotations.mapper.EFeatureAnnotator; +import org.eclipse.emf.teneo.annotations.mapper.ManyToOneReferenceAnnotator; +import org.eclipse.emf.teneo.annotations.mapper.OneToManyAttributeAnnotator; +import org.eclipse.emf.teneo.annotations.mapper.OneToManyReferenceAnnotator; +import org.eclipse.emf.teneo.annotations.mapper.OneToOneReferenceAnnotator; +import org.eclipse.emf.teneo.annotations.mapper.PersistenceFileProvider; +import org.eclipse.emf.teneo.annotations.mapper.PersistenceMappingBuilder; +import org.eclipse.emf.teneo.annotations.mapper.SingleAttributeAnnotator; +import org.eclipse.emf.teneo.annotations.mapper.UnidirectionalManyToManyAnnotator; +import org.eclipse.emf.teneo.annotations.parser.EAnnotationParserImporter; +import org.eclipse.emf.teneo.annotations.xml.XmlElementToEStructuralFeatureMapper; +import org.eclipse.emf.teneo.annotations.xml.XmlPersistenceContentHandler; +import org.eclipse.emf.teneo.annotations.xml.XmlPersistenceMapper; +import org.eclipse.emf.teneo.mapping.strategy.EntityNameStrategy; +import org.eclipse.emf.teneo.mapping.strategy.SQLNameStrategy; +import org.eclipse.emf.teneo.mapping.strategy.impl.EntityResolvingNameStrategy; +import org.eclipse.emf.teneo.mapping.strategy.impl.TeneoSQLNameStrategy; + +/** + * Contains simple utility methods. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.17 $ + */ + +public class ExtensionUtil { + + /** Creates a default extension */ + public static Extension createExtension(Class<?> extensionPoint, + Class<?> clz) { + return createExtension(extensionPoint, clz, true); + } + + public static Extension createExtension(Class<?> extensionPoint, + Class<?> clz, boolean defaultExtension) { + final Extension extension = new Extension(); + extension.setPoint(extensionPoint.getName()); + extension.setClassName(clz.getName()); + extension.setDefaultExtension(defaultExtension); + return extension; + } + + public static Extension createExtension(String pointClassName, + boolean singleton) { + final Extension extension = new Extension(); + extension.setPoint(pointClassName); + extension.setClassName(pointClassName); + extension.setDefaultExtension(true); + extension.setSingleton(singleton); + return extension; + } + + public static Extension createExtension(String pointClassName, + String className, boolean singleton) { + final Extension extension = new Extension(); + extension.setPoint(pointClassName); + extension.setClassName(className); + extension.setDefaultExtension(true); + extension.setSingleton(singleton); + return extension; + } + + /** Register a number of default Extensions */ + public static void registerDefaultExtensions(ExtensionManager em) { + + // the ones coming from this plugin + em.registerExtension(createExtension(BasicPamodelBuilder.class, + BasicPamodelBuilder.class)); + em.registerExtension(createExtension(AnnotationGenerator.class, + AnnotationGenerator.class)); + em.registerExtension(createExtension(EAnnotationParserImporter.class, + EAnnotationParserImporter.class)); + em.registerExtension(createExtension(PersistenceMappingBuilder.class, + PersistenceMappingBuilder.class)); + em.registerExtension(createExtension(XmlPersistenceMapper.class, + XmlPersistenceMapper.class)); + + em.registerExtension(createExtension(PersistenceFileProvider.class, + PersistenceFileProvider.class)); + + // from now on always use the classloader + // em.registerExtension(createExtension(ClassLoaderStrategy.class, + // ContextClassLoaderStrategy.class)); + em.registerExtension(createExtension(EntityNameStrategy.class, + EntityResolvingNameStrategy.class)); + em.registerExtension(createExtension(SQLNameStrategy.class, + TeneoSQLNameStrategy.class)); + em.registerExtension(createExtension( + XmlPersistenceContentHandler.class, + XmlPersistenceContentHandler.class)); + em.registerExtension(createExtension( + XmlElementToEStructuralFeatureMapper.class, + XmlElementToEStructuralFeatureMapper.class)); + em.registerExtension(createExtension(PersistenceOptions.class, + PersistenceOptions.class)); + + // annotator related + em.registerExtension(createExtension(EClassAnnotator.class, + EClassAnnotator.class)); + em.registerExtension(createExtension(EFeatureAnnotator.class, + EFeatureAnnotator.class)); + em.registerExtension(createExtension(OneToManyAttributeAnnotator.class, + OneToManyAttributeAnnotator.class)); + em.registerExtension(createExtension(SingleAttributeAnnotator.class, + SingleAttributeAnnotator.class)); + em.registerExtension(createExtension( + BidirectionalManyToManyAnnotator.class, + BidirectionalManyToManyAnnotator.class)); + em.registerExtension(createExtension( + UnidirectionalManyToManyAnnotator.class, + UnidirectionalManyToManyAnnotator.class)); + em.registerExtension(createExtension(EDataTypeAnnotator.class, + EDataTypeAnnotator.class)); + em.registerExtension(createExtension(OneToManyReferenceAnnotator.class, + OneToManyReferenceAnnotator.class)); + em.registerExtension(createExtension(OneToOneReferenceAnnotator.class, + OneToOneReferenceAnnotator.class)); + em.registerExtension(createExtension(ManyToOneReferenceAnnotator.class, + ManyToOneReferenceAnnotator.class)); + + // from the hibernate plugin + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.HbContext", true)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.econtainer.EContainerAccessor", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.econtainer.EContainerFeatureIDAccessor", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.econtainer.EContainerFeatureIDPropertyHandler", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.econtainer.EContainerPropertyHandler", + false)); + + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.property.EListPropertyHandler", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.property.EReferencePropertyHandler", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.property.FeatureMapEntryFeatureURIPropertyHandler", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.property.WildCardAttributePropertyHandler", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.property.WildCardReferencePropertyHandler", + false)); + + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.econtainer.NewEContainerFeatureIDPropertyHandler", + false)); + + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.property.FeatureMapEntryPropertyHandler", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.property.FeatureMapPropertyHandler", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.property.VersionPropertyHandler", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.EMFInterceptor", false)); + + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.tuplizer.EMFEntityNameResolver", + false)); + + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.elist.HibernatePersistableEList", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.elist.HibernatePersistableEMap", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.elist.HibernatePersistableFeatureMap", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.elist.HbExtraLazyPersistableEList", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.elist.HbExtraLazyPersistableEMap", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.elist.MapHibernatePersistableEMap", + false)); + + // hibernate mapper + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.HibernateMappingGenerator", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.MappingContext", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.BasicMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.EmbeddedMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.EntityMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.FeatureMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.IdMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.ManyAttributeMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.ManyToManyMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.ManyToOneMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.MappingContext", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.OneToManyMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.OneToOneMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapper.ManyExternalReferenceMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.EMFInitializeCollectionEventListener", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.hibernate.mapping.eav.EAVMergeEventListener", + false)); + + // jpox mapping + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.association.EmbeddedMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.association.ManyToManyMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.association.ManyToOneMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.association.OneToManyMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.association.OneToOneMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.property.BasicMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.property.ColumnMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.property.EClassFeatureMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.property.IdMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.property.InheritanceMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.property.JoinColumnMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.property.ManyBasicMapper", + false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.property.TableMapper", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.JPOXMappingGenerator", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.MappingContext", false)); + em.registerExtension(createExtension( + "org.eclipse.emf.teneo.jpox.mapper.NamingHandler", false)); + + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/PaModelAware.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/PaModelAware.java new file mode 100755 index 000000000..11da02622 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/PaModelAware.java @@ -0,0 +1,33 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: PaModelAware.java,v 1.4 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; + +/** + * A extension implementing this interface will be able to 'receive' an instance of a + * PAnnotatedModel. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ + +public interface PaModelAware { + /** Set the relevant pamodel */ + void setPaModel(PAnnotatedModel paModel); +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/PersistenceOptionsAware.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/PersistenceOptionsAware.java new file mode 100755 index 000000000..c5ff6cd53 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/PersistenceOptionsAware.java @@ -0,0 +1,33 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: PersistenceOptionsAware.java,v 1.4 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +import org.eclipse.emf.teneo.PersistenceOptions; + +/** + * An extension implementing this interface will be able to 'receive' an instance of the + * PersistenceOptions. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ + +public interface PersistenceOptionsAware { + /** Set the relevant pamodel */ + void setPersistenceOptions(PersistenceOptions persistenceOptions); +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/TeneoExtensionException.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/TeneoExtensionException.java new file mode 100755 index 000000000..a9217b87d --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/extension/TeneoExtensionException.java @@ -0,0 +1,48 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: TeneoExtensionException.java,v 1.2 2008/02/28 07:08:33 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.extension; + +import org.eclipse.emf.teneo.TeneoException; + +/** + * Is thrown in case of an illegal situation in handling extensions. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.2 $ + */ + +public class TeneoExtensionException extends TeneoException { + /** + * Serializable id + */ + private static final long serialVersionUID = 7433341056815136417L; + + /** + * The constructor, logs the exception also + */ + public TeneoExtensionException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * The constructor, logs the exception also + */ + public TeneoExtensionException(String msg) { + super(msg); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/MapPersistableEMap.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/MapPersistableEMap.java new file mode 100755 index 000000000..80cfff2e3 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/MapPersistableEMap.java @@ -0,0 +1,254 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Douglas Bitting + * Martin Taal + * + * </copyright> + * + * $Id: MapPersistableEMap.java,v 1.6 2009/03/30 07:53:04 mtaal Exp $ + */ +package org.eclipse.emf.teneo.mapping.elist; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.common.util.BasicEMap; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; + +/** + * A persistable emap which is mapped as a real map to the db. It differs from its parent class + * (PersistableEMap) because that class assumes that the EMap is mapped to the db as a list. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @author <a href="mailto:jdboudreault@gmail.com">Jean-Denis Boudreault</a> + * + * @version $Revision: 1.6 $ + */ +public abstract class MapPersistableEMap<K, V> extends PersistableEMap<K, V> implements + PersistableDelegateList<BasicEMap.Entry<K, V>> { + + private static final long serialVersionUID = 1L; + + /** The logger */ + private static Log log = LogFactory.getLog(MapPersistableEMap.class); + + /** + * The persisted map handled by the orm layer. This delegate is the map we receive from the + * database provider. It is kept all the time, any changes to the PersistableEMap are replicated + * to the ormMap. + * + * This field will be null unless there is a map waiting to be lazy loaded + */ + protected Map<K, V> ormMapDelegate = null; + + // Is this a map with primitives + private final boolean mapValueIsEAttribute; + + /** Not supported constructor */ + public MapPersistableEMap(EClass entryEClass, EList<BasicEMap.Entry<K, V>> delegateEList) { + super(entryEClass, delegateEList); + throw new UnsupportedOperationException("Explicitly passing delegate list is not supported!"); + } + + /** Constructor */ + public MapPersistableEMap(EClass entryEClass, Class<?> entryClass, InternalEObject owner, EStructuralFeature feature) { + // invoke constructor with no lazyLoadMapDelegate + this(entryEClass, owner, feature, (java.util.Map<K, V>) null); + } + + /** + * This version will set the lazyLoadMapDelegate if it is set. This version will prepare lazy + * lading if available + * + * @param entryEClass + * @param entryClass + * @param owner + * @param featureID + * @param lazyLoadDelegate + * a java.util.map that is a proxy collection taht will be used when lazy load is + * invoked. if it is null, then the map is considered as loaded + */ + public MapPersistableEMap(EClass entryEClass, InternalEObject owner, EStructuralFeature feature, + Map<K, V> ormMapDelegate) { + super(entryEClass, owner, feature, new ArrayList<Entry<K, V>>()); + + setORMMapDelegate(ormMapDelegate); + + // create our list as empty for now + setDelegateEList(owner, feature, this.newList()); + + if (isInitialized()) { + // perform eager loading if the underlying list has been pre-loaded + setLoaded(); + + // sets the size of this map, depending on its load status + size(); + } + + log.debug("Created persistable emap for entry eclass " + entryEClass.getName()); + mapValueIsEAttribute = entryEClass.getEStructuralFeature("value") instanceof EAttribute; + } + + /** + * This version will create the lsit completely, there is no lazy lading from this constructor + * + * @param entryEClass + * @param entryClass + * @param owner + * @param featureID + * @param ormMapDelegate + * a java.util.map that is a proxy collection taht will be used when lazy load is + * invoked. if it is null, then the map is considered as loaded + */ + public MapPersistableEMap(EClass entryEClass, InternalEObject owner, EStructuralFeature feature, + List<BasicEMap.Entry<K, V>> list) { + super(entryEClass, owner, feature, list); + this.setORMMapDelegate(null); + + // this should do nothing but set us as already loaded + setLoaded(); + + // sets the size of this map + size(); + + log.debug("Created persistable emap for entry eclass " + entryEClass.getName()); + + mapValueIsEAttribute = entryEClass.getEStructuralFeature("value") instanceof EAttribute; + } + + /** Does nothing here */ + @Override + protected void setDelegateEList(InternalEObject owner, EStructuralFeature feature, + List<BasicEMap.Entry<K, V>> delegateORMList) { + } + + /** Needs to be implemented by concrete subclass, does nothing here */ + @Override + protected EList<BasicEMap.Entry<K, V>> createDelegateEList(InternalEObject owner, EStructuralFeature feature, + List<BasicEMap.Entry<K, V>> delegateORMList) { + throw new UnsupportedOperationException("This method should not be called!"); + } + + /** + * Override this method to determine if the ormmapdelegate colelction has been eagerly loaded or + * not + * + * @return + */ + protected abstract boolean isORMMapDelegateLoaded(); + + /** + * this method will check the status of the lazy loaded delegate and if ti is eager lodaed, + * perform our loading too. + */ + protected void setLoaded() { + // now, we do a check to see if the lazyLoadedDelegate was eager loaded. + // it was, this method will wrap its data and set ourselves as loaded + if (this.getORMMapDelegate() == null) { + this.setLoaded(true); + } else if (this.getORMMapDelegate() != null && isORMMapDelegateLoaded()) { + this.load(); + } else { + this.setLoaded(false); + } + } + + /** Return the delegate list without doing a load */ + @Override + public Object getDelegate() { + // if there is a delegate then return that one + // this ensures that hibernate always sees its map back + if (getORMMapDelegate() != null) { + return getORMMapDelegate(); + } + // todo: throw error here? + return map(); + } + + /** Replace the delegate */ + @Override + @SuppressWarnings("unchecked") + public void replaceDelegate(Object newDelegate) { + // set the ormmapdelegate to null to handle the clear action + setORMMapDelegate(null); + doClear(); + + // now set the new value in there + setORMMapDelegate((Map<K, V>) newDelegate); + setLoaded(false); + } + + /** Returns the ormMapDelegate */ + public Map<K, V> getORMMapDelegate() { + return ormMapDelegate; + } + + /** + * This method sets the ormMapDelegate + */ + protected void setORMMapDelegate(Map<K, V> ormMapDelegate) { + this.ormMapDelegate = ormMapDelegate; + } + + /** + * @return the mapValueIsEAttribute + */ + public boolean isMapValueIsEAttribute() { + return mapValueIsEAttribute; + } + + /** + * Updates orm map + * + * @see org.eclipse.emf.common.util.BasicEMap#didAdd(org.eclipse.emf.common.util.BasicEMap.Entry) + */ + @Override + protected void didAdd(org.eclipse.emf.common.util.BasicEMap.Entry<K, V> entry) { + if (getORMMapDelegate() != null) { + getORMMapDelegate().put(entry.getKey(), entry.getValue()); + } + super.didAdd(entry); + } + + /** + * Sets the new value using the key in the orm map. + * + * @see org.eclipse.emf.common.util.BasicEMap#didModify(org.eclipse.emf.common.util.BasicEMap.Entry, + * java.lang.Object) + */ + @Override + protected void didModify(org.eclipse.emf.common.util.BasicEMap.Entry<K, V> entry, V oldValue) { + if (getORMMapDelegate() != null) { + getORMMapDelegate().put(entry.getKey(), entry.getValue()); + } + super.didModify(entry, oldValue); + } + + /** + * Removes the entry from the orm map + * + * @see org.eclipse.emf.common.util.BasicEMap#didRemove(org.eclipse.emf.common.util.BasicEMap.Entry) + */ + @Override + protected void didRemove(org.eclipse.emf.common.util.BasicEMap.Entry<K, V> entry) { + if (getORMMapDelegate() != null) { + getORMMapDelegate().remove(entry.getKey()); + } + super.didRemove(entry); + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableDelegateList.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableDelegateList.java new file mode 100755 index 000000000..a8bd4be8c --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableDelegateList.java @@ -0,0 +1,40 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: PersistableDelegateList.java,v 1.9 2010/03/25 00:12:45 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.elist; + +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * A tag which signals that a list is either a persistable map, featuremap or elist. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.9 $ + */ + +public interface PersistableDelegateList<E> { + /** Return the delegate list/map without doing a load */ + public Object getDelegate(); + + /** Returns true if the elist is loaded */ + public boolean isLoaded(); + + /** If the delegate has been initialized */ + public boolean isInitialized(); + + public EStructuralFeature getEStructuralFeature(); +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableEList.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableEList.java new file mode 100755 index 000000000..717e070b4 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableEList.java @@ -0,0 +1,621 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: PersistableEList.java,v 1.26 2010/02/06 20:51:42 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.elist; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.notify.NotifyingList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.util.DelegatingEcoreEList; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * A persistable elist which can be used by different or mappers. This persistable elist works around the idea that the + * persisted list (e.g. PersistentList in Hibernate) is the delegate for this elist. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.26 $ + */ + +public abstract class PersistableEList<E> extends DelegatingEcoreEList<E> implements PersistableDelegateList<E> { + private static final long serialVersionUID = 1L; + + /** The logger */ + private static Log log = LogFactory.getLog(PersistableEList.class); + + /** + * The actual list, must never be an elist as notifications etc. are done by this list + */ + protected List<E> delegate; + + /** The structural feature modeled by this list */ + private EStructuralFeature estructuralFeature; + + /** The unique path to the efeature, used to support serializaion */ + private String eFeaturePath = ""; + + /** Is loaded from backend */ + private boolean isLoaded = false; + + /** Is being loaded from backend */ + private boolean isLoading = false; + + /** The string used for logging */ + protected final String logString; + + protected Boolean isThisListWrapped; + + /** Constructor */ + public PersistableEList(InternalEObject owner, EStructuralFeature feature, List<E> list) { + super(owner); + estructuralFeature = feature; + if (list == null) { + delegate = new ArrayList<E>(); + isLoaded = true; + } else if (list instanceof EList<?>) { + delegate = new ArrayList<E>(list); + isLoaded = true; + } else if (list instanceof ArrayList<?>) { // already loaded lists are + // packaged in an elist + delegate = list; + isLoaded = list.size() > 0; + } else { + delegate = list; + } + + logString = "EList of type: " + this.getClass().getName() + " of member " + estructuralFeature.getName() + + " owned by " + owner.getClass().getName() + " with delegate list " + delegate.getClass().getName(); + + log.debug("Created persistable list " + logString); + } + + /** Takes care of serializing the efeature */ + private void writeObject(java.io.ObjectOutputStream out) throws IOException { + EStructuralFeature estructuralFeatureOld = estructuralFeature; + eFeaturePath = StoreUtil.structuralFeatureToString(estructuralFeature); + // Commented to fix the bug due to which the attribute structuralFeature + // was getting chnaged to null + estructuralFeature = null; + additionalWriteObject(); + out.defaultWriteObject(); + estructuralFeature = estructuralFeatureOld; + } + + /** Do your subclass thing for serialization */ + protected void additionalWriteObject() { + } + + /** Takes care of deserializing the efeature */ + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + estructuralFeature = StoreUtil.stringToStructureFeature(eFeaturePath); + } + + /* + * Get the underlying efeature + * + * @see org.eclipse.emf.ecore.util.DelegatingEcoreEList#getEStructuralFeature() + */ + @Override + public EStructuralFeature getEStructuralFeature() { + return estructuralFeature; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.ecore.util.DelegatingEcoreEList#getFeature() + */ + @Override + public Object getFeature() { + return estructuralFeature; + } + + /** Return the isunique value of the efeature */ + @Override + public boolean isUnique() { + return estructuralFeature.isUnique(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.ecore.util.DelegatingEcoreEList#getFeatureID() + */ + @Override + // todo: get rid of the override when the issue with the + // delegatingecorelist.getFeatureId is + // solved. + public int getFeatureID() { + return owner.eClass().getFeatureID(estructuralFeature); + } + + /** Return the delegate list without doing a load */ + public List<E> getDelegate() { + return delegate; + } + + /** Returns the underlying elist */ + @Override + protected List<E> delegateList() { + load(); + + return delegate; + } + + /** + * If this instance is again wrapped by a NotifyingList then assume that the wrapper will be smart enough to do all + * the inverse things.... Note that the check if a list is wrapped is done once and then the result is cached. So + * this assumes that a list will not be re-wrapped. + * + * @return false if the list is wrapped, otherwise the super hasInverse is called. + */ + @Override + protected boolean hasInverse() { + if (isWrapped()) { + return false; + } + + return super.hasInverse(); + } + + @Override + protected void dispatchNotification(Notification notification) { + // don't notify anyone as the wrapper should do that + if (isWrapped()) { + return; + } + super.dispatchNotification(notification); + } + + private boolean isWrapped() { + if (isThisListWrapped == null) { + final Object value = getEObject().eGet(getEStructuralFeature()); + isThisListWrapped = value != this && value instanceof NotifyingList<?>; + } + return isThisListWrapped; + } + + /** Replace the delegating list and set isLoaded = false */ + public void replaceDelegate(List<E> newDelegate) { + // disabled this assertion because in case of a session refresh it is + // possible + // that the list is replaced by a persistent list + // AssertUtil.assertTrue("This elist " + logString + + // " already wraps an or specific list", + // !isPersistencyWrapped()); + delegate = newDelegate; + isLoaded = false; + } + + /** Returns a string which can be used to log for this elist */ + public String getLogString() { + return logString; + } + + /** + * Performs the load action if not yet oaded and sends out the load notification. + */ + protected void load() { + if (isLoaded) { + return; + } + + // When we are loading we should not be reloaded! + // this can happen in the jpox elist impl. when detaching + if (isLoading) { + return; + } + + isLoading = true; + log.debug("Loading " + getLogString()); + + // prevent notifications to be sent out + boolean eDeliver = owner.eDeliver(); + boolean setDeliver = false; + try { + // only set to false if it was true + if (eDeliver) { + log.debug("Owner " + owner.getClass() + " set eDeliver to false"); + owner.eSetDeliver(false); + setDeliver = true; + } + } catch (UnsupportedOperationException e) { + // in this case the eSetDeliver was not overridden from the + // baseclass + // ignore + } + try { + doLoad(); + } finally { + isLoaded = true; + isLoading = false; + if (setDeliver) { + owner.eSetDeliver(eDeliver); + } + } + // StoreUtil.dispatchEListLoadNotification(owner, this, + // getEStructuralFeature()); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.ecore.util.EcoreEList#isNotificationRequired() + */ + @Override + protected boolean isNotificationRequired() { + if (!isLoaded() || isLoading()) { + return false; // not yet loaded so no notifications, prevents + // infinite looping + } + return super.isNotificationRequired(); + } + + /** Is loaded */ + public boolean isLoaded() { + return isLoaded; + } + + /** Is loaded */ + public void setIsLoaded(boolean isLoaded) { + this.isLoaded = isLoaded; + } + + /** Is loading */ + public void setIsLoading(boolean isLoading) { + this.isLoading = isLoading; + } + + /** Returns true if the load action is running and false otherwise */ + public boolean isLoading() { + return isLoading; + } + + /** + * The load method which should be overridden by the subclass to add lazyloading + */ + protected abstract void doLoad(); + + /** Returns true if the wrapped list is a persistency layer specific list */ + public abstract boolean isPersistencyWrapped(); + + // ---------------------------- Overloaded delegate methods + // -------------------------- + // These methods have been overridden to a load action before the backing + // list is + // accessed. + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateAdd(int, java.lang.Object) + */ + @Override + protected void delegateAdd(int index, E object) { + load(); + super.delegateAdd(index, object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateAdd(java.lang.Object) + */ + @Override + protected void delegateAdd(E object) { + load(); + super.delegateAdd(object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateBasicList() + */ + @Override + protected List<E> delegateBasicList() { + load(); + return super.delegateBasicList(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateClear() + */ + @Override + protected void delegateClear() { + load(); + super.delegateClear(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateContains(java.lang .Object) + */ + @Override + protected boolean delegateContains(Object object) { + load(); + return super.delegateContains(object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateContainsAll(java. util.Collection) + */ + @Override + protected boolean delegateContainsAll(Collection<?> collection) { + load(); + return super.delegateContainsAll(collection); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateEquals(java.lang. Object) + */ + @Override + protected boolean delegateEquals(Object object) { + load(); + return super.delegateEquals(object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateGet(int) + */ + @Override + protected E delegateGet(int index) { + load(); + return super.delegateGet(index); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateHashCode() + */ + @Override + protected int delegateHashCode() { + load(); + return super.delegateHashCode(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateIndexOf(java.lang .Object) + */ + @Override + protected int delegateIndexOf(Object object) { + load(); + return super.delegateIndexOf(object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateIsEmpty() + */ + @Override + protected boolean delegateIsEmpty() { + load(); + return super.delegateIsEmpty(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateIterator() + */ + @Override + protected Iterator<E> delegateIterator() { + load(); + return super.delegateIterator(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateLastIndexOf(java. lang.Object) + */ + @Override + protected int delegateLastIndexOf(Object object) { + load(); + return super.delegateLastIndexOf(object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateListIterator() + */ + @Override + protected ListIterator<E> delegateListIterator() { + return super.delegateListIterator(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateRemove(int) + */ + @Override + protected E delegateRemove(int index) { + load(); + return super.delegateRemove(index); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateSet(int, java.lang.Object) + */ + @Override + protected E delegateSet(int index, E object) { + load(); + return super.delegateSet(index, object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateSize() + */ + @Override + protected int delegateSize() { + load(); + return super.delegateSize(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateToArray() + */ + @Override + protected Object[] delegateToArray() { + load(); + return super.delegateToArray(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateToArray(java.lang .Object[]) + */ + @Override + protected <T> T[] delegateToArray(T[] array) { + load(); + return super.delegateToArray(array); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateToString() + */ + @Override + protected String delegateToString() { + load(); + return super.delegateToString(); + } + + /** If not loaded then basicIterator will always return a false for hasNext */ + @SuppressWarnings("deprecation") + @Override + public Iterator<E> basicIterator() { + if (!isLoaded()) { + return new NonResolvingEIterator<E>() { + /** Always returns false */ + @Override + public boolean hasNext() { + return false; + } + }; + } + + return super.basicIterator(); + } + + /** + * If not loaded then basicIterator will always return a false for hasNext/hasPrevious + */ + @Override + @SuppressWarnings("deprecation") + public ListIterator<E> basicListIterator() { + if (!isLoaded()) { + return new NonResolvingEListIterator<E>() { + /** Always returns false */ + @Override + public boolean hasNext() { + return false; + } + + /** Always returns false */ + @Override + public boolean hasPrevious() { + return false; + } + }; + } + + return super.basicListIterator(); + } + + /** + * If not loaded then basicIterator will always return a false for hasNext/hasPrevious + */ + @Override + @SuppressWarnings("deprecation") + public ListIterator<E> basicListIterator(int index) { + if (!isLoaded()) { + // note no size check on index as this would load this thing + return new NonResolvingEListIterator<E>() { + /** Always returns false */ + @Override + public boolean hasNext() { + return false; + } + + /** Always returns false */ + @Override + public boolean hasPrevious() { + return false; + } + }; + } + + return super.basicListIterator(index); + } + + /** + * Is overridden because it can't use delegates for equality because the delegate (a hibernate or jpox list) will + * try to be equal with this persistable elist. + * + * This method does jvm instance equality because doing a full-fledge equal would result in a load of the list. + */ + @Override + public boolean equals(Object object) { + return this == object; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#clone() + */ + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableEMap.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableEMap.java new file mode 100755 index 000000000..f2bdfe5fa --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableEMap.java @@ -0,0 +1,617 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Douglas Bitting + * Martin Taal + * + * </copyright> + * + * $Id: PersistableEMap.java,v 1.17 2010/04/03 12:55:21 mtaal Exp $ + */ +package org.eclipse.emf.teneo.mapping.elist; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.common.notify.NotificationChain; +import org.eclipse.emf.common.util.BasicEMap; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.common.util.EMap; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.util.EcoreEMap; + +/** + * A persistable emap which uses the PersistableEList as its delegate. Note that this implementation is based on the + * implementation of the superclass. The superclass makes use of a delegate list to store its content. This + * implementation puts a persistent list in this member. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @author <a href="mailto:jdboudreault@gmail.com">Jean-Denis Boudreault</a> + * + * @version $Revision: 1.17 $ + */ +public abstract class PersistableEMap<K, V> extends EcoreEMap<K, V> implements + PersistableDelegateList<BasicEMap.Entry<K, V>> { + + private static final long serialVersionUID = 1L; + + /** The logger */ + private static Log log = LogFactory.getLog(PersistableEMap.class); + + /** The logstring */ + protected String logString; + + /** Used for assertion */ + private final int featureID; + + /** Is loaded from backend */ + private boolean isLoaded = false; + + /** Is being loaded from backend */ + private boolean isLoading = false; + + /** + * The owner of the objet. we must keep a copy since emap does not have one and the delegate EList does not expose + * this field publicly + */ + private InternalEObject owner; + + /** Not supported constructor */ + public PersistableEMap(EClass entryEClass, EList<BasicEMap.Entry<K, V>> delegateEList) { + super(entryEClass, BasicEMap.Entry.class, delegateEList); + throw new UnsupportedOperationException("Explicitly passing delegate list is not supported!"); + } + + /** Constructor */ + public PersistableEMap(EClass entryEClass, Class<?> entryClass, InternalEObject owner, EStructuralFeature feature) { + // invoke constructor with no lazyLoadMapDelegate + super(entryEClass, BasicEMap.Entry.class, owner, owner.eClass().getFeatureID(feature)); + + setDelegateEList(owner, feature, new ArrayList<Entry<K, V>>()); + + this.owner = owner; + this.featureID = owner.eClass().getFeatureID(feature); + log.debug("Created persistable emap for entry eclass " + entryEClass.getName()); + } + + /** + * This version will create the lsit completely, there is no lazy lading from this constructor + * + * @param entryEClass + * @param entryClass + * @param owner + * @param featureID + */ + public PersistableEMap(EClass entryEClass, InternalEObject owner, EStructuralFeature feature, + List<BasicEMap.Entry<K, V>> list) { + super(entryEClass, BasicEMap.Entry.class, owner, owner.eClass().getFeatureID(feature)); + + this.owner = owner; + this.featureID = owner.eClass().getFeatureID(feature); + + // create our list + setDelegateEList(owner, feature, list); + + // sets the size of this map + // size(); + + log.debug("Created persistable emap for entry eclass " + entryEClass.getName()); + } + + /** Sets the delegatelist to a persistablelist */ + protected void setDelegateEList(InternalEObject owner, EStructuralFeature feature, + List<BasicEMap.Entry<K, V>> delegateORMList) { + assert (owner.eClass().getFeatureID(feature) == featureID); + + // NOTE BEWARE: the delegateEList is a member of the superclass! + delegateEList = createDelegateEList(owner, feature, delegateORMList); + + logString = "EMap with entry eclass: " + entryEClass.getName() + " of member " + feature.getName() + + " owned by " + owner.getClass().getName() + " with delegate list " + + delegateORMList.getClass().getName(); + + log.debug("Created/reset elist " + logString); + + if (delegateORMList instanceof EList<?>) { + setLoaded(true); + } else if (delegateORMList instanceof ArrayList<?>) { // already loaded + // lists are + // packaged in + // an elist + setLoaded(delegateORMList.size() > 0); + } + if (isLoaded) { + // force the map to be computed, this sets the internal entrydata/size member + get(null); + } + } + + /** Needs to be implemented by concrete subclass */ + protected abstract EList<BasicEMap.Entry<K, V>> createDelegateEList(InternalEObject owner, + EStructuralFeature feature, List<BasicEMap.Entry<K, V>> delegateORMList); + + /** Replace the delegate */ + @SuppressWarnings("unchecked") + public void replaceDelegate(Object newDelegate) { + setDelegateEList(owner, getEStructuralFeature(), (List<BasicEMap.Entry<K, V>>) newDelegate); + setLoaded(false); + } + + /** + * Performs the load action if not yet oaded and sends out the load notification. + */ + protected void load() { + if (isLoaded) { + // reset the size + size = delegateEList.size(); + return; + } + + // When we are loading we should not be reloaded! + // this can happen in the jpox elist impl. when detaching + if (isLoading) { + return; + } + + isLoading = true; + log.debug("Loading " + getLogString()); + + // set the size + size = this.size(); + + // prevent notifications to be sent out + boolean eDeliver = this.getOwner().eDeliver(); + boolean setDeliver = false; + try { + // only set to false if it was true + if (eDeliver) { + log.debug("Owner " + getOwner().getClass() + " set eDeliver to false"); + getOwner().eSetDeliver(false); + setDeliver = true; + } + } catch (UnsupportedOperationException e) { + // in this case the eSetDeliver was not overridden from the + // baseclass + // ignore + } + try { + doLoad(); + + // force the map to be computed, this sets the internal entrydata/size member + get(null); + + // set the size + size = this.size(); + } finally { + isLoaded = true; + isLoading = false; + if (setDeliver) { + owner.eSetDeliver(eDeliver); + } + } + } + + /** + * The load method which should be overridden by the subclass to add lazyloading + */ + protected abstract void doLoad(); + + // + // /** + // * Overridden to prevent the super + // */ + // @Override + // public void initializeDelegateEList() { + // this.isLoaded = false; + // this.size = 0; + // this.delegateEList = null; + // } + + /** Return ourselves, this class assumes that the emap is mapped as a list */ + public Object getDelegate() { + return this; + } + + /** Returns true if the elist is loaded */ + public boolean isLoaded() { + return isLoaded; + } + + /** + * Overridden for access to size member + */ + @Override + protected void ensureEntryDataExists() { + load(); + super.ensureEntryDataExists(); + } + + /** + * Overridden because of access to size attribute + */ + @Override + public int size() { + // the subclass can override the size to perform smart size + // determination + if (!this.isLoaded()) { + load(); + } + size = delegateEList.size(); + return super.size(); + } + + /** + * Overridden because of access to size attribute + */ + @Override + public boolean isEmpty() { + size(); + if (!this.isLoaded()) { + return (this.size == 0); + } + + return super.isEmpty(); + } + + /* + * Javadoc copied from interface. Overridden because of access to size attribute + */ + @Override + public boolean containsKey(Object key) { + load(); + return super.containsKey(key); + } + + /* + * Javadoc copied from interface. Overridden because of access to size attribute + */ + @Override + public Set<K> keySet() { + load(); + return super.keySet(); + } + + /* + * Javadoc copied from interface. Overridden because of access to size attribute + */ + @Override + public Collection<V> values() { + load(); + return super.values(); + } + + /* + * Javadoc copied from interface. Overridden because of access to size attribute + */ + @Override + public Set<Map.Entry<K, V>> entrySet() { + load(); + return super.entrySet(); + } + + /* + * Javadoc copied from interface. Overridden because of access to size attribute + */ + @Override + public boolean containsValue(Object value) { + load(); + return super.containsValue(value); + } + + /* + * Javadoc copied from interface. Overridden because of access to size attribute + */ + @Override + public V get(Object key) { + load(); + return super.get(key); + } + + @Override + public Map<K, V> map() { + load(); + if (view == null) { + view = new View<K, V>(); + } + if (view.map == null) { + view.map = new PersistableDelegatingMap(); + } + + return view.map; + } + + /** Used to tag the returned map class and give access to the owner */ + public class PersistableDelegatingMap extends DelegatingMap { + + /** Return my owner */ + public PersistableEMap<K, V> getOwner() { + return PersistableEMap.this; + } + + } + + /** Set the delegate again */ + + protected boolean isLoading() { + return isLoading; + } + + protected void setLoading(boolean isLoading) { + this.isLoading = isLoading; + } + + protected void setLoaded(boolean isLoaded) { + this.isLoaded = isLoaded; + log.debug("Isloaded is " + isLoaded); + } + + protected String getLogString() { + return logString; + } + + protected InternalEObject getOwner() { + return owner; + } + + @Override + public NotificationChain basicAdd(java.util.Map.Entry<K, V> object, NotificationChain notifications) { + load(); + return super.basicAdd(object, notifications); + } + + @Override + public org.eclipse.emf.common.util.BasicEMap.Entry<K, V> basicGet(int index) { + load(); + return super.basicGet(index); + } + + @Override + public Iterator<java.util.Map.Entry<K, V>> basicIterator() { + load(); + return super.basicIterator(); + } + + @Override + public List<java.util.Map.Entry<K, V>> basicList() { + load(); + return super.basicList(); + } + + @Override + public ListIterator<java.util.Map.Entry<K, V>> basicListIterator() { + load(); + return super.basicListIterator(); + } + + @Override + public ListIterator<java.util.Map.Entry<K, V>> basicListIterator(int index) { + load(); + return super.basicListIterator(index); + } + + @Override + public NotificationChain basicRemove(Object object, NotificationChain notifications) { + load(); + return super.basicRemove(object, notifications); + } + + @Override + public void addUnique(java.util.Map.Entry<K, V> object) { + load(); + super.addUnique(object); + } + + @Override + public void addUnique(int index, java.util.Map.Entry<K, V> object) { + load(); + super.addUnique(index, object); + } + + @Override + public java.util.Map.Entry<K, V> setUnique(int index, java.util.Map.Entry<K, V> object) { + load(); + return super.setUnique(index, object); + } + + @Override + public boolean add(java.util.Map.Entry<K, V> object) { + load(); + return super.add(object); + } + + @Override + public void add(int index, java.util.Map.Entry<K, V> object) { + load(); + super.add(index, object); + } + + @Override + public boolean addAll(Collection<? extends java.util.Map.Entry<K, V>> collection) { + load(); + return super.addAll(collection); + } + + @Override + public boolean addAll(int index, Collection<? extends java.util.Map.Entry<K, V>> collection) { + load(); + return super.addAll(index, collection); + } + + @Override + public void clear() { + this.isLoaded = false; + super.clear(); + } + + @Override + public org.eclipse.emf.common.util.BasicEMap.Entry<K, V> get(int index) { + load(); + return super.get(index); + } + + @Override + protected int indexOf(int hash) { + load(); + return super.indexOf(hash); + } + + @Override + public int indexOf(Object object) { + load(); + return super.indexOf(object); + } + + @Override + public int indexOfKey(Object key) { + load(); + return super.indexOfKey(key); + } + + @Override + public Iterator<java.util.Map.Entry<K, V>> iterator() { + load(); + return super.iterator(); + } + + @Override + public int lastIndexOf(Object object) { + load(); + return super.lastIndexOf(object); + } + + @Override + public ListIterator<java.util.Map.Entry<K, V>> listIterator() { + load(); + return super.listIterator(); + } + + @Override + public ListIterator<java.util.Map.Entry<K, V>> listIterator(int index) { + load(); + return super.listIterator(index); + } + + @Override + public void move(int index, java.util.Map.Entry<K, V> object) { + load(); + super.move(index, object); + } + + @Override + public java.util.Map.Entry<K, V> move(int targetIndex, int sourceIndex) { + load(); + return super.move(targetIndex, sourceIndex); + } + + @Override + public V put(K key, V value) { + load(); + return super.put(key, value); + } + + @Override + public void putAll(EMap<? extends K, ? extends V> map) { + load(); + super.putAll(map); + } + + @Override + public void putAll(Map<? extends K, ? extends V> map) { + load(); + super.putAll(map); + } + + @Override + protected V putEntry(org.eclipse.emf.common.util.BasicEMap.Entry<K, V> entry, V value) { + return super.putEntry(entry, value); + } + + @Override + public java.util.Map.Entry<K, V> remove(int index) { + load(); + return super.remove(index); + } + + @Override + public boolean remove(Object object) { + + load(); + return super.remove(object); + } + + @Override + public boolean removeAll(Collection<?> collection) { + load(); + return super.removeAll(collection); + } + + @Override + protected V removeEntry(int index, int entryIndex) { + load(); + return super.removeEntry(index, entryIndex); + } + + @Override + public V removeKey(Object key) { + load(); + return super.removeKey(key); + } + + @Override + protected V resolve(K key, V value) { + load(); + return super.resolve(key, value); + } + + @Override + public boolean retainAll(Collection<?> collection) { + load(); + return super.retainAll(collection); + } + + @Override + public java.util.Map.Entry<K, V> set(int index, java.util.Map.Entry<K, V> object) { + load(); + return super.set(index, object); + } + + @Override + public List<java.util.Map.Entry<K, V>> subList(int start, int end) { + load(); + return super.subList(start, end); + } + + @Override + public Object[] toArray() { + load(); + return super.toArray(); + } + + @Override + public <T> T[] toArray(T[] array) { + load(); + return super.toArray(array); + } + + @Override + public void set(Object value) { + load(); + super.set(value); + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableFeatureMap.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableFeatureMap.java new file mode 100755 index 000000000..04e9ba693 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/elist/PersistableFeatureMap.java @@ -0,0 +1,539 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: PersistableFeatureMap.java,v 1.15 2010/02/04 11:03:00 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.elist; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.util.DelegatingFeatureMap; +import org.eclipse.emf.ecore.util.FeatureMap; +import org.eclipse.emf.teneo.TeneoException; +import org.eclipse.emf.teneo.type.FeatureMapEntry; +import org.eclipse.emf.teneo.util.AssertUtil; + +/** + * A persistable elist which can be used by different or mappers. This persistable elist works around the idea that the + * persisted list (e.g. PersistentList in Hibernate) is the delegate for this elist. + * + * Note the delegate**() methods are overridden to force a load before anything else happens with the delegated list. + * The addUnique. addSet methods are overridden to ensure that the featuremap entries of the right type are passed to + * the persistent store. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.15 $ + */ + +public abstract class PersistableFeatureMap extends DelegatingFeatureMap implements + PersistableDelegateList<FeatureMap.Entry> { + + private static final long serialVersionUID = 1L; + + /** The logger */ + private static Log log = LogFactory.getLog(PersistableFeatureMap.class); + + /** + * The actual list, must never be an elist as notifications etc. are done by this list + */ + protected List<FeatureMap.Entry> delegate; + + /** Is loaded from backend */ + private boolean isLoaded = false; + + /** Is being loaded from backend */ + private boolean isLoading = false; + + /** The string used for logging */ + protected final String logString; + + /** The type of the elements in this list */ + private final Class<? extends FeatureMap.Entry> elementType; + + /** Constructor */ + public PersistableFeatureMap(InternalEObject owner, EStructuralFeature feature, List<FeatureMap.Entry> list) { + super(owner, feature); + elementType = determineElementType(); + + if (list == null) { + delegate = new ArrayList<FeatureMap.Entry>(); + isLoaded = true; + } else if (list instanceof EList<?>) { + AssertUtil.assertTrue("The passed elist is not a featuremap but a : " + list.getClass().getName() + + ". Error in featureMap: " + getLogString(), list instanceof FeatureMap); + + delegate = replaceEntryAll(list); + isLoaded = true; + } else { + delegate = list; + isLoaded = list.size() > 0; + } + + logString = "FeatureMap of member " + getEStructuralFeature().getName() + " owned by " + + owner.getClass().getName() + " with delegate list " + delegate.getClass().getName(); + + log.debug("Created persistable featuremap " + logString); + } + + /** Returns the element type to be used */ + protected abstract Class<? extends FeatureMap.Entry> determineElementType(); + + /** Returns the element type */ + public Class<? extends FeatureMap.Entry> getElementType() { + return elementType; + } + + /** Shortcut to replace entries */ + protected FeatureMap.Entry replaceEntry(FeatureMap.Entry entry) { + if (entry instanceof FeatureMapEntry && ((FeatureMapEntry) entry).belongsToFeatureMap(this)) { + return entry; + } + + final FeatureMap.Entry emfEntry = entry; + return createEntry(emfEntry.getEStructuralFeature(), emfEntry.getValue()); + } + + /** Convenience to replace all */ + private List<FeatureMap.Entry> replaceEntryAll(Collection<? extends FeatureMap.Entry> coll) { + final ArrayList<FeatureMap.Entry> result = new ArrayList<FeatureMap.Entry>(); + for (FeatureMap.Entry fe : coll) { + result.add(replaceEntry(fe)); + } + return result; + } + + /** Creates an exception with the logID added, without a cause */ + protected TeneoException createException(String msg) { + return new TeneoException(msg + "\n" + getLogString()); + } + + /** Creates an exception with the logID added, without a cause */ + protected TeneoException createException(String msg, Throwable t) { + return new TeneoException(msg + "\n" + getLogString(), t); + } + + /** Return the delegate list without doing a load */ + public List<FeatureMap.Entry> getDelegate() { + return delegate; + } + + /** Returns the underlying elist */ + @Override + protected List<FeatureMap.Entry> delegateList() { + load(); + + return delegate; + } + + /** Replace the delegating list */ + public void replaceDelegate(List<FeatureMap.Entry> newDelegate) { + AssertUtil.assertTrue("This featuremap " + logString + " already wraps an or specific featuremap", + !isPersistencyWrapped()); + + delegate = newDelegate; + isLoaded = false; + } + + /** Returns a string which can be used to log for this elist */ + public String getLogString() { + return logString; + } + + /** + * Performs the load action if not yet loaded and sends out the load notification + */ + protected void load() { + if (isLoaded) { + return; + } + + // When we are loading we should not be reloaded! + // this can happen in the jpox fm impl. when detaching + if (isLoading) { + return; + } + + isLoading = true; + doLoad(); + isLoaded = true; + isLoading = false; + // StoreUtil.dispatchEListLoadNotification(owner, this, + // getEStructuralFeature()); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.ecore.util.EcoreEList#isNotificationRequired() + */ + @Override + protected boolean isNotificationRequired() { + if (!isLoaded() || isLoading()) { + return false; // not yet loaded so no notifications, prevents + // infinite looping + } + return super.isNotificationRequired(); + } + + /** Is loaded */ + public boolean isLoaded() { + return isLoaded; + } + + /** Is loaded */ + public void setIsLoaded(boolean isLoaded) { + this.isLoaded = isLoaded; + } + + /** Is loading */ + public void setIsLoading(boolean isLoading) { + this.isLoading = isLoading; + } + + /** Returns true if the load action is running and false otherwise */ + public boolean isLoading() { + return isLoading; + } + + /** + * The load method which should be overridden by the subclass to add lazyloading + */ + protected abstract void doLoad(); + + /** Returns true if the wrapped list is a persistency layer specific list */ + public abstract boolean isPersistencyWrapped(); + + /** Override the didadd to enable opposite setting */ + // MT not necessary anymore in new EMF versions + // @Override + // protected void didAdd(int index, FeatureMap.Entry obj) { + // final NotificationChain nc = inverseAdd(obj, null); + // if (nc != null && isNotificationRequired()) { + // nc.dispatch(); + // } + // super.didAdd(index, obj); + // } + /* Override the didremove to enable opposite setting */ + // @Override + // protected void didRemove(int index, FeatureMap.Entry obj) { + // final NotificationChain nc = inverseRemove(obj, null); + // if (nc != null && isNotificationRequired()) { + // nc.dispatch(); + // } + // super.didRemove(index, obj); + // } + // ---------------------------- Overloaded delegate methods + // -------------------------- + // These methods have been overridden to a load action before the backing + // list is + // accessed. + /** OVerridden to create the correct featuremap entry */ + @Override + protected abstract FeatureMap.Entry createEntry(EStructuralFeature eStructuralFeature, Object value); + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateAdd(int, java.lang.Object) + */ + @Override + protected void delegateAdd(int index, FeatureMap.Entry object) { + load(); + super.delegateAdd(index, object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateAdd(java.lang.Object) + */ + @Override + protected void delegateAdd(FeatureMap.Entry object) { + load(); + super.delegateAdd(object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.notify.impl.DelegatingNotifyingListImpl#addAllUnique (java.util.Collection ) + */ + @Override + public boolean addAllUnique(Collection<? extends FeatureMap.Entry> collection) { + return super.addAllUnique(replaceEntryAll(collection)); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.notify.impl.DelegatingNotifyingListImpl#addAllUnique (int, java.util.Collection) + */ + @Override + public boolean addAllUnique(int index, Collection<? extends FeatureMap.Entry> collection) { + return super.addAllUnique(index, replaceEntryAll(collection)); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.notify.impl.DelegatingNotifyingListImpl#addUnique (int, java.lang.Object) + */ + @Override + public void addUnique(int index, FeatureMap.Entry object) { + super.addUnique(index, replaceEntry(object)); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.notify.impl.DelegatingNotifyingListImpl#addUnique (java.lang.Object) + */ + @Override + public void addUnique(FeatureMap.Entry object) { + super.addUnique(replaceEntry(object)); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.notify.impl.DelegatingNotifyingListImpl#setUnique (int, java.lang.Object) + */ + @Override + public FeatureMap.Entry setUnique(int index, FeatureMap.Entry object) { + return super.setUnique(index, replaceEntry(object)); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateBasicList() + */ + @Override + protected List<FeatureMap.Entry> delegateBasicList() { + load(); + return super.delegateBasicList(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateClear() + */ + @Override + protected void delegateClear() { + load(); + super.delegateClear(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateContains(java.lang .Object) + */ + @Override + protected boolean delegateContains(Object object) { + load(); + return super.delegateContains(object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateContainsAll(java. util.Collection) + */ + @Override + protected boolean delegateContainsAll(Collection<?> collection) { + load(); + return super.delegateContainsAll(collection); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateEquals(java.lang. Object) + */ + @Override + protected boolean delegateEquals(Object object) { + load(); + return super.delegateEquals(object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateGet(int) + */ + @Override + protected FeatureMap.Entry delegateGet(int index) { + load(); + return super.delegateGet(index); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateHashCode() + */ + @Override + protected int delegateHashCode() { + load(); + return super.delegateHashCode(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateIndexOf(java.lang .Object) + */ + @Override + protected int delegateIndexOf(Object object) { + load(); + return super.delegateIndexOf(object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateIsEmpty() + */ + @Override + protected boolean delegateIsEmpty() { + load(); + return super.delegateIsEmpty(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateIterator() + */ + @Override + protected Iterator<FeatureMap.Entry> delegateIterator() { + load(); + return super.delegateIterator(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateLastIndexOf(java. lang.Object) + */ + @Override + protected int delegateLastIndexOf(Object object) { + load(); + return super.delegateLastIndexOf(object); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateListIterator() + */ + @Override + protected ListIterator<FeatureMap.Entry> delegateListIterator() { + // TODO Auto-generated method stub + return super.delegateListIterator(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateRemove(int) + */ + @Override + protected FeatureMap.Entry delegateRemove(int index) { + load(); + return super.delegateRemove(index); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateSet(int, java.lang.Object) + */ + @Override + protected FeatureMap.Entry delegateSet(int index, FeatureMap.Entry object) { + load(); + // do the delegate set in two steps so that cascade delete works + // for featuremap as a component + final FeatureMap.Entry old = delegateRemove(index); + delegateAdd(index, object); + return old; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateSize() + */ + @Override + protected int delegateSize() { + load(); + return super.delegateSize(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateToArray() + */ + @Override + protected Object[] delegateToArray() { + load(); + return super.delegateToArray(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateToArray(java.lang .Object[]) + */ + @Override + protected <T> T[] delegateToArray(T[] array) { + load(); + return super.delegateToArray(array); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.util.DelegatingEList#delegateToString() + */ + @Override + protected String delegateToString() { + load(); + return super.delegateToString(); + } + + /** + * Is overridden because it can't use delegates for equality because the delegate (a hibernate or jpox list) will + * try to be equal with this persistable elist. + * + * This method does jvm instance equality because doing a full-fledge equal would result in a load of the list. + */ + @Override + public boolean equals(Object object) { + return this == object; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/EntityNameStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/EntityNameStrategy.java new file mode 100755 index 000000000..049353296 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/EntityNameStrategy.java @@ -0,0 +1,64 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: EntityNameStrategy.java,v 1.7 2010/02/06 18:25:46 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.strategy; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.extension.ExtensionManagerAware; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Converter from entityname to and from an eclass. The entityname is used in + * the hql, etc. + * + * @author <a href="mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.7 $ + */ +public interface EntityNameStrategy extends ExtensionPoint, + ExtensionManagerAware { + + /** The EObject eclass */ + public static EClass EOBJECT_ECLASS = (EClass) EcorePackage.eINSTANCE + .getEClassifier("EObject"); + + /** The EObject eclass name */ + public static String EOBJECT_ECLASS_NAME = EcorePackage.eINSTANCE.getName() + + "_" + EOBJECT_ECLASS.getName(); + + /** + * Determines the name for a given EClass. This name can be used in jsf + * pages and queries. + * + * Note if the eClass is the EObject eclass then the string + * EOBJECT_ECLASS_NAME must be returned. + */ + public String toEntityName(EClass eClass); + + /** + * Return the EClass for a certain name, searches in the array of epackages + */ + public EClass toEClass(String eClassName); + + /** + * @param paModel + * the paModel to set + */ + public void setPaModel(PAnnotatedModel paModel); + +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/SQLNameStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/SQLNameStrategy.java new file mode 100755 index 000000000..9799ac757 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/SQLNameStrategy.java @@ -0,0 +1,135 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: SQLNameStrategy.java,v 1.11 2011/10/29 06:12:48 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.strategy; + +import java.util.List; + +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.extension.ExtensionPoint; + +/** + * Takes care of creating correct names for sql artifacts such as tables, + * columns, foreign keys, etc. + * + * Note that strategies are normally created once for each instance of + * persistenceoptions. + * + * @author <a href="mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.11 $ + */ +public interface SQLNameStrategy extends ExtensionPoint { + + /** + * Converts the name to the required sql setting (length and case). If the + * name is too long (see option maximum sql name length) then the prefix is + * trunced away. + */ + public abstract String convert(String name); + + /** + * Converts the name to the required sql setting (length and case). If the + * name is too long (see the option maximum sql name length) then: if + * truncPrefix = false then the part after the _ is trunced. if truncPrefix + * = true then the part before the _ is trunced. An underscore often occurs + * in the name of a join table. + */ + public String convert(String name, boolean truncPrefix); + + /** + * The join column name used to join a joined-subclass table with its parent + * table + */ + public abstract String getPrimaryKeyJoinColumnName( + PAnnotatedEClass aSuperClass, String idFeature); + + /** The join colum name for the secondary table */ + public abstract String getSecondaryTablePrimaryKeyJoinColumnName( + PAnnotatedEStructuralFeature iddef); + + /** Returns the table name for a passed AnnotatedEClass */ + public abstract String getTableName(PAnnotatedEClass aClass); + + /** Simple column name with optional prefix */ + public abstract String getColumnName( + PAnnotatedEStructuralFeature aStructuralFeature, String prefix); + + /** + * Return the name of the foreign key used for this aReference. If null is + * returned then the name of the foreign key is not set. Returns the + * concatenation of the entityname of the aclass to which the areference + * belongs. + * + * This method is normally called when the PersistenceOption + * CREATE_READABLE_FOREIGN_KEY_NAMES is true. + */ + public abstract String getForeignKeyName( + PAnnotatedEStructuralFeature aFeature); + + /** Return joincolumn names for many-to-one */ + public abstract List<String> getManyToOneJoinColumnNames( + PAnnotatedEReference aReference); + + /** Return a list of join columns for a many is eAttribute */ + public abstract List<String> getOneToManyEAttributeJoinColumns( + PAnnotatedEAttribute aAttribute); + + /** Return a list of join columns for a many is eReference */ + public abstract List<String> getOneToManyEReferenceJoinColumns( + PAnnotatedEReference aReference); + + /** + * Return a list of join columns for a join table for a many to many + */ + public abstract List<String> getJoinTableJoinColumns( + PAnnotatedEReference aReference, boolean inverse); + + /** Return the name of the join table in case of a list of simpletypes */ + public abstract String getJoinTableName(PAnnotatedEAttribute aAttribute); + + /** Return the name of the join table */ + public abstract String getJoinTableName(PAnnotatedEReference aReference); + + /** + * @see PersistenceOptions#DISCRIMINATOR_COLUMN_NAME + */ + public abstract String getDiscriminatorColumnName(); + + /** + * Return the name of the version column used. + */ + public abstract String getVersionColumnName(); + + /** + * Return the column name for the id column of the idbag join table. + */ + public abstract String getIdBagIDColumn(); + + /** Return the column name for the synthetic ID column */ + public abstract String getSyntheticIDColumnName(); + + /** + * Sets the PersistenceOptions used. This is mainly to support backward + * compatibility with older version in which the naming strategy was + * controlled by options. + */ + public abstract void setPersistenceOptions(PersistenceOptions po); +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/StrategyUtil.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/StrategyUtil.java new file mode 100755 index 000000000..cc26d2dde --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/StrategyUtil.java @@ -0,0 +1,124 @@ +/** + * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights + * reserved. This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal - Initial API and + * implementation </copyright> $Id: StrategyUtil.java,v 1.7 2009/03/30 06:41:00 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.strategy; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * Contains different util methods related to strategies. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.7 $ + */ + +public class StrategyUtil { + + /** Returns the entity name based on a specific logic */ + public static String getEntityName(EntityNameStrategy ens, + PersistenceOptions po, PAnnotatedModel paModel, EClass eclass) { + if (eclass == null) { + throw new IllegalArgumentException( + "Passed eclass is null." + + "This can occur if epackages which refer to eachother are placed in different ecore/xsd files " + + "and they are not read using one resource set. The reference from one epackage to another must be " + + "resolvable by EMF."); + } + + // ok, here we figure out if it is an EMap. if so, we return the + // destination child name, not the keyToValueEntry wrapper + final PAnnotatedEClass aclass = (paModel != null ? paModel + .getPAnnotated(eclass) : null); + if (aclass == null && paModel != null) { // happens when the eclass is + // EObject itself + return ens.toEntityName(eclass); + } + if (po.isMapEMapAsTrueMap() && StoreUtil.isMapEntry(eclass)) { + // ok, it is an EMAp, get the annotaetd class of the child + EStructuralFeature feature = eclass.getEStructuralFeature("value"); + if (feature instanceof EReference) { + return getEntityName(ens, po, paModel, ((EReference) feature) + .getEReferenceType()); + } + return ((EAttribute) feature).getEType().getInstanceClassName(); + } + // + // if (aclass != null && aclass.getEntity() != null && + // aclass.getEntity().getName() != null) { + // return aclass.getEntity().getName(); + // } + return ens.toEntityName(eclass); + } + + /** + * Returns the list of names of id props of the eclass, walks the + * inheritance tree to find the id feature, if none is found then the + */ + public static List<String> getIDFeaturesNames(PAnnotatedEClass aClass, + String optionDefaultIDFeatureName) { + final List<String> list = getIDFeaturesNamesRecurse(aClass); + // See, 172756 + if (list.isEmpty()) { + list.add(optionDefaultIDFeatureName); + } + return list; + } + + /** Internal will walk the inheritance tree to find the id feature */ + private static List<String> getIDFeaturesNamesRecurse( + PAnnotatedEClass aClass) { + final ArrayList<String> list = new ArrayList<String>(); + for (EStructuralFeature feature : aClass.getModelEClass() + .getEStructuralFeatures()) { + final PAnnotatedEStructuralFeature aStructuralFeature = aClass + .getPaModel().getPAnnotated(feature); + if (aStructuralFeature instanceof PAnnotatedEAttribute) { + final PAnnotatedEAttribute aAttribute = (PAnnotatedEAttribute) aStructuralFeature; + final String attrName = aAttribute.getModelEAttribute() + .getName(); + if (aAttribute.getId() != null && !list.contains(attrName)) { + list.add(attrName); + } + } + } + + if (list.isEmpty() + && aClass.getModelEClass().getESuperTypes().size() > 0) { + for (EClass eClass : aClass.getModelEClass().getESuperTypes()) { + final PAnnotatedEClass aSuperClass = aClass.getPaModel() + .getPAnnotated(eClass); + if (aSuperClass != null) { + final List<String> superList = getIDFeaturesNamesRecurse(aSuperClass); + list.removeAll(superList); + list.addAll(superList); + } + if (!list.isEmpty()) { + return list; + } + } + if (!list.isEmpty()) { + return list; + } + // fall through + } + return list; + } + +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/ClassicEntityNameStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/ClassicEntityNameStrategy.java new file mode 100755 index 000000000..c62ea9625 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/ClassicEntityNameStrategy.java @@ -0,0 +1,164 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ClassicEntityNameStrategy.java,v 1.7 2009/07/28 03:39:44 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.strategy.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEPackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.classloader.ClassLoaderResolver; +import org.eclipse.emf.teneo.classloader.StoreClassLoadException; +import org.eclipse.emf.teneo.ecore.EModelResolver; +import org.eclipse.emf.teneo.extension.ExtensionManager; +import org.eclipse.emf.teneo.mapping.strategy.EntityNameStrategy; + +/** + * This implementation assumes that EClass names are unique. It will (de)Resolve using the EClass name. + * + * @author <a href="mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.7 $ + */ +public class ClassicEntityNameStrategy implements EntityNameStrategy { + + /** The logger */ + private static Log log = LogFactory.getLog(ClassicEntityNameStrategy.class); + + /** The singleton instance as it is thread safe */ + public static final ClassicEntityNameStrategy INSTANCE = new ClassicEntityNameStrategy(); + + // The pamodel for which this is done + private PAnnotatedModel paModel; + + private ExtensionManager extensionManager; + + /* + * (non-Javadoc) + * + * @see org.elver.ecore.spring.EClassResolver#deResolve(org.eclipse.emf.ecore .EClass) + */ + public String toEntityName(EClass eClass) { + if (eClass == EOBJECT_ECLASS) { + return EOBJECT_ECLASS_NAME; + } + + if (eClass == null) { + throw new IllegalArgumentException( + "Passed eclass is null." + + "This can occur if epackages which refer to eachother are placed in different ecore/xsd files " + + "and they are not read using one resource set. The reference from one epackage to another must be " + + "resolvable by EMF."); + } + + if (eClass.getName() == null) { + throw new IllegalArgumentException( + "EClass " + + eClass.toString() + + " has a null name." + + "This can occur if epackages which refer to eachother are placed in different ecore/xsd files " + + "and they are not read using one resource set. The reference from one epackage to another must be " + + "resolvable by EMF."); + } + + return eClass.getName(); + } + + /* + * (non-Javadoc) + * + * @see org.elver.ecore.spring.EClassResolver#resolve(java.lang.String) + */ + public EClass toEClass(String eClassName) { + if (eClassName == null) { + throw new IllegalArgumentException("eClassName may not be null"); + } + + if (eClassName.compareTo(EOBJECT_ECLASS_NAME) == 0) { + return EcorePackage.eINSTANCE.getEObject(); + } + + // now try all epackages + EClass eClass = null; + for (final PAnnotatedEPackage aPackage : getPaModel().getPaEPackages()) { + for (final PAnnotatedEClass aClass : aPackage.getPaEClasses()) { + final EClass checkEClass = aClass.getModelEClass(); + if (checkEClass.getName().compareTo(eClassName) == 0) { + if (eClass != null) { + // doubly entry! Actually require different resolver + throw new IllegalArgumentException("There is more than one EClass with the same name (" + + eClassName + " in EPackage " + eClass.getEPackage().getName() + " and " + + aPackage.getModelEPackage().getName() + + ". A different EClassResolver should be used."); + } + } + eClass = checkEClass; + } + } + + // we didn'y find it, perhaps it is fully qualified, lets try by full + // class name + if (eClass == null) { + try { + final Class<?> cls = ClassLoaderResolver.classForName(eClassName); + eClass = EModelResolver.instance().getEClass(cls); + } catch (StoreClassLoadException e) { + log.debug("Failed to retreive ECLass for name: " + eClassName + + ". This is no problem if this is a featuremap."); + } + } + + if (eClass == null) { + throw new IllegalArgumentException("No EClass found using " + eClassName); + } + return eClass; + } + + /** + * @return the paModel + */ + public PAnnotatedModel getPaModel() { + return paModel; + } + + /** + * @param paModel + * the paModel to set + */ + public void setPaModel(PAnnotatedModel paModel) { + this.paModel = paModel; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.extension.ExtensionManagerAware#setExtensionManager + * (org.eclipse.emf.teneo.extension.ExtensionManager) + */ + public void setExtensionManager(ExtensionManager extensionManager) { + this.extensionManager = extensionManager; + } + + /** + * @return the extensionManager + */ + public ExtensionManager getExtensionManager() { + return extensionManager; + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/ClassicSQLNameStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/ClassicSQLNameStrategy.java new file mode 100755 index 000000000..2a412a014 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/ClassicSQLNameStrategy.java @@ -0,0 +1,611 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: ClassicSQLNameStrategy.java,v 1.20 2011/10/29 06:12:49 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.strategy.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.extension.ExtensionManager; +import org.eclipse.emf.teneo.extension.ExtensionManagerAware; +import org.eclipse.emf.teneo.mapping.strategy.EntityNameStrategy; +import org.eclipse.emf.teneo.mapping.strategy.SQLNameStrategy; +import org.eclipse.emf.teneo.mapping.strategy.StrategyUtil; +import org.eclipse.emf.teneo.util.AssertUtil; + +/** + * Implements the sql naming strategy of older versions of Teneo. This + * implementation is driven by the options set in the PersistenceOptions. + * + * @author <a href="mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.20 $ + */ +public class ClassicSQLNameStrategy implements SQLNameStrategy, + ExtensionManagerAware { + + // The logger + protected static final Log log = LogFactory + .getLog(ClassicSQLNameStrategy.class); + + // The local members for several options + protected String optionJoinTableNamingStrategy; + protected String optionJoinColumnNamingStrategy; + protected int optionMaximumSqlLength; + protected EntityNameStrategy entityNameStrategy; + protected PersistenceOptions persistenceOptions; + protected boolean optionSQLUpperCase = false; + protected boolean optionSQLLowerCase = false; + protected String optionTableNamePrefix = ""; + protected String optionColumnNamePrefix = ""; + protected String optionForeignKeyNamePrefix = ""; + protected String optionSQLNameColumnPrefix = ""; + + private ExtensionManager extensionManager; + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy# + * getPrimaryKeyJoinColumnName + * (org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass, + * java.lang.String) + */ + public String getPrimaryKeyJoinColumnName(PAnnotatedEClass aSuperClass, + String idFeature) { + return optionColumnNamePrefix + + convert( + getEntityName(aSuperClass.getPaModel(), + aSuperClass.getModelEClass()) + + "_" + idFeature, true); + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy# + * getSecondaryTablePrimaryKeyJoinColumnName + * (org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature) + */ + public String getSecondaryTablePrimaryKeyJoinColumnName( + PAnnotatedEStructuralFeature iddef) { + return optionColumnNamePrefix + + convert(iddef.getModelEStructuralFeature().getName()); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#getTableName(org + * .eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass) + */ + public String getTableName(PAnnotatedEClass aClass) { + return optionTableNamePrefix + + convert(getEntityName(aClass.getPaModel(), + aClass.getModelEClass())); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#getColumnName( + * org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature) + */ + public String getColumnName( + PAnnotatedEStructuralFeature aStructuralFeature, String prefix) { + if (prefix != null) { + return convert(prefix + "_" + + aStructuralFeature.getModelEStructuralFeature().getName()); + } + return optionColumnNamePrefix + + convert(aStructuralFeature.getModelEStructuralFeature() + .getName()); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#getForeignKeyName + * (org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature) + */ + public String getForeignKeyName(PAnnotatedEStructuralFeature aFeature) { + final PAnnotatedEClass aClass = aFeature.getPaEClass(); + return optionForeignKeyNamePrefix + + convert( + getEntityName(aClass.getPaModel(), + aClass.getModelEClass()) + + "_" + + aFeature.getModelEStructuralFeature() + .getName(), true); + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy# + * getManyToOneJoinColumnNames + * (org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference) + */ + public List<String> getManyToOneJoinColumnNames( + PAnnotatedEReference aReference) { + final EReference eref = aReference.getModelEReference(); + + // isTransient occurs for computed featuremap features, these are + // ignored + // later on + assert (eref.isTransient() || !eref.isMany()); // otherwise this should + // have been a mtm + + // in case of many-to-one to qualify use the name of the class to which + // is refered + // this is just there for backward compatibility, see the case of + // the manytoone and onetoone below. + final PAnnotatedEClass aClass; + if (eref.getEOpposite() == null) { + aClass = aReference.getAReferenceType(); + } else { + // the aclass is just the class of the structuralfeature itself. + // this is the most common case the name of the join column reflects + // the owner of the one-to-many + aClass = aReference.getPaEClass(); + } + final String typeName = getMappingName(aClass); + final String featureName = eref.getName(); + + final List<String> result = new ArrayList<String>(); + final List<String> names = StrategyUtil.getIDFeaturesNames(aClass, + persistenceOptions.getDefaultIDFeatureName()); + final boolean simpleNaming = optionJoinColumnNamingStrategy + .compareTo("simple") == 0; + for (String name : names) { + final String postFix; + if (names.size() == 1 && simpleNaming) { + postFix = ""; + } else { + postFix = "_" + name; + } + + final String jcName; + if (simpleNaming) { + jcName = featureName + postFix; + } else { // backward compatibility + jcName = typeName + "_" + featureName + postFix; + } + result.add(optionColumnNamePrefix + convert(jcName, true)); + } + return result; + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy# + * getOneToManyEAttributeJoinColumns + * (org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute) + */ + public List<String> getOneToManyEAttributeJoinColumns( + PAnnotatedEAttribute aAttribute) { + final PAnnotatedEClass aClass = aAttribute.getPaEClass(); + final String typeName = getMappingName(aClass); + final String featureName = aAttribute.getModelEAttribute().getName(); + + final List<String> result = new ArrayList<String>(); + final List<String> names = StrategyUtil.getIDFeaturesNames(aClass, + persistenceOptions.getDefaultIDFeatureName()); + final boolean simpleNaming = optionJoinColumnNamingStrategy + .compareTo("simple") == 0; + for (String name : names) { + final String postFix; + if (names.size() == 1 && simpleNaming) { + postFix = ""; + } else { + postFix = "_" + name; + } + + final String jcName = typeName + "_" + featureName + postFix; + result.add(optionColumnNamePrefix + convert(jcName, true)); + } + return result; + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy# + * getOneToManyEReferenceJoinColumns + * (org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference) + */ + public List<String> getOneToManyEReferenceJoinColumns( + PAnnotatedEReference aReference) { + final PAnnotatedEClass aClass = aReference.getPaEClass(); + final String typeName; + final String featureName; + // for backwards compatibility if the other side is there then use the + // name + // of the other side for the typeName and featureName. The many-to-one + // determines + // the naming + if (aReference.getModelEReference().getEOpposite() != null) { + typeName = getMappingName(aReference.getAReferenceType()); + featureName = aReference.getModelEReference().getEOpposite() + .getName(); + } else { + typeName = getMappingName(aClass); + featureName = aReference.getModelEReference().getName(); + } + + final List<String> result = new ArrayList<String>(); + final List<String> names = StrategyUtil.getIDFeaturesNames(aClass, + persistenceOptions.getDefaultIDFeatureName()); + final boolean simpleNaming = optionJoinColumnNamingStrategy + .compareTo("simple") == 0; + for (String name : names) { + final String postFix; + if (names.size() == 1 && simpleNaming) { + postFix = ""; + } else { + postFix = "_" + name; + } + + final String jcName = typeName + "_" + featureName + postFix; + result.add(optionColumnNamePrefix + convert(jcName, true)); + } + return result; + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy# + * getJoinTableJoinColumns + * (org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference, boolean) + */ + public List<String> getJoinTableJoinColumns( + PAnnotatedEReference aReference, boolean inverse) { + final PAnnotatedEClass aClass; + final String typeName; + String featureName; + if (inverse) { + aClass = aReference.getAReferenceType(); + if (aReference.getModelEReference().getEOpposite() != null) { + typeName = getMappingName(aReference.getAReferenceType()); + featureName = "_" + + aReference.getModelEReference().getEOpposite() + .getName(); + } else { + typeName = getMappingName(aReference.getAReferenceType()); + featureName = ""; + } + } else { + aClass = aReference.getPaEClass(); + typeName = getMappingName(aClass); + featureName = "_" + aReference.getModelEReference().getName(); + } + // for backward compatibility, only use featurename if the reference is + // to itself + if (aReference.getAReferenceType() != aReference.getPaEClass()) { + featureName = ""; + } + + final List<String> result = new ArrayList<String>(); + final List<String> names = StrategyUtil.getIDFeaturesNames(aClass, + persistenceOptions.getDefaultIDFeatureName()); + final boolean simpleNaming = optionJoinColumnNamingStrategy + .compareTo("simple") == 0; + for (String name : names) { + final String postFix; + if (names.size() == 1 && simpleNaming) { + postFix = ""; + } else { + postFix = "_" + name; + } + + final String jcName = typeName + featureName + postFix; + result.add(optionColumnNamePrefix + convert(jcName, true)); + } + return result; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#getJoinTableName + * (org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute) + */ + public String getJoinTableName(PAnnotatedEAttribute aAttribute) { + // note for array the isMany is false, so disable this check + // assert (aAttribute.getModelEAttribute().isMany()); + final PAnnotatedEClass aClass = aAttribute.getPaEClass(); + String truncedName = getEntityName(aClass.getPaModel(), + aClass.getModelEClass()) + + "_" + aAttribute.getModelEAttribute().getName(); + return optionTableNamePrefix + convert(truncedName, true); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#getJoinTableName + * (org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference) + */ + public String getJoinTableName(PAnnotatedEReference aReference) { + final EReference eReference = aReference.getModelEReference(); + final boolean isEObject = eReference.getEType() == ClassicEntityNameStrategy.EOBJECT_ECLASS; + final String jTableName; + + EReference eOpposite = eReference.getEOpposite(); + if (aReference.getManyToMany() != null + && aReference.getManyToMany().isIndexed()) { + // In case the reference is not indexed then one join table can be + // used for both sides + // If indexed then separate join tables should be used. + eOpposite = null; // trick to force a specific naming + } + + if (!isEObject + && optionJoinTableNamingStrategy.compareToIgnoreCase("ejb3") == 0) { + // table name is the entityname of the eclass of the reference and + // entityname of + // the eclass of the refered to + final String thisEntityName = getEntityName( + aReference.getPaModel(), eReference.getEContainingClass()); + final String thatEntityName = getEntityName( + aReference.getPaModel(), eReference.getEReferenceType()); + + if (eOpposite != null && eOpposite.isMany() + && compareNames(eReference, eOpposite)) { + jTableName = thatEntityName + "_" + thisEntityName; + } else { + jTableName = thisEntityName + "_" + thatEntityName; + } + } else { + AssertUtil.assertTrue( + "option optionJoinTableNamingStrategy " + + optionJoinTableNamingStrategy + " not supported", + isEObject + || optionJoinTableNamingStrategy + .compareToIgnoreCase("unique") == 0); + if (eOpposite != null && eOpposite.isMany() + && compareNames(eReference, eOpposite)) { + final String thatEntityName = getEntityName( + aReference.getPaModel(), + eOpposite.getEContainingClass()); + jTableName = thatEntityName + "_" + eOpposite.getName(); + } else { + final String thisEntityName = getEntityName( + aReference.getPaModel(), + eReference.getEContainingClass()); + jTableName = thisEntityName + "_" + eReference.getName(); + } + } + return optionTableNamePrefix + convert(jTableName); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy# + * getDiscriminatorColumnName() + */ + public String getDiscriminatorColumnName() { + return optionColumnNamePrefix + + convert(persistenceOptions.getDiscriminatorColumnName()); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#getVersionColumnName + * () + */ + public String getVersionColumnName() { + return optionColumnNamePrefix + + convert(persistenceOptions.getVersionColumnName()); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#getIdBagIDColumn() + */ + public String getIdBagIDColumn() { + return optionColumnNamePrefix + + convert(persistenceOptions.getIDBagIDColumnName()); + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy# + * getSyntheticIDColumnName() + */ + public String getSyntheticIDColumnName() { + return optionColumnNamePrefix + + convert(persistenceOptions.getIdColumnName()); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#setPersistenceOptions + * (org.eclipse.emf.teneo.PersistenceOptions) + */ + public void setPersistenceOptions(PersistenceOptions po) { + optionMaximumSqlLength = po.getMaximumSqlNameLength(); + optionJoinTableNamingStrategy = po.getJoinTableNamingStrategy(); + optionJoinColumnNamingStrategy = po.getJoinColumnNamingStrategy(); + + final String optionSQLCaseStrategy = po.getSQLCaseStrategy(); + if (optionSQLCaseStrategy.toLowerCase().compareTo("lowercase") == 0) { + optionSQLLowerCase = true; + } else if (optionSQLCaseStrategy.toLowerCase().compareTo("uppercase") == 0) { + optionSQLUpperCase = true; + } + optionTableNamePrefix = po.getSQLTableNamePrefix(); + optionColumnNamePrefix = po.getSQLColumnNamePrefix(); + optionForeignKeyNamePrefix = po.getSQLForeignKeyNamePrefix(); + optionSQLNameColumnPrefix = po.getSQLColumnNamePrefix(); + persistenceOptions = po; + } + + // Returns the entityname of the refered to entity + protected String getEntityName(PAnnotatedModel paModel, EClass eClass) { + return StrategyUtil.getEntityName(getEntityNameStrategy(), + persistenceOptions, paModel, eClass); + } + + // Returns the entityname of the refered to entity + protected String getMappingName(PAnnotatedEClass aClass) { + return aClass.getModelEClass().getName(); + // return StrategyUtil.getEntityName(getEntityNameStrategy(), + // persistenceOptions, aClass.getPaModel(), aClass + // .getModelEClass()); + } + + public String getIndexColumnName(PAnnotatedEStructuralFeature aFeature) { + return optionSQLNameColumnPrefix + + (getMappingName(aFeature.getPaEClass()) + "_" + + aFeature.getModelEStructuralFeature().getName() + "_IDX") + .toUpperCase(); + + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#convert(java.lang + * .String) + */ + public String convert(String name) { + return convert(name, false); + } + + public String convert(String name, boolean truncPrefix) { + final String result = trunc(optionMaximumSqlLength, name, truncPrefix); + if (optionSQLLowerCase) { + return result.toLowerCase(); + } else if (optionSQLUpperCase) { + return result.toUpperCase(); + } else { + return result; + } + } + + /** + * Determines where to place a certain annotation/characteristic, this is + * done by comparing names.. + */ + protected boolean compareNames(EReference here, EReference there) { + final String nameHere = here.getEContainingClass().getName() + + here.getName(); + final String nameThere = there.getEContainingClass().getName() + + there.getName(); + assert (nameHere.compareTo(nameThere) != 0); + return nameHere.compareTo(nameThere) > 0; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#getExtensionManager + * () + */ + public ExtensionManager getExtensionManager() { + return extensionManager; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#setExtensionManager + * (org.eclipse.emf.teneo.extension.ExtensionManager) + */ + public void setExtensionManager(ExtensionManager extensionManager) { + this.extensionManager = extensionManager; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy#getEntityNameStrategy + * () + */ + public EntityNameStrategy getEntityNameStrategy() { + if (entityNameStrategy == null) { + entityNameStrategy = getExtensionManager().getExtension( + EntityNameStrategy.class); + } + return entityNameStrategy; + } + + /** + * Utility method to truncate a column/table name, the truncPrefix + * determines if the part before the _ (the prefix) or after the _ (the + * suffix) is truncated. A _ often occurs in a jointable name. + */ + protected String trunc(int optionMaximumSqlLength, String truncName, + boolean truncPrefix) { + final String correctedName = truncName.replace('.', '_'); + + if (optionMaximumSqlLength == -1) { + return correctedName; + } + if (correctedName.length() < optionMaximumSqlLength) { + return correctedName; + } + + if (!truncPrefix) { // this truncs away the suffix + return correctedName.substring(0, optionMaximumSqlLength); + } + + // truncate the part before the last _ because this is often the suffix + final int underscore = correctedName.lastIndexOf('_'); + if (underscore != -1 && underscore > 0) { + final String usStr = correctedName.substring(underscore); + if ((optionMaximumSqlLength - usStr.length()) < 0) { + return correctedName; + } + return correctedName.substring(0, + optionMaximumSqlLength - usStr.length()) + + usStr; + } + + return correctedName.substring(0, optionMaximumSqlLength); + } + +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/EntityInterfaceNameStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/EntityInterfaceNameStrategy.java new file mode 100755 index 000000000..68e26af4c --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/EntityInterfaceNameStrategy.java @@ -0,0 +1,212 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: EntityInterfaceNameStrategy.java,v 1.3 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.strategy.impl; + +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEPackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.extension.ExtensionManager; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * This implementation will first use the name of the entity annotation and then the interface name + * + * @author <a href="mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.3 $ + */ +public class EntityInterfaceNameStrategy extends EntityResolvingNameStrategy { + + /** The logger */ + private static Log log = LogFactory.getLog(EntityInterfaceNameStrategy.class); + + /** The singleton instance as it is thread safe */ + public static final EntityInterfaceNameStrategy INSTANCE = new EntityInterfaceNameStrategy(); + + // The pamodel for which this is done + private PAnnotatedModel paModel; + + // Internal cache name from name to eclass + private ConcurrentHashMap<String, EClass> entityNameToEClass = new ConcurrentHashMap<String, EClass>(); + + private ExtensionManager extensionManager; + + /* + * (non-Javadoc) + * + * @see org.elver.ecore.spring.EClassResolver#deResolve(org.eclipse.emf.ecore .EClass) + */ + @Override + public String toEntityName(EClass eClass) { + if (eClass == EOBJECT_ECLASS) { + return EOBJECT_ECLASS_NAME; + } + + if (eClass == null) { + throw new IllegalArgumentException( + "Passed eclass is null." + + "This can occur if epackages which refer to eachother are placed in different ecore/xsd files " + + "and they are not read using one resource set. The reference from one epackage to another must be " + + "resolvable by EMF."); + } + + if (eClass.getName() == null) { + throw new IllegalArgumentException( + "EClass " + + eClass.toString() + + " has a null name." + + "This can occur if epackages which refer to eachother are placed in different ecore/xsd files " + + "and they are not read using one resource set. The reference from one epackage to another must be " + + "resolvable by EMF."); + } + + // check if there is an entity annotation on the eclass with a name set + final PAnnotatedEClass aClass = getPaModel().getPAnnotated(eClass); + if (aClass != null && aClass.getEntity() != null && aClass.getEntity().getName() != null) { + return aClass.getEntity().getName(); + } + + if (eClass.getInstanceClassName() != null) { + return eClass.getInstanceClassName(); + } + return eClass.getName(); + } + + /* + * (non-Javadoc) + * + * @see org.elver.ecore.spring.EClassResolver#resolve(java.lang.String) + */ + @Override + public EClass toEClass(String eClassName) { + if (eClassName == null) { + throw new IllegalArgumentException("eClassName may not be null"); + } + + if (eClassName.compareTo(EOBJECT_ECLASS_NAME) == 0) { + return EcorePackage.eINSTANCE.getEObject(); + } + + EClass eClass = null; + if ((eClass = entityNameToEClass.get(eClassName)) != null) { + return eClass; + } + + // first try the entityname + for (final PAnnotatedEPackage aPackage : getPaModel().getPaEPackages()) { + for (final PAnnotatedEClass aClass : aPackage.getPaEClasses()) { + if (aClass.getEntity() != null && aClass.getEntity().getName() != null && + aClass.getEntity().getName().compareTo(eClassName) == 0 && + !StoreUtil.isMapEntry(aClass.getModelEClass())) { // map + // entries + // are + // ignored + if (eClass != null) { + // doubly entry! Actually require different resolver + throw new IllegalArgumentException("There is more than one EClass with the same name (" + + eClassName + " in EPackage " + eClass.getEPackage().getName() + " and " + + aPackage.getModelEPackage().getName() + ". A different EClassResolver should be used."); + } + eClass = aClass.getModelEClass(); + } + } + } + if (eClass != null) { + entityNameToEClass.put(eClassName, eClass); + return eClass; + } + // now try the eclassname itself + for (final PAnnotatedEPackage aPackage : getPaModel().getPaEPackages()) { + for (PAnnotatedEClass aClass : aPackage.getPaEClasses()) { + if (aClass.getEntity() != null && aClass.getEntity().getName() != null && + aClass.getEntity().getName().compareTo(eClassName) == 0) { + if (eClass != null) { + // doubly entry! Actually require different resolver + throw new IllegalArgumentException("There is more than one EClass with the same name (" + + eClassName + " in EPackage " + eClass.getEPackage().getName() + " and " + + aPackage.getModelEPackage().getName() + ". A different EClassResolver should be used."); + } + eClass = aClass.getModelEClass(); + } + } + } + + // we didn'y find it, perhaps it is fully qualified, lets try by full + // class name + // if (eClass == null) { + // try { + // final Class<?> cls = ClassLoaderResolver.classForName(eClassName); + // eClass = EModelResolver.instance().getEClass(cls); + // } catch (StoreClassLoadException e) { + // log.debug("Failed to retreive EClass for name: " + eClassName + + // ". This is no problem if this is a featuremap."); + // } + // } + + if (eClass == null) { + log.debug("Failed to retreive EClass for name: " + eClassName + + ". This is no problem if this is a featuremap."); + return null; + // throw new IllegalArgumentException("No EClass found using " + + // eClassName); + } + entityNameToEClass.put(eClassName, eClass); + return eClass; + } + + /** + * @return the paModel + */ + @Override + public PAnnotatedModel getPaModel() { + return paModel; + } + + /** + * @param paModel + * the paModel to set + */ + @Override + public void setPaModel(PAnnotatedModel paModel) { + this.paModel = paModel; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.extension.ExtensionManagerAware#setExtensionManager + * (org.eclipse.emf .teneo.extension.ExtensionManager) + */ + @Override + public void setExtensionManager(ExtensionManager extensionManager) { + this.extensionManager = extensionManager; + } + + /** + * @return the extensionManager + */ + @Override + public ExtensionManager getExtensionManager() { + return extensionManager; + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/EntityResolvingNameStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/EntityResolvingNameStrategy.java new file mode 100755 index 000000000..2519a2dee --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/EntityResolvingNameStrategy.java @@ -0,0 +1,222 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: EntityResolvingNameStrategy.java,v 1.12 2009/12/04 15:06:37 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.strategy.impl; + +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEPackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.extension.ExtensionManager; +import org.eclipse.emf.teneo.mapping.strategy.EntityNameStrategy; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * This implementation will first use the name of the entity annotation and then the EClass name. For the DocumentRoot + * the EClass name is always prefixed with the EPackage namespace. + * + * @author <a href="mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.12 $ + */ +public class EntityResolvingNameStrategy implements EntityNameStrategy { + + /** The logger */ + private static Log log = LogFactory.getLog(EntityResolvingNameStrategy.class); + + /** The singleton instance as it is thread safe */ + public static final EntityResolvingNameStrategy INSTANCE = new EntityResolvingNameStrategy(); + + // The pamodel for which this is done + private PAnnotatedModel paModel; + + // Internal cache name from name to eclass + private ConcurrentHashMap<String, EClass> entityNameToEClass = new ConcurrentHashMap<String, EClass>(); + + private ExtensionManager extensionManager; + + /* + * (non-Javadoc) + * + * @see org.elver.ecore.spring.EClassResolver#deResolve(org.eclipse.emf.ecore .EClass) + */ + public String toEntityName(EClass eClass) { + if (eClass == EOBJECT_ECLASS) { + return EOBJECT_ECLASS_NAME; + } + + if (eClass == null) { + throw new IllegalArgumentException( + "Passed eclass is null." + + "This can occur if epackages which refer to eachother are placed in different ecore/xsd files " + + "and they are not read using one resource set. The reference from one epackage to another must be " + + "resolvable by EMF."); + } + + if (eClass.getName() == null) { + throw new IllegalArgumentException( + "EClass " + + eClass.toString() + + " has a null name." + + "This can occur if epackages which refer to eachother are placed in different ecore/xsd files " + + "and they are not read using one resource set. The reference from one epackage to another must be " + + "resolvable by EMF."); + } + + // check if there is an entity annotation on the eclass with a name set + final PAnnotatedEClass aClass = getPaModel().getPAnnotated(eClass); + if (aClass == null) { + return createEClassEntityName(eClass); + } + if (aClass.getEntity() != null && aClass.getEntity().getName() != null) { + return aClass.getEntity().getName(); + } + + return createEClassEntityName(eClass); + } + + // always prefix DocumentRoot + protected String createEClassEntityName(EClass eClass) { + if (ExtendedMetaData.INSTANCE.isDocumentRoot(eClass)) { + return eClass.getEPackage().getNsPrefix() + "." + eClass.getName(); + } + return eClass.getName(); + } + + /* + * (non-Javadoc) + * + * @see org.elver.ecore.spring.EClassResolver#resolve(java.lang.String) + */ + public EClass toEClass(String eClassName) { + if (eClassName == null) { + throw new IllegalArgumentException("eClassName may not be null"); + } + + if (eClassName.compareTo(EOBJECT_ECLASS_NAME) == 0) { + return EcorePackage.eINSTANCE.getEObject(); + } + + EClass eClass = null; + if ((eClass = entityNameToEClass.get(eClassName)) != null) { + return eClass; + } + + // first try the entityname + for (final PAnnotatedEPackage aPackage : getPaModel().getPaEPackages()) { + for (final PAnnotatedEClass aClass : aPackage.getPaEClasses()) { + if (aClass.getEntity() != null && aClass.getEntity().getName() != null + && aClass.getEntity().getName().compareTo(eClassName) == 0 + && !StoreUtil.isMapEntry(aClass.getModelEClass())) { // map + // entries + // are + // ignored + if (eClass != null) { + // doubly entry! Actually require different resolver + throw new IllegalArgumentException("There is more than one EClass with the same name (" + + eClassName + " in EPackage " + eClass.getEPackage().getName() + " and " + + aPackage.getModelEPackage().getName() + + ". A different EClassResolver should be used."); + } + eClass = aClass.getModelEClass(); + } + } + } + if (eClass != null) { + entityNameToEClass.put(eClassName, eClass); + return eClass; + } + // now try the eclassname itself + for (final PAnnotatedEPackage aPackage : getPaModel().getPaEPackages()) { + for (PAnnotatedEClass aClass : aPackage.getPaEClasses()) { + final EClass compareToEClass = aClass.getModelEClass(); + // convert the EClass to a name using the standard approach + // also handle documentroot + final String compareToName = createEClassEntityName(compareToEClass); + if (compareToName.compareTo(eClassName) == 0) { + if (eClass != null) { + // doubly entry! Actually require different resolver + throw new IllegalArgumentException("There is more than one EClass with the same name (" + + eClassName + " in EPackage " + eClass.getEPackage().getName() + " and " + + aPackage.getModelEPackage().getName() + + ". A different EClassResolver should be used."); + } + eClass = compareToEClass; + } + } + } + + // we didn'y find it, perhaps it is fully qualified, lets try by full + // class name + // if (eClass == null) { + // try { + // final Class<?> cls = ClassLoaderResolver.classForName(eClassName); + // eClass = EModelResolver.instance().getEClass(cls); + // } catch (StoreClassLoadException e) { + // log.debug("Failed to retreive EClass for name: " + eClassName + + // ". This is no problem if this is a featuremap."); + // } + // } + + if (eClass == null) { + log.debug("Failed to retreive EClass for name: " + eClassName + + ". This is no problem if this is a featuremap."); + return null; + // throw new IllegalArgumentException("No EClass found using " + + // eClassName); + } + entityNameToEClass.put(eClassName, eClass); + return eClass; + } + + /** + * @return the paModel + */ + public PAnnotatedModel getPaModel() { + return paModel; + } + + /** + * @param paModel + * the paModel to set + */ + public void setPaModel(PAnnotatedModel paModel) { + this.paModel = paModel; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.extension.ExtensionManagerAware#setExtensionManager (org.eclipse.emf + * .teneo.extension.ExtensionManager) + */ + public void setExtensionManager(ExtensionManager extensionManager) { + this.extensionManager = extensionManager; + } + + /** + * @return the extensionManager + */ + public ExtensionManager getExtensionManager() { + return extensionManager; + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/QualifyingEntityNameStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/QualifyingEntityNameStrategy.java new file mode 100755 index 000000000..3ac9cf33a --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/QualifyingEntityNameStrategy.java @@ -0,0 +1,136 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: QualifyingEntityNameStrategy.java,v 1.8 2009/07/28 03:39:44 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.strategy.impl; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEPackage; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedModel; +import org.eclipse.emf.teneo.extension.ExtensionManager; +import org.eclipse.emf.teneo.mapping.strategy.EntityNameStrategy; + +/** + * This implementation prefixes the eclass names with the epackage nsprefix. This makes it less likely that name clashes + * occur of eclasses in different packages. + * + * @author <a href="mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.8 $ + */ +public class QualifyingEntityNameStrategy implements EntityNameStrategy { + + // The pamodel for which this is done + private PAnnotatedModel paModel; + + public static final QualifyingEntityNameStrategy INSTANCE = new QualifyingEntityNameStrategy(); + + private ExtensionManager extensionManager; + + public String toEntityName(EClass eClass) { + if (eClass == null) { + throw new IllegalArgumentException("EClass cannot be null."); + } + if (eClass == EOBJECT_ECLASS) { + return EOBJECT_ECLASS_NAME; + } + String nsPrefix = eClass.getEPackage().getNsPrefix(); + if (nsPrefix == null) { + nsPrefix = eClass.getEPackage().getName(); + } + return nsPrefix + "." + eClass.getName(); + } + + public EClass toEClass(String eClassStr) { + if (eClassStr == null) { + throw new IllegalArgumentException("eClassStr may not be null"); + } + + if (eClassStr.compareTo(EOBJECT_ECLASS_NAME) == 0) { + return EcorePackage.eINSTANCE.getEObject(); + } + + // get prefix or name + final int index = eClassStr.lastIndexOf("."); + if (index == -1) { + throw new IllegalArgumentException( + "Illegal eClassStr for this resolver (no dot separating the epackage nsprefix and name): " + + eClassStr); + } + final String nsPrefix = eClassStr.substring(0, index); + final String eClassName = eClassStr.substring(index + 1); + + // now try all epackages + EClass eClass = null; + for (final PAnnotatedEPackage aPackage : getPaModel().getPaEPackages()) { + final EPackage ePackage = aPackage.getModelEPackage(); + if (ePackage.getNsPrefix().compareTo(nsPrefix) != 0 && ePackage.getName().compareTo(nsPrefix) != 0) { + continue; + } + for (final PAnnotatedEClass aClass : aPackage.getPaEClasses()) { + final EClass checkEClass = aClass.getModelEClass(); + if (checkEClass.getName().compareTo(eClassName) == 0) { + if (eClass != null) { + // doubly entry! Actually require different resolver + throw new IllegalArgumentException( + "There is more than one EClass with the same identifying String (" + eClassStr + + " in EPackage " + eClass.getEPackage().getName() + " and " + + ePackage.getName() + ". A different EClassResolver should be used."); + } + eClass = checkEClass; + } + } + } + if (eClass == null) { + throw new IllegalArgumentException("No EClass found using " + eClassStr); + } + return eClass; + } + + /** + * @return the paModel + */ + public PAnnotatedModel getPaModel() { + return paModel; + } + + /** + * @param paModel + * the paModel to set + */ + public void setPaModel(PAnnotatedModel paModel) { + this.paModel = paModel; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.extension.ExtensionManagerAware#setExtensionManager + * (org.eclipse.emf.teneo.extension.ExtensionManager) + */ + public void setExtensionManager(ExtensionManager extensionManager) { + this.extensionManager = extensionManager; + } + + /** + * @return the extensionManager + */ + public ExtensionManager getExtensionManager() { + return extensionManager; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/TeneoNewSQLNameStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/TeneoNewSQLNameStrategy.java new file mode 100644 index 000000000..c59c01f51 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/TeneoNewSQLNameStrategy.java @@ -0,0 +1,36 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: TeneoNewSQLNameStrategy.java,v 1.1 2009/10/02 07:23:23 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.strategy.impl; + +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.mapping.strategy.StrategyUtil; + +/** + * Mapping strategy which uses the EClass entity name. + * + * @author <a href="mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.1 $ + */ +public class TeneoNewSQLNameStrategy extends TeneoSQLNameStrategy { + + // Returns the entityname of the refered to entity + protected String getMappingName(PAnnotatedEClass aClass) { + return StrategyUtil.getEntityName(getEntityNameStrategy(), persistenceOptions, aClass.getPaModel(), aClass + .getModelEClass()); + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/TeneoSQLNameStrategy.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/TeneoSQLNameStrategy.java new file mode 100755 index 000000000..b57b0b9f7 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/mapping/strategy/impl/TeneoSQLNameStrategy.java @@ -0,0 +1,204 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: TeneoSQLNameStrategy.java,v 1.10 2010/01/26 20:09:42 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.mapping.strategy.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; +import org.eclipse.emf.teneo.mapping.strategy.StrategyUtil; + +/** + * Differences between this implementation and the ClassicSQLNameStrategy is the way truncation is done if a name is + * longer than the sql name length constraint. To truncate a name this class will first remove vowels (in the order: u, + * o, a, e, i) and if that is not enough it will truncate the different parts of a name (separated by _). + * + * @author <a href="mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.10 $ + */ +public class TeneoSQLNameStrategy extends ClassicSQLNameStrategy { + + // The logger + protected static final Log log = LogFactory.getLog(TeneoSQLNameStrategy.class); + + private static String[] removables = new String[] { "u", "o", "a", "e", "i" }; + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.teneo.mapping.strategy.SqlNameStrategy# getManyToOneJoinColumnNames + * (org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference) + */ + @Override + public List<String> getManyToOneJoinColumnNames(PAnnotatedEReference aReference) { + final EReference eref = aReference.getModelEReference(); + + // isTransient occurs for computed featuremap features, these are + // ignored + // later on + assert (eref.isTransient() || !eref.isMany()); // otherwise this should + // have been a mtm + + // in case of many-to-one to qualify use the name of the class to which + // is refered + // this is only used in case of non-simple naming (simple naming is + // better readable)! + final PAnnotatedEClass aClass; + if (eref.getEOpposite() == null) { + aClass = aReference.getAReferenceType(); + } else { + // the aclass is just the class of the structuralfeature itself. + // This is done so that both sides of the relationship use the same + // columns + aClass = aReference.getPaEClass(); + } + final String typeName = getMappingName(aClass); + final String featureName = eref.getName(); + + final List<String> result = new ArrayList<String>(); + final List<String> names = StrategyUtil.getIDFeaturesNames(aReference.getAReferenceType(), persistenceOptions + .getDefaultIDFeatureName()); + final boolean simpleNaming = optionJoinColumnNamingStrategy.compareTo("simple") == 0; + for (String name : names) { + final String postFix; + if (names.size() == 1 && simpleNaming) { + postFix = ""; + } else { + postFix = "_" + name; + } + + final String jcName; + if (simpleNaming) { + jcName = featureName + postFix; + } else { // backward compatibility + jcName = typeName + "_" + featureName + postFix; + } + result.add(optionColumnNamePrefix + convert(jcName, true)); + } + return result; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.teneo.mapping.strategy.impl.ClassicSQLNameStrategy#trunc (int, java.lang.String, boolean) + */ + @Override + public String trunc(int maxSqlLength, String truncName, boolean truncPrefix) { + String correctedName = truncName.replace('.', '_'); + if (maxSqlLength == -1) { + return correctedName; + } + if (correctedName.length() <= maxSqlLength) { + return correctedName; + } + + correctedName = correctedName.replaceAll("__", "_"); + if (correctedName.startsWith("_")) { + correctedName = correctedName.substring(1); + } + + if (correctedName.length() <= maxSqlLength) { + return correctedName; + } + + // first do some standard things + // truncate the standard e_id + correctedName = correctedName.replaceAll("e_id", "id"); + if (correctedName.length() <= maxSqlLength) { + return correctedName; + } + + // now do vowel truncation preserving the first character + char correctedNameFirstChar = correctedName.charAt(0); + String correctedNameTail = correctedName.substring(1); + for (String vowel : getRemovableCharacters()) { + while (correctedNameTail.indexOf(vowel) != -1 || correctedNameTail.indexOf(vowel.toUpperCase()) != -1) { + if (correctedNameTail.indexOf(vowel) != -1) { + correctedNameTail = correctedNameTail.replaceFirst(vowel, ""); + } else { + correctedNameTail = correctedNameTail.replaceFirst(vowel.toUpperCase(), ""); + } + correctedNameTail = correctedNameTail.replaceAll("__", "_"); + if ((correctedNameTail.length() + 1) <= maxSqlLength) { + return correctedNameFirstChar + correctedNameTail; + } + } + } + + // still failed do length truncation + return doLengthTruncation(maxSqlLength, correctedNameFirstChar + correctedNameTail); + } + + protected String doLengthTruncation(int maxSqlLength, String correctedName) { + // failed do length truncation with the remainder + final int underscore = correctedName.lastIndexOf('_'); + if (underscore == -1) { + return correctedName.substring(0, maxSqlLength); + } + + // now do the complex logic to truncate different parts + final String[] parts = correctedName.split("_"); + int maxLength = -1; + for (String part : parts) { + if (part.length() > maxLength && part.length() > 0) { + maxLength = part.length(); + } + } + + // can this ever happen + int totalLength = correctedName.length(); + while (maxLength > 1 && totalLength > maxSqlLength) { + totalLength = 0; + int newMax = 0; + for (int i = 0; i < parts.length; i++) { + if (parts[i].length() == maxLength) { + parts[i] = parts[i].substring(0, maxLength - 1); + } + if (parts[i].length() > newMax) { + newMax = parts[i].length(); + } + totalLength += parts[i].length(); + } + totalLength += parts.length - 1; // count the underscores + maxLength = newMax; + } + + final StringBuffer result = new StringBuffer(); + for (String part : parts) { + if (result.length() > 0) { + result.append("_"); + } + result.append(part); + } + + return result.toString(); + } + + /** + * Return the characters to remove, the character removal is done in order of the returned array. This method is + * provided to be overridden to pass a custom set of removable characters. + */ + protected String[] getRemovableCharacters() { + return removables; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/NonLoadingDiagnostician.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/NonLoadingDiagnostician.java new file mode 100755 index 000000000..68b07ada7 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/NonLoadingDiagnostician.java @@ -0,0 +1,56 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: NonLoadingDiagnostician.java,v 1.7 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.resource; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.common.util.DiagnosticChain; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.Diagnostician; + +/** + * Extends the default EMF Diagnostican to prevent the validation to load unloaded lists. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.7 $ + */ + +public class NonLoadingDiagnostician extends Diagnostician { + + /** The instance */ + public static NonLoadingDiagnostician INSTANCE = new NonLoadingDiagnostician(); + + /** Overriden to prevent loading of complete content */ + @Override + protected boolean doValidateContents(EObject eObject, DiagnosticChain diagnostics, Map<Object, Object> context) { + List<EObject> eContents = NonLoadingEContentsEList.create(eObject, true); + if (!eContents.isEmpty()) { + Iterator<EObject> i = eContents.iterator(); + EObject child = i.next(); + boolean result = validate(child, diagnostics, context); + while (i.hasNext() && (result || diagnostics != null)) { + child = i.next(); + result &= validate(child, diagnostics, context); + } + return result; + } + return true; + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/NonLoadingEContentsEList.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/NonLoadingEContentsEList.java new file mode 100755 index 000000000..060628144 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/NonLoadingEContentsEList.java @@ -0,0 +1,85 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: NonLoadingEContentsEList.java,v 1.8 2010/02/04 11:03:00 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.resource; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.InternalEObject.EStore; +import org.eclipse.emf.ecore.util.EContentsEList; +import org.eclipse.emf.teneo.mapping.elist.PersistableEList; +import org.eclipse.emf.teneo.mapping.elist.PersistableEMap; +import org.eclipse.emf.teneo.mapping.elist.PersistableFeatureMap; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * Is a contents elist which will only iterate over loaded efeatures. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.8 $ + */ + +public class NonLoadingEContentsEList<E> extends EContentsEList<E> { + + /** Creates an instance of a NonResolvingEContentsEList for the EObject */ + public static EContentsEList<EObject> create(EObject eObject, boolean forValidation) { + final ArrayList<EStructuralFeature> result = new ArrayList<EStructuralFeature>(); + for (EReference eref : eObject.eClass().getEAllReferences()) { + if (!eref.isContainment()) { + continue; + } + if (eref.isMany()) { + List<?> list = (List<?>) eObject.eGet(eref); + + // in case of an estore the list has to be retrieved in a + // special + // way + if (StoreUtil.isEStoreList(list)) { + final EStore eStore = ((InternalEObject) eObject).eStore(); + list = (List<?>) eStore.get((InternalEObject) eObject, eref, EStore.NO_INDEX); + } + + if ((list instanceof PersistableEList<?>) && ((PersistableEList<?>) list).isLoaded()) { + result.add(eref); + } else if ((list instanceof PersistableEMap<?,?>) && ((PersistableEMap<?, ?>) list).isLoaded()) { + result.add(eref); + } else if ((list instanceof PersistableFeatureMap) && ((PersistableFeatureMap) list).isLoaded()) { + result.add(eref); + } else if (eref.getLowerBound() > 0 && forValidation) { + result.add(eref); + } + if (!(list instanceof PersistableEList<?>) && !(list instanceof PersistableFeatureMap) && + !(list instanceof PersistableEMap<?,?>)) { + result.add(eref); + } + + } else { + result.add(eref); + } + } + return new NonLoadingEContentsEList<EObject>(eObject, result); + } + + private NonLoadingEContentsEList(EObject eObject, List<? extends EStructuralFeature> eStructuralFeatures) { + super(eObject, eStructuralFeatures); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/StoreResource.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/StoreResource.java new file mode 100755 index 000000000..d50f11dd1 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/StoreResource.java @@ -0,0 +1,1115 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * Benjamin Cabé - See bugzilla 176356 + * + * </copyright> + * + * $Id: StoreResource.java,v 1.44 2011/07/03 11:16:06 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.resource; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.notify.NotificationChain; +import org.eclipse.emf.common.notify.impl.AdapterImpl; +import org.eclipse.emf.common.notify.impl.NotificationImpl; +import org.eclipse.emf.common.util.AbstractTreeIterator; +import org.eclipse.emf.common.util.BasicEMap; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.common.util.TreeIterator; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.impl.BasicEObjectImpl; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.impl.ResourceImpl; +import org.eclipse.emf.ecore.util.EContentsEList; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.util.InternalEList; +import org.eclipse.emf.ecore.xmi.XMIResource; +import org.eclipse.emf.ecore.xmi.XMLResource; +import org.eclipse.emf.teneo.EContainerRepairControl; +import org.eclipse.emf.teneo.StoreValidationException; +import org.eclipse.emf.teneo.util.FieldUtil; + +/** + * General part of Store Resources. Main feature is that it keeps track of + * changes to the resource content and that settrackingmodification will not + * load unloaded elists. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.44 $ + */ + +public abstract class StoreResource extends ResourceImpl { + + /** The name of the parameter used for the datastore name */ + public static final String DS_NAME_PARAM = "dsname"; + + /** + * Prefix to use when specifying query parameters in the uri or in the + * property file + */ + public static final String QUERY_PREFIX = "query"; + + /** Load strategy */ + public static final String LOAD_STRATEGY_PARAM = "loadStrategy"; + + /** The default strategy */ + public static final String ADD_TO_CONTENTS = "addToContents"; + + /** The just set eResource */ + public static final String SET_ERESOURCE = "setEResource"; + + /** + * Don't do anything, risk of dangling_references but is better if objects + * get added to other resources anyway. + */ + public static final String NOT_SET_ERESOURCE = "notSetEResource"; + + /** The logger */ + private static Log log = LogFactory.getLog(StoreResource.class); + + /** + * The list of objects which will be deleted at commit time. Needs to be a + * list because of the order of deletion. + */ + protected List<EObject> removedEObjects = new ArrayList<EObject>(); + + /** + * The set of objects loaded from the backend. objects which have been + * removed are still part of this list. This is to prevent them from being + * added to the newEObjects set. + */ + protected HashSet<EObject> loadedEObjects = new HashSet<EObject>(); + // is used for more efficient access + protected HashSet<EObject> loadedEObjectSet = new HashSet<EObject>(); + + /** + * The set of new objects new EObjects are never part of the loadedEObjects, + * when a newEObject is removed it is just removed from this set and not + * added to the removed EObjects. new eobjects are never part of the + * modifiedEObjects set. + */ + protected List<EObject> newEObjects = new ArrayList<EObject>(); + // is used for more efficient access + protected HashSet<EObject> newEObjectSet = new HashSet<EObject>(); + + /** + * The set of changed objects, this contains the loadedEObjects which have + * changed new EObjects are never part of this set + */ + protected HashSet<EObject> modifiedEObjects = new HashSet<EObject>(); + + /** + * Apparently the super EMF resource classes have a different idea of loaded + * During the unload action they again start loading from the db, this + * should be prevented. + */ + protected boolean isUnLoading = false; + + /** The list of topclasses */ + protected String[] topClassNames; + + /** + * The list of queries if they are defined for this resource. If not set + * (length is 0) then the resource will as default behavior read the + * topclasses of the database. + */ + private String[] definedQueries = new String[0]; + + /** Send notifications under load */ + private boolean sendNotificationsOnLoad = true; + + /** Is send notification set by parameter */ + private boolean sendNotificationSetByParam = false; + + /** The load strategy */ + private String loadStrategy = SET_ERESOURCE; + + /** + * The constructor, gets an uri and retrieves the backing OJBStore + */ + public StoreResource(URI uri) { + super(uri); + + final Map<String, String> params = decodeQueryString(uri.query()); + if (params.get(LOAD_STRATEGY_PARAM) != null) { + loadStrategy = params.get(LOAD_STRATEGY_PARAM); + } + + if (log.isDebugEnabled()) { + log.debug("Created " + this.getClass().getName() + " using uri: " + + uri.toString()); + } + + if (params.get(XMLResource.OPTION_DISABLE_NOTIFY) != null) { + sendNotificationSetByParam = true; + sendNotificationsOnLoad = "false".compareToIgnoreCase(params + .get(XMLResource.OPTION_DISABLE_NOTIFY)) == 0; + } else if (params.get(XMIResource.OPTION_DISABLE_NOTIFY) != null) { + sendNotificationSetByParam = true; + sendNotificationsOnLoad = "false".compareToIgnoreCase(params + .get(XMIResource.OPTION_DISABLE_NOTIFY)) == 0; + } + + setLoaded(false); + + // note this ugly statement is required because in this class + // it is required to have direct access to the contents member + // the call to getSuperContents will initialize it + if (getContents().size() == 0) { + log.debug("Initialized contents member"); + } + + // set the intrinsicmap + setIntrinsicIDToEObjectMap(new HashMap<String, EObject>()); + } + + /** Sets topclasses */ + protected void init(String[] topClasses) { + topClassNames = topClasses; + } + + public void setIsLoading(boolean loading) { + isLoading = loading; + } + + // ----------------------------------- Defined Queries + // ---------------------------------- + + /** Sets the defined queries */ + public void setDefinedQueries(String[] qrys) { + log.debug("Setting defined queries of resource " + getURI().toString() + + "/" + this.getClass().getName()); + for (String element : qrys) { + log.debug("Adding query: " + element); + } + + definedQueries = qrys; + } + + /** Returns true if there are defined queries */ + public boolean definedQueriesPresent() { + return definedQueries.length > 0; + } + + /** + * Returns the current array of defined queries. Note if there are no + * definedQueries then an array of length 0 is returned. + */ + public String[] getDefinedQueries() { + return definedQueries; + } + + /** Returns a list of queries if they are passed as parameters */ + protected String[] getQueries(Map<?, ?> params) { + final ArrayList<String> queries = new ArrayList<String>(); + for (Object key : params.keySet()) { + if (((String) key).startsWith(QUERY_PREFIX)) { + queries.add((String) params.get(key)); + } + } + return queries.toArray(new String[queries.size()]); + } + + /** Get the parameter from the hashmap, if not found then throw an exception */ + protected String getParam(Map<String, String> params, String paramName, + String report) { + final String param = params.get(paramName); + if (param == null) { + throw new StoreResourceException("Parameter " + paramName + + " missing in querystring: " + report); + } + return param; + } + + /** Decode the query string in a hashmap */ + protected Map<String, String> decodeQueryString(String qryStr) { + final TreeMap<String, String> result = new TreeMap<String, String>(); + + if (qryStr == null) { + return result; + } + + final String[] qryParts = qryStr.split("&"); + for (final String qryPart : qryParts) { + final String fieldName = qryPart.substring(0, qryPart.indexOf('=')); + final String fieldValue = URI.decode(qryPart.substring(qryPart + .indexOf('=') + 1)); + result.put(fieldName, fieldValue); + } + return result; + } + + // ------------------------------ Subclass methods + // --------------------------------- + + /** + * Returns an array of EObjects which refer to a certain EObject, note if + * the array is of length zero then no refering EObjects where found. + */ + public abstract Object[] getCrossReferencers(EObject referedTo); + + /** Load implemented by subclass */ + protected abstract List<EObject> loadResource(Map<?, ?> options); + + /** Save implemented by subclass */ + protected abstract void saveResource(Map<?, ?> options); + + // ------------------------------------ Load + // ------------------------------------------ + + /** Returns true if the resource is unloading */ + public boolean isUnLoading() { + return isUnLoading; + } + + /** Loads the resource */ + @Override + public void load(Map<?, ?> options) { + if (isUnLoading) { + return; + } + if (isLoaded()) { + return; + } + if (isLoading()) { + return; + } + + String option; + if (options != null + && (option = (String) options + .get(XMLResource.OPTION_DISABLE_NOTIFY)) != null) { + sendNotificationsOnLoad = "false".compareToIgnoreCase(option) == 0; + } else if (options != null + && (option = (String) options + .get(XMIResource.OPTION_DISABLE_NOTIFY)) != null) { + sendNotificationsOnLoad = "false".compareToIgnoreCase(option) == 0; + } else if (!sendNotificationSetByParam) { + sendNotificationsOnLoad = true; + } + + if (options != null && options.get(LOAD_STRATEGY_PARAM) != null) { + loadStrategy = (String) options.get(LOAD_STRATEGY_PARAM); + } + + isLoading = true; + Notification notification = setLoaded(true); + try { + clearChangeTrackerArrays(); + List<EObject> list = loadResource(options); + for (EObject eObject : list) { + addToContent((InternalEObject) eObject); + } + } finally { + if (notification != null) { + eNotify(notification); + } + + setModified(false); + isLoading = false; + } + } + + /** Add to this resource */ + protected void addToContent(InternalEObject eObject) { + // note direct access to super member + if (!getLocalContents().contains(eObject)) { + final NotificationChain notifications = getLocalContents() + .basicAdd(eObject, null); + if (notifications != null && sendNotificationsOnLoad) { + notifications.dispatch(); + } + setEResource(eObject, true); + attached(eObject); + } + } + + protected void addUsingContainmentStructure(InternalEObject eObject) { + final boolean oldLoading = isLoading(); + try { + setIsLoading(true); + // find the topcontainer + InternalEObject currentEObject = eObject; + while (currentEObject.eContainer() != null + && !currentEObject.eContainmentFeature().isResolveProxies()) { + currentEObject = (InternalEObject) currentEObject.eContainer(); + } + // if the topcontainer is not yet part of the resource then add it + if (!loadedEObjectSet.contains(currentEObject)) { + final NotificationChain notifications = getLocalContents() + .basicAdd(eObject, null); + if (notifications != null && sendNotificationsOnLoad) { + notifications.dispatch(); + } + setEResource(eObject, true); + // attached will also call the children + attached(currentEObject); + } else if (!loadedEObjectSet.contains(eObject)) { + // container is already part of the resource + // just add the current object to the loaded eobjects + attached(eObject); + } + } finally { + setIsLoading(oldLoading); + } + } + + // This method is called when ereferences are resolved. + public void addToContentOrAttach(InternalEObject eObject, + EReference eReference) { + final boolean oldLoading = isLoading(); + try { + if (loadStrategy.compareTo(ADD_TO_CONTENTS) == 0) { + if (eObject.eResource() != null && eObject.eResource() != this) { + return; + } + // always add to the resource, this will change + // the contents of the resource + addUsingContainmentStructure(eObject); + } else if (eReference.isContainment()) { + // if resolve proxies then it is allowed to have child objects + // in other + // resources + if (eReference.isResolveProxies() + && eObject.eResource() != null + && eObject.eResource() != this) { + return; + } + // only add if contained, just add to the loaded eobjects lists + assert (eObject.eContainer().eResource() == this); + attached(eObject); + } else if (loadStrategy.compareTo(SET_ERESOURCE) == 0) { + // this is not the nicest solution because everyone is added to + // the top of the + // resource. + // but it prevents dangling references + // when objects are not added explicitly to a resource + setEResource(eObject, false); + attached(eObject); + } + } finally { + setIsLoading(oldLoading); + } + } + + /** + * Called by subclass + * + * /** Saves the resource + */ + @Override + public void save(Map<?, ?> options) { + boolean err = true; + try { + setAllowNotifications(false); + validateContents(); + saveResource(options); + err = false; + } catch (Throwable t) { + throw new IllegalStateException(t); + } finally { + // now clear the changed eobjects and move the new objects + // to the loaded eobjects + setAllowNotifications(true); + if (!err) { + modifiedEObjects.clear(); + removedEObjects.clear(); + loadedEObjects.addAll(newEObjects); + loadedEObjectSet.addAll(newEObjects); + newEObjects.clear(); + newEObjectSet.clear(); + setModified(false); + } + + } + } + + /** + * Clears different lists to start with an empty resource again. + */ + @Override + protected void doUnload() { + isUnLoading = true; + super.doUnload(); + clearChangeTrackerArrays(); + isUnLoading = false; + } + + /* + * Javadoc copied from interface. + */ + @Override + public EList<EObject> getContents() { + if (contents == null) { + contents = new LocalContentsEList(); + } + return contents; + } + + protected LocalContentsEList getLocalContents() { + return (LocalContentsEList) getContents(); + } + + // this specific ContentsElist overrides inverseremove to handle the + // following case + // using queries it is possible to load a parent and child both in the root + // of the + // resource. During unload of the resource the child is removed from the + // parent + // see the BasicEObjectImpl.eSetResource implementation. This is undesirable + // therefore + // the inverseRemove method is overridden. + // See bugzilla 227500 + protected class LocalContentsEList extends ContentsEList<EObject> { + private static final long serialVersionUID = 1L; + + @Override + public NotificationChain inverseRemove(EObject object, + NotificationChain notifications) { + if (!isUnLoading()) { + return super.inverseRemove(object, notifications); + } + if (!unloadingContents.contains(object)) { + return super.inverseRemove(object, notifications); + } + + final InternalEObject eObject = (InternalEObject) object; + final InternalEObject eContainer = eObject.eInternalContainer(); + if (eContainer == null) { + return super.inverseRemove(object, notifications); + } + + // specific case which works fine as in this case the + // object is not removed from its container + if (eObject.eContainmentFeature().isResolveProxies()) { + return super.inverseRemove(object, notifications); + } + + // can this ever happen? + if (!(eObject instanceof BasicEObjectImpl)) { + return super.inverseRemove(object, notifications); + } + + // now the only thing remaining is mimick the inverseRemove without + // removal + // from the container + // this is the invariant: + // eDirectResource() != null && eContainer != null + // in this case the directresource has to be set, using java + // reflection to handle that + final Resource oldResource = eObject.eDirectResource(); + if (oldResource != null) { + notifications = ((InternalEList<?>) oldResource.getContents()) + .basicRemove(this, notifications); + } + FieldUtil.callMethod(eObject, "eSetDirectResource", + new Object[] { null }); + + return notifications; + } + + // reimplement the contains because the super class does something + // strange with checking + // the eresource of the object, this implementation needs to check the + // actual content + public boolean contains(Object object) { + if (useEquals() && object != null) { + for (int i = 0; i < size; ++i) { + if (object.equals(data[i])) { + return true; + } + } + } else { + for (int i = 0; i < size; ++i) { + if (data[i] == object) { + return true; + } + } + } + + return false; + } + + } + + /** + * Validate the contents of the resource, only the changed/new EObjects are + * checked + */ + protected void validateContents() throws StoreValidationException { + // get the changed or new eobjects + final ArrayList<EObject> newOrChangedObjects = new ArrayList<EObject>( + newEObjects); + newOrChangedObjects.addAll(modifiedEObjects); + + log.debug("Validating contents of resource " + uri + " approx. " + + newOrChangedObjects.size() + " will be checked"); + + final ArrayList<org.eclipse.emf.common.util.Diagnostic> diags = new ArrayList<org.eclipse.emf.common.util.Diagnostic>(); + for (int i = 0; i < newOrChangedObjects.size(); i++) { + final InternalEObject obj = (InternalEObject) newOrChangedObjects + .get(i); + + // ensure that the resource is set correctly before validating + if (obj.eResource() != this) { + assert (obj.eResource() == this); + } + EContainerRepairControl.setEResourceToAlLContent(obj, this); + + if (newOrChangedObjects.contains(obj.eContainer())) { + continue; // they will be checked as part of their container + } + + final org.eclipse.emf.common.util.Diagnostic diag = validateObject(newOrChangedObjects + .get(i)); + if (diag != null) { + diags.add(diag); + } + } + log.debug("Found " + diags.size() + " errors "); + if (diags.size() > 0) { + throw new StoreValidationException( + diags.toArray(new org.eclipse.emf.common.util.Diagnostic[diags + .size()])); + } + } + + /** Copied from IBM tutorial, validates one eobject */ + public org.eclipse.emf.common.util.Diagnostic validateObject(EObject eObject) { + org.eclipse.emf.common.util.Diagnostic diagnostic = NonLoadingDiagnostician.INSTANCE + .validate(eObject); + if (diagnostic.getSeverity() == org.eclipse.emf.common.util.Diagnostic.ERROR) { + return diagnostic; + } + return null; + } + + // -------------------------- Content Iteration + // ----------------------------------- + + // /** + // * Override load to force a load when getContents is called without a + // previous load call. + // */ + // @Override + // public EList<EObject> getContents() { + // // the getContents should not load but should return an + // // empty string if not yet loaded. + // // if (!isLoaded() && !isLoading) { + // // load(null); + // // } + // // + // // and then do our thing! + // return super.getContents(); + // } + // + // /** + // * Extra method to allow subclasses to easily reach the contents of the + // superclass of this class + // */ + // protected EList<EObject> getSuperContents() { + // return super.getContents(); + // } + + // -------------------------------- Change Tracker + // ---------------------------------- + + /** Clears the change tracker arrays */ + public void clearChangeTrackerArrays() { + removedEObjects.clear(); + newEObjects.clear(); + newEObjectSet.clear(); + loadedEObjects.clear(); + loadedEObjectSet.clear(); + modifiedEObjects.clear(); + } + + /** + * Keeps track of changed objects, CHECK: currently if a tree of objects has + * been added to this resource and afterwards a child in the tree is changed + * then the child is added to the modifiedEObjects list while its containing + * parent is part of the addedEObjects list. This should maybe be prevented + * here but this can come at some cost as it means that the complete + * container path has to be loaded for each modification. + */ + public void modifiedEObject(EObject eObject) { + // if (addedEObjects.contains(eObject)) return; // see remark above, if + // childs are added to the modified list + // then childs also + + if (loadedEObjectSet.contains(eObject) + && !modifiedEObjects.contains(eObject)) { + assert (!newEObjectSet.contains(eObject)); + modifiedEObjects.add(eObject); + } + } + + /** Keeps track of new objects */ + public void addedEObject(EObject eObject) { + if (isLoading()) { + if (removedEObjects.contains(eObject)) { + // special case an eobject was removed from the resource but is + // readded during load of an elist + // the remove will be done at save + return; + } + // assert (!removedEObjects.contains(eObject)); + assert (!loadedEObjectSet.contains(eObject)); + loadedEObjects.add(eObject); + loadedEObjectSet.add(eObject); + } else { + // assert (!removedEObjects.contains(eObject)); + assert (!newEObjectSet.contains(eObject)); + if (removedEObjects.remove(eObject)) { + modifiedEObjects.add(eObject); + } else { + newEObjects.add(eObject); + newEObjectSet.add(eObject); + } + } + } + + /** Keeps track of removed objects */ + public void removedEObject(EObject eObject) { + assert (!removedEObjects.contains(eObject)); + + if (newEObjectSet.contains(eObject)) { + assert (newEObjects.contains(eObject)); + newEObjects.remove(eObject); // just remove from this list + newEObjectSet.remove(eObject); + return; + } + + if (!loadedEObjectSet.contains(eObject)) { + assert (!loadedEObjects.contains(eObject)); + } + assert (!removedEObjects.contains(eObject)); + + if (!(eObject instanceof BasicEMap.Entry<?, ?>)) { + removedEObjects.add(eObject); + } + + loadedEObjects.remove(eObject); + loadedEObjectSet.remove(eObject); + modifiedEObjects.remove(eObject); + } + + /** Object is attached, is overridden to use non-resolving iterator */ + @Override + public void attached(EObject eObject) { + attachedHelper(eObject); + for (Iterator<EObject> tree = getNonResolvingContent(eObject) + .basicIterator(); tree.hasNext();) { + final Object obj = tree.next(); + // before this called: + // attachedHelper((EObject) obj); + // however this resulted in child elements (which were loaded) not + // being + // added to the internal id map + attached((EObject) obj); + } + } + + /** Detached means deleted from resource */ + @Override + public void detached(EObject eObject) { + detachedHelper(eObject); + for (EObject object : getNonResolvingContent(eObject)) { + detachedHelper(object); + } + } + + /** + * Attached the object to this resource, This object will be stored at the + * next save action. Also handles the specific id generation used for + * different resource impl. + * + * Note that this method does not add the object to the + * resource.getContents(). It just adds the object to the internal lists of + * this resource. + */ + // 20 April 2008: cleaned up side-effects of this method, add to contents, + // setting + // eresource has been disabled, this should be done in calling methods + @Override + protected void attachedHelper(EObject eObject) { + // also attach the container + // if (eObject.eContainer() != null && eObject.eContainer().eResource() + // == null && + // !eObject.eContainmentFeature().isResolveProxies()) { + // attached(eObject.eContainer()); + // } + + // a bit strange as an object can only be contained once but this can + // happen if someone + // adds an object to a resource directly and then later add this same + // object as a child + // to a container + if (newEObjectSet.contains(eObject) + || loadedEObjectSet.contains(eObject)) { + return; + } + + // Already belongs to another resource + if (eObject.eResource() != null && eObject.eResource() != this) { + return; + } + + addedEObject(eObject); + + // if (eObject instanceof InternalEObject && eObject.eResource() == + // null) { + // setEResource((InternalEObject) eObject, false); + // } + + // NOTE: should this really happen here? My feel is that this should + // be cleaned up and be done outside of this method + // This is required here because the attached method is called + // recursively for the container, see the first if in this method + // only add an eobject to contents if it does not have a container or if + // the container + // relation allows resolve proxies (container and contained can be in + // different resources) + // and the load strategy is correct + // if ((eObject.eContainer() == null || eObject.eContainmentFeature() == + // null || + // eObject.eContainmentFeature() + // .isResolveProxies()) && + // !getContents().contains(eObject) && + // loadStrategy.compareTo(ADD_TO_CONTENTS) == 0) { + // // note direct access to super contents + // final NotificationChain notifications = contents.basicAdd(eObject, + // null); + // if (notifications != null && sendNotificationsOnLoad) { + // notifications.dispatch(); + // } + // } + + if (isTrackingModification()) { + eObject.eAdapters().add(modificationTrackingAdapter); + } + + Map<String, EObject> map = getIntrinsicIDToEObjectMap(); + if (map != null) { + String id = EcoreUtil.getID(eObject); + if (id != null) { + map.put(id, eObject); + } else { + id = getURIFragment(eObject); + if (id != null) { + map.put(id, eObject); + } + } + } + + // now also attach all ereferences with single values which are + // contained + for (EReference eref : eObject.eClass().getEAllReferences()) { + if (!eref.isMany() && eObject.eGet(eref, false) != null) { // the + // ismanies + // are handled + // differently + final Resource res = ((EObject) eObject.eGet(eref)).eResource(); + if (res == null) { // attach it to this resource because it has + // no other + final InternalEObject referedTo = (InternalEObject) eObject + .eGet(eref); + addToContentOrAttach(referedTo, eref); + } + } + } + } + + /** + * Add to the contents of this resource and dispatch if send notification. + * It also takes care of attaching this object and its contained children to + * the internal lists and setting the eResource. + * + * Approaches: - Set eResource and add to contents (if no econtainer) - Just + * set eResource and add to contents if it does not have a container public + * void addToResource(InternalEObject eObject, boolean forceAddToContents) { + * // if it has an econtainer then also add the econtainer if + * (eObject.eContainer() != null && eObject.eContainer().eResource() == + * null) { addToResource((InternalEObject)eObject.eContainer(), false); } // + * if the econtainer is already part of this resource then just attach if + * (!forceAddToContents && eObject.eContainer() != null && + * eObject.eContainer().eResource() == this) { attached(eObject); return; } + * // probably lazy load action of non-containment, already part of this + * just return if (!forceAddToContents && eObject.eResource() == this && + * !loadedEObjects.contains(eObject) && !removedEObjects.contains(eObject) + * && !newEObjects.contains(eObject)) { attached(eObject); return; } // + * already part of another resource if (!forceAddToContents && + * eObject.eResource() != null) { return; } + * + * final ContentsEList elist = (ContentsEList) super.getContents(); if + * (elist.contains(eObject)) { // can happen because of extends, + * polymorphism return; } // fill in the resource, do not use the normal add + * method because it // is possible that a child of a container is loaded, + * in that case // the normal add will remove the container of the object + * when the // resource is set in the child object, this issue can happen + * with // direct reads using queries. NotificationChain notification = + * null; if (loadStrategy.compareToIgnoreCase(ADD_TO_CONTENTS) == 0 || + * forceAddToContents) { notification = elist.basicAdd(eObject, null); } if + * (eObject.eResource() == null || (forceAddToContents && + * eObject.eResource() != this)) { setEResource(eObject, + * forceAddToContents); } // attached(eObject); if (sendNotificationsOnLoad + * && notification != null) { notification.dispatch(); } } + */ + + /** + * Sets the eresource by walking up the containment structure and finding + * the highest parent. There the resource is set.If the resource is already + * set nothing is done. + */ + public void setEResource(InternalEObject eobj, boolean force) { + if (eobj.eResource() != null && eobj.eResource() != this && !force) { + return; + } + + final Resource prevResource = eobj.eResource(); + + InternalEObject currentEObject = eobj; + final List<EObject> toMove = new ArrayList<EObject>(); + while (currentEObject.eContainer() != null + && !currentEObject.eContainmentFeature().isResolveProxies()) { + toMove.add(currentEObject); + currentEObject = (InternalEObject) currentEObject.eContainer(); + } + if (currentEObject.eResource() != this) { + currentEObject.eSetResource(this, null); + attached(currentEObject); + } + + // the original object can be loaded in the root of the old resource + // it is not removed automatically in this approach, do that now + for (EObject toMoveEObject : toMove) { + if (prevResource != null + && toMoveEObject.eResource() != prevResource) { + ((ContentsEList<?>) prevResource.getContents()).basicRemove( + toMoveEObject, null); + } + } + + } + + /** + * Overridden to also support persistence specific id instead of single emf + * id + */ + @Override + protected void detachedHelper(EObject eObject) { + + // support move to other resource + if (eObject.eResource() != this) { + removedEObjects.remove(eObject); + modifiedEObjects.remove(eObject); + loadedEObjects.remove(eObject); + loadedEObjectSet.remove(eObject); + newEObjects.remove(eObject); + newEObjectSet.remove(eObject); + return; + } + + removedEObject(eObject); + + Map<String, EObject> map = getIntrinsicIDToEObjectMap(); + if (map != null) { + String id = EcoreUtil.getID(eObject); + if (id != null) { + map.remove(id); + } else { + id = getURIFragment(eObject); + if (id != null) { + map.remove(id); + } + } + } + + if (isTrackingModification()) { + eObject.eAdapters().remove(modificationTrackingAdapter); + } + } + + /** Returns the added objects */ + public List<EObject> getNewEObjects() { + return newEObjects; + } + + public HashSet<EObject> getNewEObjectSet() { + return newEObjectSet; + } + + /** Return the new eobjects */ + public HashSet<EObject> getModifiedEObjects() { + return modifiedEObjects; + } + + /** Return the new eobjects */ + public List<EObject> getRemovedEObjects() { + return removedEObjects; + } + + /** + * Overridden to make it non resolving for not loaded elists and proxy + * eobjects + */ + @Override + public void setTrackingModification(boolean isTrackingModification) { + boolean oldIsTrackingModification = modificationTrackingAdapter != null; + + if (oldIsTrackingModification != isTrackingModification) { + if (isTrackingModification) { + + // note the global modification adapter is set after the for + // loop + // because in the for loop extra objects can be added to the + // resource + // these objects would get two adapters, once in the + // attachedHelper method + // and once here, btw can also prevent this by adding a contains + // check within + // for loop + final Adapter adapter = createModificationTrackingAdapter(); + for (Iterator<EObject> i = getNonResolvingAllContents(); i + .hasNext();) { + EObject eObject = i.next(); + assert (!eObject.eAdapters().contains(adapter)); + eObject.eAdapters().add(adapter); + } + modificationTrackingAdapter = adapter; + } else { + Adapter oldModificationTrackingAdapter = modificationTrackingAdapter; + + for (Iterator<EObject> i = getNonResolvingAllContents(); i + .hasNext();) { + EObject eObject = i.next(); + assert (eObject.eAdapters() + .contains(modificationTrackingAdapter)); + eObject.eAdapters().remove(oldModificationTrackingAdapter); + } + modificationTrackingAdapter = null; + } + } + + if (eNotificationRequired()) { + Notification notification = new NotificationImpl(Notification.SET, + oldIsTrackingModification, isTrackingModification) { + @Override + public Object getNotifier() { + return StoreResource.this; + } + + @Override + public int getFeatureID(Class<?> expectedClass) { + return RESOURCE__IS_TRACKING_MODIFICATION; + } + }; + eNotify(notification); + } + } + + /** + * Always returns a non-resolving iterator because resolving is defined on + * model level and the resource should adhere to that + */ + @SuppressWarnings("serial") + protected TreeIterator<EObject> getNonResolvingAllContents() { + return new AbstractTreeIterator<EObject>(this, false) { + @Override + public Iterator<EObject> getChildren(Object object) { + return object == StoreResource.this ? getLocalContents() + .basicIterator() : getNonResolvingContent( + (EObject) object).basicIterator(); + } + }; + } + + @Override + // Method is called at unload, only loaded content should be iterated + protected TreeIterator<EObject> getAllProperContents(List<EObject> contents) { + return getNonResolvingAllContents(); + } + + /** Returns a non-resolving contents elist for an eobject */ + protected EContentsEList<EObject> getNonResolvingContent(EObject eObject) { + return NonLoadingEContentsEList.create(eObject, false); + } + + /* + * (non-Javadoc) + * + * @seeorg.eclipse.emf.ecore.resource.impl.ResourceImpl# + * createModificationTrackingAdapter() + */ + @Override + protected Adapter createModificationTrackingAdapter() { + return new StoreModificationTrackingAdapter(); + } + + // Enable or disable notifications for the current content + protected void setAllowNotifications(boolean allow) { + for (Iterator<?> i = getNonResolvingAllContents(); i.hasNext();) { + EObject eObject = (EObject) i.next(); + eObject.eSetDeliver(allow); + } + } + + /** + * An adapter implementation for tracking resource modification, registers + * changed objects + */ + protected class StoreModificationTrackingAdapter extends AdapterImpl { + @Override + public void notifyChanged(Notification notification) { + switch (notification.getEventType()) { + case Notification.SET: + case Notification.UNSET: + case Notification.MOVE: { + if (!notification.isTouch()) { + setModified(true); + modifiedEObject((EObject) notification.getNotifier()); + } + break; + } + case Notification.ADD: + case Notification.REMOVE: + case Notification.ADD_MANY: + case Notification.REMOVE_MANY: { + setModified(true); + break; + } + } + } + } + + /** + * @return the sendNotificationsOnLoad + */ + public boolean isSendNotificationsOnLoad() { + return sendNotificationsOnLoad; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/StoreResourceException.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/StoreResourceException.java new file mode 100755 index 000000000..86bc3cfec --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/resource/StoreResourceException.java @@ -0,0 +1,42 @@ +/** + * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights + * reserved. This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal - Initial API and + * implementation </copyright> $Id: StoreResourceException.java,v 1.8 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.resource; + +import org.eclipse.emf.teneo.TeneoException; + +/** + * Is used to throw runtime store exceptions. This class offers automatic logging to commons + * logging. Note that this class extends RuntimeException, so no forced throws and catch statements. + * Although there are very differing views on this topic but it is our experience that to many + * checked exceptions only distract the programmer and have no added value. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.8 $ + */ + +public class StoreResourceException extends TeneoException { + /** + * Serializable id + */ + private static final long serialVersionUID = 7433341056815136417L; + + /** + * The constructor, logs the exception also + */ + public StoreResourceException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * The constructor, logs the exception also + */ + public StoreResourceException(String msg) { + super(msg); + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Attribute.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Attribute.java new file mode 100755 index 000000000..3f0f250b5 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Attribute.java @@ -0,0 +1,43 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: Attribute.java,v 1.6 2008/02/28 07:08:33 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.simpledom; + +/** + * This simple class is part of the replacement of dom4j. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.6 $ + */ +public class Attribute extends Node { + + /** Constructor */ + public Attribute() { + super(); + } + + /** Constructor */ + public Attribute(String name, String text) { + setName(name); + setText(text); + } + + /** emit me */ + String emitXML() { + return getName() + "=\"" + getText() + "\""; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Document.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Document.java new file mode 100755 index 000000000..dfedab415 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Document.java @@ -0,0 +1,64 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: Document.java,v 1.9 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.simpledom; + +/** + * This simple class is part of the replacement of dom4j. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.9 $ + */ + +public class Document { + + /** The doctype */ + private String docType = ""; + + /** Root element */ + private Element root = null; + + /** Set the docType */ + public void setDocType(String docType) { + this.docType = docType; + } + + /** Set the root */ + public Element setRoot(Element root) { + this.root = root; + return root; + } + + /** Return the root */ + public Element getRoot() { + return root; + } + + /** Emit ourselve as a XML string */ + public String emitXML() { + final StringBuffer result = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + if (docType.length() > 0) { + result.append(docType + "\n"); + } + // removed the following line because then comparison between different + // versions + // of hbm files is easier. + // result.append("<!--\tGenerated by Teneo on " + new Date() + " -->"); + result.append(root.emitXML()); + return result.toString(); + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/DocumentHelper.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/DocumentHelper.java new file mode 100755 index 000000000..6fbd99288 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/DocumentHelper.java @@ -0,0 +1,35 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: DocumentHelper.java,v 1.4 2008/02/28 07:08:33 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.simpledom; + +/** + * This simple class is part of the replacement of dom4j. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.4 $ + */ + +public class DocumentHelper { + + /** Create an element */ + public static Element createElement(String name) { + final Element element = new Element(); + element.setName(name); + return element; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Element.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Element.java new file mode 100755 index 000000000..0e4135e9b --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Element.java @@ -0,0 +1,230 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: Element.java,v 1.11 2010/07/15 07:46:44 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.simpledom; + +import java.util.ArrayList; +import java.util.List; + +/** + * This simple class is part of the replacement of dom4j. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.11 $ + */ + +public class Element extends Node { + + /** The children */ + private List<Element> children = new ArrayList<Element>(); + + /** The attributes */ + private List<Attribute> attributes = new ArrayList<Attribute>(); + + /** The parent element */ + private Element parent = null; + + /** Constructor */ + public Element() { + } + + /** Constructor */ + public Element(String name) { + setName(name); + } + + /** Method to add attribute */ + public Element addAttribute(String name, String text) { + // check if already present + for (Attribute attr : attributes) { + if (attr.getName().compareTo(name) == 0) { + attr.setText(text); + return this; + } + } + + final Attribute attr = new Attribute(name, text); + attributes.add(attr); + return this; + } + + /** + * Return the value of an attribute the attribute name, if not found then null is returned + */ + public String getAttributeValue(String name) { + for (Attribute attr : attributes) { + if (attr.getName().compareTo(name) == 0) { + return attr.getText(); + } + } + return null; + } + + /** Remove the attribute using a specific name */ + public void removeAttribute(String name) { + Attribute toRemove = null; + for (Attribute attr : attributes) { + if (attr.getName().compareTo(name) == 0) { + toRemove = attr; + } + } + if (toRemove != null) { + attributes.remove(toRemove); + } + } + + /** Method to add attribute */ + public Element addAttribute(Attribute attr) { + attributes.add(attr); + return this; + } + + /** Method to add Element */ + public Element addElement(String name) { + final Element element = new Element(); + element.setName(name); + children.add(element); + element.setParent(this); + return element; + } + + /** Method to add Element */ + public Element addElement(Element element) { + children.add(element); + element.setParent(this); + return element; + } + + /** + * @param text + * the text to set + */ + public void addText(String text) { + setText(getText() + text); + } + + /** Add */ + public Element add(Element element) { + children.add(element); + element.setParent(this); + return element; + } + + /** Add */ + public Element add(int index, Element element) { + children.add(index, element); + element.setParent(this); + return element; + } + + /** Find a child element */ + public Element element(String name) { + for (Element elem : children) { + if (elem.getName().compareTo(name) == 0) { + return elem; + } + } + return null; + } + + /** The indexof */ + public int indexOf(Element element) { + return children.indexOf(element); + } + + /** The remove */ + public boolean remove(Element element) { + return children.remove(element); + } + + /** + * @return the parent + */ + public Element getParent() { + return parent; + } + + /** + * @param parent + * the parent to set + */ + public void setParent(Element parent) { + this.parent = parent; + } + + /** Emit ourselves */ + String emitXML() { + final StringBuffer result = new StringBuffer("\n"); + int level = getLevel(); + for (int i = 0; i < level; i++) { + result.append("\t"); + } + + result.append("<" + getName()); + for (int i = 0; i < attributes.size(); i++) { + result.append(" "); + final Attribute attr = attributes.get(i); + result.append(attr.emitXML()); + } + if (children.size() == 0 && (getText() == null || getText().length() == 0)) { + result.append("/>"); + return result.toString(); + } + result.append(">"); + if (children.size() == 0) { + result.append(getText()); + result.append("</" + getName() + ">"); + return result.toString(); + } + + for (int i = 0; i < children.size(); i++) { + final Element element = children.get(i); + result.append(element.emitXML()); + } + result.append("\n"); + for (int i = 0; i < level; i++) { + result.append("\t"); + } + result.append("</" + getName() + ">"); + return result.toString(); + } + + /* Return the level */ + int getLevel() { + if (getParent() != null) { + return 1 + getParent().getLevel(); + } + return 0; + } + + /** Return the children */ + public List<Element> getChildren() { + return children; + } + + /** Clone */ + @Override + public Object clone() { + final Element element = new Element(); + element.setName(getName()); + element.setText(getText()); + element.children.addAll(children); + element.attributes.addAll(attributes); + element.setParent(getParent()); + return element; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Node.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Node.java new file mode 100755 index 000000000..d58bfb6d6 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/simpledom/Node.java @@ -0,0 +1,64 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: Node.java,v 1.6 2008/02/28 07:08:33 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.simpledom; + +/** + * This simple class is part of the replacement of dom4j. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.6 $ + */ + +public abstract class Node { + + /** The element name */ + private String name; + + /** The content, only text is supported */ + private String text = ""; + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the text + */ + public String getText() { + return text; + } + + /** + * @param text + * the text to set + */ + public void setText(String text) { + this.text = text; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/type/FeatureMapEntry.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/type/FeatureMapEntry.java new file mode 100755 index 000000000..b55b19acb --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/type/FeatureMapEntry.java @@ -0,0 +1,467 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: FeatureMapEntry.java,v 1.10 2010/02/04 11:03:02 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.type; + +import java.io.IOException; +import java.io.Serializable; + +import org.eclipse.emf.common.notify.NotificationChain; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.ecore.util.FeatureMap; +import org.eclipse.emf.teneo.EContainerRepairControl; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * Is used to replace the EMF feature map entry with an entry which can be handled by the or layer. + * + * The FeatureMap.Entry.Internal methods are handled through a delegate. Based on the efeature the + * correct delegate is created. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.10 $ + */ + +public abstract class FeatureMapEntry implements FeatureMap.Entry.Internal, Serializable { + private static final long serialVersionUID = 1L; + + /** The structural feature which defines which element this is */ + private EStructuralFeature eStructuralFeature; + + /** Path to the efeature for serialization support */ + private String eFeaturePath; + + /** And its value */ + private Object value; + + /** Keeps track if the class was initialized */ + private boolean initialized = false; + + /** The delegate which implements the inverse action */ + private InverseAction inverseAction; + + /** + * The featuremap to which we are connected. Is used to determine if entries have been added to + * another featuremap. This happens in copy actions. + */ + private FeatureMap.Internal owningMap; + + /** + * Constructor called by the storage layer, fields need to be set by calls to subclass + */ + public FeatureMapEntry() { + } + + /** + * Constructor called by the storage layer, fields need to be set by calls to subclass + */ + public FeatureMapEntry(EStructuralFeature feature, Object val) { + eStructuralFeature = feature; + value = val; + initialized = true; + initializeSpecificImplementation(); + setInverseAction(); + } + + /** Takes care of serializing the efeature */ + private void writeObject(java.io.ObjectOutputStream out) throws IOException { + eFeaturePath = StoreUtil.structuralFeatureToString(eStructuralFeature); + eStructuralFeature = null; + out.defaultWriteObject(); + } + + /** Takes care of deserializing the efeature */ + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + eStructuralFeature = StoreUtil.stringToStructureFeature(eFeaturePath); + } + + /** Set the inverseaction delegate, must be called after the efeature is set */ + private void setInverseAction() { + if (eStructuralFeature instanceof EReference) { + final EReference eref = (EReference) eStructuralFeature; + if (eref.getEOpposite() != null) { + inverseAction = new BidirectionalInverseAction(); + } else if (eref.isContainment()) { + inverseAction = new ContainmentInverseAction(); + } else { + inverseAction = new InverseAction(); + } + } else { + inverseAction = new InverseAction(); + } + } + + /** Sets the featuremap, is done when an entry is added to the featuremap */ + public void setFeatureMap(FeatureMap.Internal featureMap) { + owningMap = featureMap; + + /* + * if (value != null && value instanceof InternalEObject && eStructuralFeature instanceof + * EReference && ((EReference)eStructuralFeature).isContainment()) { ((InternalEObject) + * value).eSetResource((Resource.Internal)owningMap.getEObject ().eResource(), null); } + */ + } + + /** Unsets the featuremap, is done when an entry is removed */ + public void unsetFeatureMap() { + owningMap = null; + } + + /** Is true if this featureMap already belongs to a Map */ + public boolean isFeatureMapSet() { + return owningMap != null; + } + + /** Is true if this featureMap already belongs to the passed map */ + public boolean belongsToFeatureMap(FeatureMap.Internal fm) { + return owningMap == fm; // object equality! + } + + /** Set the value from a previous entry */ + public void setEntry(FeatureMap.Entry entry) { + eStructuralFeature = entry.getEStructuralFeature(); + value = entry.getValue(); + initialized = true; // needs to be set before the call to the subclass, + // otherwise infinite + // looping + initializeSpecificImplementation(); + setInverseAction(); + } + + /** Initializes this class from the values in the subclass */ + public void initialize() { + eStructuralFeature = retrieveStructuralFeature(getStructuralFeatureDBID()); + value = getValueFromSpecificImplementation(eStructuralFeature); + initialized = true; + setInverseAction(); + } + + /** + * Needs to be implemented by the subclass, returns the value based on one of the fields set + * through the db + */ + protected abstract Object getValueFromSpecificImplementation(EStructuralFeature eFeature); + + /** + * Needs to be implemented by the subclass, returns the database id of the structural feature + */ + protected abstract String getStructuralFeatureDBID(); + + /** + * Is called by the super class to notify the subclass that it needs to set its fields based on + * the structural feature + */ + protected abstract void initializeSpecificImplementation(); + + /** + * Method which needs to be called by the subclass to set the superclass members + */ + public void setFields(EStructuralFeature structuralFeature, Object structuralValue) { + eStructuralFeature = structuralFeature; + value = structuralValue; + initialized = true; // do this before the call to the subclass + // initialize the subclass so that the fields are stored in the db + initializeSpecificImplementation(); + setInverseAction(); + } + + /** Returns structural feature */ + public EStructuralFeature getEStructuralFeature() { + if (!initialized) { + initialize(); + } + + return eStructuralFeature; + } + + /** Returns the value */ + public Object getValue() { + if (!initialized) { + initialize(); + } + + return value; + } + + /** + * Returns the string which is used to store the unique identification of this structuralfeature + * in the db + */ + protected String createStructuralFeatureDBID() { + return StoreUtil.structuralFeatureToString(getEStructuralFeature()); + } + + /** Gets a structuralfeature on the basis of the passed id */ + protected EStructuralFeature retrieveStructuralFeature(String dbid) { + return StoreUtil.stringToStructureFeature(dbid); + } + + /** + * Checks if a certain feature has a certain name or that its group (if present) has this name, + * in which case it is also set to true. + */ + protected boolean featureForField(String name) { + if (eStructuralFeature.getName().compareTo(name) == 0) { + return true; + } + + // check the group feature + final EStructuralFeature groupFeature = ExtendedMetaData.INSTANCE.getGroup(eStructuralFeature); + if (groupFeature != null && groupFeature.getName().compareTo(name) == 0) { + return true; + } + + final EStructuralFeature affiliatedFeature = ExtendedMetaData.INSTANCE.getAffiliation(eStructuralFeature); + if (affiliatedFeature != null && affiliatedFeature.getName().compareTo(name) == 0) { + return true; + } + return false; + } + + /** + * Sets the container property of the value if the value is an EObject and the feature is a + * containment feature. + */ + public void setContainer(InternalEObject owner) { + if (!initialized) { + initialize(); + } + + if (value != null && value instanceof InternalEObject && eStructuralFeature instanceof EReference && + ((EReference) eStructuralFeature).isContainment()) { + EContainerRepairControl.setContainer(owner, (InternalEObject) value, eStructuralFeature); + } + } + + /** Code copied from FeatureMapUtil.EntryImpl */ + @Override + public boolean equals(Object that) { + if (!initialized) { + initialize(); + } + + if (this == that) { + return true; + } else if (!(that instanceof FeatureMap.Entry)) { + return false; + } else { + FeatureMap.Entry entry = (FeatureMap.Entry) that; + return entry.getEStructuralFeature() == eStructuralFeature && + (value == null ? entry.getValue() == null : value.equals(entry.getValue())); + } + } + + /** Code copied from FeatureMapUtil.EntryImpl */ + @Override + public int hashCode() { + /* + * Used to create a hashcode which maps all instances of one class to the same hashcode Is + * required because the normal hashcode method (see commented out part below) resulted in + * null-pointer exceptions in hibernate because the content of the entry was used for + * determining the hashcode while the object was not initialized from the db + */ + return this.getClass().hashCode(); + /* + * if (!initialized) initialize(); + * + * return eStructuralFeature.hashCode() ^ (value == null ? 0 : value.hashCode()); + */ + } + + /** Code copied from FeatureMapUtil.EntryImpl */ + @Override + public String toString() { + if (!initialized) { + initialize(); + } + + String prefix = eStructuralFeature.getEContainingClass().getEPackage().getNsPrefix(); + eStructuralFeature.getName(); + return (prefix != null && prefix.length() != 0 ? prefix + ":" + eStructuralFeature.getName() + : eStructuralFeature.getName()) + + "=" + value; + } + + /** Create copy with same feature and different value */ + public Internal createEntry(InternalEObject value) { + return createEntry((Object) value); + } + + /** Create copy with same feature and different value */ + public abstract Internal createEntry(Object value); + + /** Do inverse action */ + public NotificationChain inverseAdd(InternalEObject owner, int featureID, NotificationChain notifications) { + return inverseAction.inverseAdd(owner, featureID, notifications); + } + + /** Do inverse action */ + public NotificationChain inverseAdd(InternalEObject owner, Object otherEnd, int featureID, + NotificationChain notifications) { + return inverseAction.inverseAdd(owner, otherEnd, featureID, notifications); + } + + /** Do inverse action */ + public NotificationChain inverseRemove(InternalEObject owner, int featureID, NotificationChain notifications) { + return inverseAction.inverseRemove(owner, featureID, notifications); + } + + /** Do inverse action */ + public NotificationChain inverseRemove(InternalEObject owner, Object otherEnd, int featureID, + NotificationChain notifications) { + return inverseAction.inverseRemove(owner, otherEnd, featureID, notifications); + } + + /** Validate type of object against the type of the efeature */ + public void validate(Object value) { + if (value != null && !eStructuralFeature.getEType().isInstance(value)) { + String valueClass = + value instanceof EObject ? ((EObject) value).eClass().getName() : value.getClass().getName(); + throw new ClassCastException("The feature '" + eStructuralFeature.getName() + "'s type '" + + eStructuralFeature.getEType().getName() + "' does not permit a value of type '" + valueClass + "'"); + } + } + + /** Internal class to handle inverse actions */ + private class InverseAction { + + /** Handles inverse action, differs on the basis of the feature type */ + public NotificationChain inverseAdd(InternalEObject owner, int featureID, NotificationChain notifications) { + return inverseAdd(owner, value, featureID, notifications); + } + + /** Handles inverse action, differs on the basis of the feature type */ + public NotificationChain inverseRemove(InternalEObject owner, int featureID, NotificationChain notifications) { + return inverseRemove(owner, value, featureID, notifications); + } + + /** Handles inverse action, differs on the basis of the feature type */ + public NotificationChain inverseAdd(InternalEObject owner, Object otherEnd, int featureID, + NotificationChain notifications) { + return notifications; + } + + /** Handles inverse action, differs on the basis of the feature type */ + public NotificationChain inverseRemove(InternalEObject owner, Object otherEnd, int featureID, + NotificationChain notifications) { + return notifications; + } +// +// /** +// * validate the type of the value with the type expected by the efeature +// */ +// public void validate(Object value) { +// if (value != null && !eStructuralFeature.getEType().isInstance(value)) { +// String valueClass = +// value instanceof EObject ? ((EObject) value).eClass().getName() : value.getClass().getName(); +// throw new ClassCastException("The feature '" + eStructuralFeature.getName() + "'s type '" + +// eStructuralFeature.getEType().getName() + "' does not permit a value of type '" + valueClass + +// "'"); +// } +// } + } + + /** Containment Inverse Action */ + private class ContainmentInverseAction extends InverseAction { + + /** Handles inverse action, differs on the basis of the feature type */ + @Override + public NotificationChain inverseAdd(InternalEObject owner, Object otherEnd, int featureID, + NotificationChain notifications) { + return inverseAdd(owner, (InternalEObject) value, featureID, notifications); + } + + /** Handles inverse action, differs on the basis of the feature type */ + @Override + public NotificationChain inverseRemove(InternalEObject owner, Object otherEnd, int featureID, + NotificationChain notifications) { + return inverseRemove(owner, (InternalEObject) value, featureID, notifications); + } + + /** Does inverse action on other end */ + private NotificationChain inverseAdd(InternalEObject owner, InternalEObject otherEnd, int featureID, + NotificationChain notifications) { + if (otherEnd != null) { + int containmentFeatureID = owner.eClass().getFeatureID(eStructuralFeature); + notifications = + otherEnd.eInverseAdd(owner, InternalEObject.EOPPOSITE_FEATURE_BASE - + (containmentFeatureID == -1 ? featureID : containmentFeatureID), null, notifications); + } + + return notifications; + } + + /** Does inverse action on other end */ + private NotificationChain inverseRemove(InternalEObject owner, InternalEObject otherEnd, int featureID, + NotificationChain notifications) { + if (otherEnd != null) { + int containmentFeatureID = owner.eClass().getFeatureID(eStructuralFeature); + notifications = + otherEnd.eInverseRemove(owner, InternalEObject.EOPPOSITE_FEATURE_BASE - + (containmentFeatureID == -1 ? featureID : containmentFeatureID), null, notifications); + } + + return notifications; + } + } + + /** Bidirectional feature value */ + private class BidirectionalInverseAction extends InverseAction { + + /** Handles inverse action, differs on the basis of the feature type */ + @Override + public NotificationChain inverseAdd(InternalEObject owner, Object otherEnd, int featureID, + NotificationChain notifications) { + return inverseAdd(owner, (InternalEObject) value, featureID, notifications); + } + + /** Handles inverse action, differs on the basis of the feature type */ + @Override + public NotificationChain inverseRemove(InternalEObject owner, Object otherEnd, int featureID, + NotificationChain notifications) { + return inverseRemove(owner, (InternalEObject) value, featureID, notifications); + } + + /** Does inverse action on other end */ + private final NotificationChain inverseAdd(InternalEObject owner, InternalEObject otherEnd, int featureID, + NotificationChain notifications) { + if (otherEnd != null) { + notifications = + otherEnd.eInverseAdd(owner, otherEnd.eClass().getFeatureID( + ((EReference) eStructuralFeature).getEOpposite()), null, notifications); + } + + return notifications; + } + + /** Does inverse action on other end */ + private final NotificationChain inverseRemove(InternalEObject owner, InternalEObject otherEnd, int featureID, + NotificationChain notifications) { + if (otherEnd != null) { + notifications = + otherEnd.eInverseRemove(owner, otherEnd.eClass().getFeatureID( + ((EReference) eStructuralFeature).getEOpposite()), null, notifications); + } + return notifications; + } + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/type/MixedFeatureMapEntry.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/type/MixedFeatureMapEntry.java new file mode 100755 index 000000000..b35b99eb8 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/type/MixedFeatureMapEntry.java @@ -0,0 +1,80 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: MixedFeatureMapEntry.java,v 1.7 2009/03/30 07:53:05 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.type; + +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.teneo.Constants; + +/** + * Is a specific EMF feature map for handling mixed content. Mixed content is content which consists + * of normal nodes and other content. The other content supported here is text, cdata and comment. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.7 $ + */ + +public abstract class MixedFeatureMapEntry extends FeatureMapEntry { + private static final long serialVersionUID = 1L; + + /** Constant used to encode the TEXT feature in the db. */ + static final String TEXT_FEATURE_DBID = "TEXT"; + + /** Constant used to encode the CDATA feature in the db. */ + static final String CDATA_FEATURE_DBID = "CDATA"; + + /** Constant used to encode the COMMENT feature in the db. */ + static final String COMMENT_FEATURE_DBID = "COMMENT"; + + /** Overridden to encode the TEXT, CDATA or COMMENT structural features */ + @Override + protected String createStructuralFeatureDBID() { + final EStructuralFeature structuralFeature = getEStructuralFeature(); + + if (structuralFeature == Constants.TEXT) { + return TEXT_FEATURE_DBID; + } else if (structuralFeature == Constants.CDATA) { + return CDATA_FEATURE_DBID; + } else if (structuralFeature == Constants.COMMENT) { + return COMMENT_FEATURE_DBID; + } + return super.createStructuralFeatureDBID(); + } + + /** Gets a structuralfeature on the basis of the passed id */ + @Override + protected EStructuralFeature retrieveStructuralFeature(String dbid) { + if (TEXT_FEATURE_DBID.compareTo(dbid) == 0) { + return Constants.TEXT; + } + if (CDATA_FEATURE_DBID.compareTo(dbid) == 0) { + return Constants.CDATA; + } + if (COMMENT_FEATURE_DBID.compareTo(dbid) == 0) { + return Constants.COMMENT; + } + + return super.retrieveStructuralFeature(dbid); + } + + /** Returns true if the feature is a TEXT, CDATA or COMMENT */ + protected boolean isMixedFeature() { + final EStructuralFeature structuralFeature = getEStructuralFeature(); + return structuralFeature == Constants.TEXT || structuralFeature == Constants.CDATA || + structuralFeature == Constants.COMMENT; + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/type/PersistentStoreAdapter.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/type/PersistentStoreAdapter.java new file mode 100644 index 000000000..2c8525cc3 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/type/PersistentStoreAdapter.java @@ -0,0 +1,280 @@ +/** + * <copyright> + * + * Copyright (c) 2009 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: PersistentStoreAdapter.java,v 1.15 2011/02/21 04:43:18 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.type; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.teneo.util.StoreUtil; + +/** + * Keeps a list of PersistentLists by efeature. Is used when a new object is + * persisted and the OR-layer wants to replace the list implementation. + * + * This adapter keeps the PersistentList and ensures that any updates in the + * original list are also done in the persistent store. + * + * This adapter only operates in case the target object is not read from the + * persistent store but is persisted there for the first time. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.15 $ + */ + +public class PersistentStoreAdapter implements Adapter { + private static final long serialVersionUID = 1L; + + private Notifier target; + private boolean targetCreatedByORM; + + private Map<EStructuralFeature, Object> storeCollections = new HashMap<EStructuralFeature, Object>(); + + private Map<String, Object> syntheticProperties = new HashMap<String, Object>(); + + public void addStoreCollection(EStructuralFeature eFeature, + Object storeCollection) { + // note that when refresh is called on a persisted object + // then this call replaces the current collection + storeCollections.put(eFeature, storeCollection); + } + + public Object getStoreCollection(EStructuralFeature eFeature) { + return storeCollections.get(eFeature); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.emf.common.notify.Adapter#getTarget() + */ + public Notifier getTarget() { + return target; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.common.notify.Adapter#isAdapterForType(java.lang.Object) + */ + public boolean isAdapterForType(Object type) { + return false; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.common.notify.Adapter#notifyChanged(org.eclipse.emf.common + * .notify.Notification) + */ + @SuppressWarnings("unchecked") + public void notifyChanged(Notification notification) { + final EStructuralFeature eFeature = (EStructuralFeature) notification + .getFeature(); + + final Object collectionObject = storeCollections.get(eFeature); + if (collectionObject == null) { + return; + } + + final List<Object> list = (collectionObject instanceof List ? (List<Object>) collectionObject + : null); + final Map<Object, Object> map = (collectionObject instanceof Map<?, ?> ? (Map<Object, Object>) collectionObject + : null); + + final boolean isEReference = eFeature instanceof EReference; + int changedPosition = -1; + switch (notification.getEventType()) { + case Notification.ADD: + if (list != null) { + if (notification.getPosition() != Notification.NO_INDEX) { + changedPosition = notification.getPosition(); + list.add(notification.getPosition(), + replaceValue(notification.getNewValue(), eFeature)); + } else { + changedPosition = list.size(); + list.add(replaceValue(notification.getNewValue(), eFeature)); + } + } + if (map != null) { + final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) notification + .getNewValue(); + map.put(entry.getKey(), entry.getValue()); + } + break; + case Notification.ADD_MANY: + if (list != null) { + if (notification.getPosition() != Notification.NO_INDEX) { + changedPosition = notification.getPosition(); + list.addAll( + notification.getPosition(), + replaceValues( + (List<Object>) notification.getNewValue(), + eFeature)); + + } else { + changedPosition = list.size(); + list.addAll(replaceValues( + (List<Object>) notification.getNewValue(), eFeature)); + } + } + if (map != null) { + for (Object o : (List<?>) notification.getNewValue()) { + final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o; + map.put(entry.getKey(), entry.getValue()); + } + } + break; + case Notification.REMOVE: + if (list != null) { + final Object removed; + if (notification.getPosition() != Notification.NO_INDEX) { + removed = list.remove(notification.getPosition()); + } else { + removed = replaceValue(notification.getOldValue(), eFeature); + list.remove(removed); + } + // recompute them all + changedPosition = 0; + if (isEReference) { + StoreUtil.resetSyntheticListInfo(eFeature, removed); + } + } + if (map != null) { + final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) notification + .getOldValue(); + map.remove(entry.getKey()); + } + break; + case Notification.REMOVE_MANY: + if (list != null) { + final List<?> removed = replaceValues( + (List<Object>) notification.getOldValue(), eFeature); + list.removeAll(removed); + if (isEReference) { + for (Object removedObject : removed) { + StoreUtil.resetSyntheticListInfo(eFeature, + removedObject); + } + } + changedPosition = 0; + } + if (map != null) { + for (Object o : (List<?>) notification.getOldValue()) { + final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o; + map.remove(entry.getKey()); + } + } + break; + case Notification.MOVE: + if (list != null) { + final int oldPosition = (Integer) notification.getOldValue(); + final int newPosition = notification.getPosition(); + final Object o = list.remove(oldPosition); + if (o != notification.getNewValue()) { + throw new IllegalStateException( + "Persistent list and EList are out of sync"); + } + list.add(newPosition, o); + if (newPosition < oldPosition) { + changedPosition = newPosition; + } else { + changedPosition = oldPosition; + } + } + break; + case Notification.SET: + if (list != null) { + final int position = notification.getPosition(); + Object removed = list.set(position, + replaceValue(notification.getNewValue(), eFeature)); + changedPosition = position; + if (isEReference) { + StoreUtil.resetSyntheticListInfo(eFeature, removed); + } + } + break; + } + + if (changedPosition > -1 && isEReference) { + int newIndex = changedPosition; + for (Object element : list.subList(changedPosition, list.size())) { + StoreUtil.setSyntheticListOwner(eFeature, element, + notification.getNotifier()); + StoreUtil.setSyntheticListIndex(eFeature, element, newIndex++); + } + } + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.emf.common.notify.Adapter#setTarget(org.eclipse.emf.common + * .notify.Notifier) + */ + public void setTarget(Notifier newTarget) { + target = newTarget; + } + + /** + * @return the targetCreatedByORM + */ + public boolean isTargetCreatedByORM() { + return targetCreatedByORM; + } + + /** + * @param targetCreatedByORM + * the targetCreatedByORM to set + */ + public void setTargetCreatedByORM(boolean targetCreatedByORM) { + this.targetCreatedByORM = targetCreatedByORM; + } + + /** + * @return the storeCollections + */ + public Map<EStructuralFeature, Object> getStoreCollections() { + return storeCollections; + } + + protected Object replaceValue(Object value, EStructuralFeature eFeature) { + return value; + } + + protected List<Object> replaceValues(List<Object> values, + EStructuralFeature eFeature) { + return values; + } + + public Object getSyntheticProperty(String property) { + return syntheticProperties.get(property); + } + + public void setSyntheticProperty(String property, Object value) { + syntheticProperties.put(property, value); + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/AssertUtil.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/AssertUtil.java new file mode 100755 index 000000000..0fc384050 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/AssertUtil.java @@ -0,0 +1,100 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal - Initial API and implementation + * + * </copyright> + * + * $Id: AssertUtil.java,v 1.7 2009/03/30 07:53:04 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.util; + +import org.eclipse.emf.ecore.EObject; + +/** + * Contains utility methods for assertions + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.7 $ + */ + +public class AssertUtil { + /** AssertTrue with message */ + public static void assertTrue(String msg, boolean value) { + if (!value) { + throw new AssertionError(msg); + } + } + + /** Same resource check */ + public static void assertResource(EObject obj1, EObject obj2) { + if (obj1.eResource() == null && ((EObject) obj2).eResource() == null) return; + if (obj1.eResource() != ((EObject) obj2).eResource()) { + throw new AssertionError("The resources are different: " + obj1.getClass().getName() + "/" + + obj2.getClass().getName()); + } + } + + /** Checks for correct container relations */ + public static void assertContainer(EObject parent, EObject child) { + if (parent != child.eContainer()) { + throw new AssertionError("The child's container is incorrect! parent/child: " + + parent.getClass().getName() + "/" + child.getClass().getName()); + } + } + + /** Asserts that the passed entry is null */ + public static void assertIsNull(Object obj) { + if (obj != null) { + throw new AssertionError("Passed object: " + obj.getClass().getName() + + " is not null while this was expected"); + } + } + + /** + * Checks if the passed object is of the class specified, null values are ignored + */ + public static void assertInstanceOf(Object obj, Class<?> expClass) { + if (obj == null) return; + if (!(expClass.isAssignableFrom(obj.getClass()))) { + throw new AssertionError("Expected class: " + expClass.getName() + " but object has class: " + + obj.getClass().getName()); + } + } + + /** + * Checks if the passed object is of the class specified, null values throw an exception + */ + public static void assertInstanceOfNotNull(Object obj, Class<?> expClass) { + if (obj == null) { + throw new AssertionError("Checking instanceof but object is null, expecting class: " + expClass.getName()); + } + if (!(expClass.isAssignableFrom(obj.getClass()))) { + throw new AssertionError("Expected class: " + expClass.getName() + " but object has class: " + + obj.getClass().getName()); + } + } + + /** Checks object memory equality */ + public static void assertSameObject(Object obj1, Object obj2) { + if (obj1 != obj2) { + throw new AssertionError("Objects are not the same"); + } + } + + /** Checks object memory inequality */ + public static void assertNotSameObject(Object obj1, Object obj2) { + if (obj1 == obj2) { + throw new AssertionError("Objects are the same"); + } + } + +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/EcoreDataTypes.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/EcoreDataTypes.java new file mode 100755 index 000000000..69e5ae6d2 --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/EcoreDataTypes.java @@ -0,0 +1,435 @@ +/** + * <copyright> + * + * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Taal + * Davide Marchignoli + * Brian Vetter (bugzilla 175909) + * Alexandros Karypidis (bugzilla 207799) + * </copyright> + * + * $Id: EcoreDataTypes.java,v 1.17 2011/03/17 09:21:31 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.eclipse.emf.ecore.EAnnotation; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.ecore.EModelElement; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.ecore.xml.type.XMLTypePackage; +import org.eclipse.emf.teneo.PersistenceOptions; +import org.eclipse.emf.teneo.TeneoException; +import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; + +/** + * Utility class to classify Ecore datatypes. + * + * @author <a href="mailto:marchign at elver.org">Davide Marchignoli</a> + */ +public class EcoreDataTypes { + + // The xml types + private static XMLTypePackage xmlTypePackage = XMLTypePackage.eINSTANCE; + private static EDataType xmlDateEDataType = xmlTypePackage.getDate(); + private static EDataType xmlDateTimeEDataType = xmlTypePackage + .getDateTime(); + private static EDataType xmlTimeEDataType = xmlTypePackage.getTime(); + + // The source of the annotations of extended metadata used by emf + private static final String ANNOTATION_SOURCE_METADATA = "http:///org/eclipse/emf/ecore/util/ExtendedMetaData"; + + // XML datatype factory instance + private final DatatypeFactory dataTypeFactory; + + private static final List<EDataType> PRIMITIVES_ETYPES_LIST = Collections + .unmodifiableList(Arrays.asList(new EDataType[] { + EcorePackage.eINSTANCE.getEBoolean(), + EcorePackage.eINSTANCE.getEByte(), + EcorePackage.eINSTANCE.getEChar(), + EcorePackage.eINSTANCE.getEDouble(), + EcorePackage.eINSTANCE.getEFloat(), + EcorePackage.eINSTANCE.getEInt(), + EcorePackage.eINSTANCE.getELong(), + EcorePackage.eINSTANCE.getEShort(), })); + + private static final List<Class<?>> PRIMITIVE_OBJECT_TYPE_LIST = Collections + .unmodifiableList(Arrays.asList(new Class<?>[] { + java.lang.Boolean.class, java.lang.Byte.class, + java.lang.Double.class, java.lang.Float.class, + java.lang.Integer.class, java.lang.Long.class, + java.math.BigDecimal.class, java.math.BigInteger.class })); + + private static final List<EDataType> WRAPPERS_ETYPES_LIST = Collections + .unmodifiableList(Arrays.asList(new EDataType[] { + EcorePackage.eINSTANCE.getEBooleanObject(), + EcorePackage.eINSTANCE.getEByteObject(), + EcorePackage.eINSTANCE.getECharacterObject(), + EcorePackage.eINSTANCE.getEDoubleObject(), + EcorePackage.eINSTANCE.getEFloatObject(), + EcorePackage.eINSTANCE.getEIntegerObject(), + EcorePackage.eINSTANCE.getELongObject(), + EcorePackage.eINSTANCE.getEShortObject(), })); + + public static EcoreDataTypes INSTANCE = new EcoreDataTypes(); + + private EcoreDataTypes() { + try { + dataTypeFactory = DatatypeFactory.newInstance(); + } catch (DatatypeConfigurationException e) { + throw new TeneoException("Exception ", e); + } + } + + /** Returns the type name of a many attribute */ + public String getTargetTypeName(PAnnotatedEAttribute aAttribute) { + final EAttribute eAttribute = aAttribute.getModelEAttribute(); + // check on equality on object.class is used for listunion simpleunions + final Class<?> instanceClass = eAttribute.getEAttributeType() + .getInstanceClass(); + if (instanceClass != null && !Object.class.equals(instanceClass) + && !List.class.equals(instanceClass)) { + if (instanceClass.isArray()) { + // get rid of the [] at the end + return eAttribute + .getEType() + .getInstanceClassName() + .substring( + 0, + eAttribute.getEType().getInstanceClassName() + .length() - 2); + } + return instanceClass.getName(); + } + // the type is hidden somewhere deep get it + // the edatatype is the java.util.list + // it has an itemType which is the name of the element edatatype + // which contains the instanceclass + // takes also into account inheritance between datatypes + // NOTE the otm.targetentity can consist of a comma delimited list + // of target + // entities this is required for listunion types but is not + // according to the ejb3 spec! + ArrayList<EClassifier> eclassifiers = getItemTypes((EDataType) eAttribute + .getEType()); + if (eclassifiers.size() > 0) { + StringBuffer result = new StringBuffer(); + for (int i = 0; i < eclassifiers.size(); i++) { + final EClassifier eclassifier = eclassifiers.get(i); + if (i > 0) { + result.append(","); + } + result.append(eclassifier.getInstanceClassName()); + } + return result.toString(); + } + return Object.class.getName(); + } + + /** Walks up a edatatype inheritance structure to find the itemType */ + public ArrayList<EClassifier> getItemTypes(EDataType eDataType) { + final ArrayList<EClassifier> result = new ArrayList<EClassifier>(); + if (eDataType == null) { + return result; + } + final String itemType = getEAnnotationValue(eDataType, + ANNOTATION_SOURCE_METADATA, "itemType"); + if (itemType != null) { + final EClassifier eClassifier = getEClassifier( + eDataType.getEPackage(), itemType); + if (eClassifier != null) { + result.add(eClassifier); + } + + return result; + } + + final String memberTypes = getEAnnotationValue(eDataType, + ANNOTATION_SOURCE_METADATA, "memberTypes"); + if (memberTypes != null) { + String[] mtypes = memberTypes.split(" "); + for (String element : mtypes) { + final EClassifier eclassifier = getEClassifier( + eDataType.getEPackage(), element); + if (eclassifier != null) { + result.addAll(getItemTypes((EDataType) eclassifier)); + } + } + return result; + } + + final String baseType = getEAnnotationValue(eDataType, + ANNOTATION_SOURCE_METADATA, "baseType"); + if (baseType != null) { + final EClassifier eClassifier = getEClassifier( + eDataType.getEPackage(), baseType); + if (eClassifier != null) { + final ArrayList<EClassifier> tmpResult = getItemTypes((EDataType) eClassifier); + if (tmpResult.size() > 0) { + result.addAll(tmpResult); + return result; + } + } + } + if (!Object.class.equals(eDataType.getInstanceClass())) { + result.add(eDataType); + } + return result; + } + + /** + * Returns the eclassifier using either the name of the eclassifier or the + * name element + */ + public EClassifier getEClassifier(EPackage epackage, String searchName) { + for (EClassifier eclassifier : epackage.getEClassifiers()) { + if (eclassifier.getName().compareTo(searchName) == 0) { + return eclassifier; + } + String nameAnnotation = getEAnnotationValue(eclassifier, + ANNOTATION_SOURCE_METADATA, "name"); + if (nameAnnotation != null + && searchName.compareTo(nameAnnotation) == 0) { + return eclassifier; + } + } + return null; + } + + /** Returns the value of an annotation with a certain key */ + public String getEAnnotationValue(EModelElement eModelElement, + String source, String key) { + final EAnnotation eAnnotation = eModelElement.getEAnnotation(source); + if (eAnnotation == null) { + return null; + } + return eAnnotation.getDetails().get(key); + } + + // TODO: Make all utility methods static. + + /** Return a XMLGregorianCalendar on the basis of the date */ + public XMLGregorianCalendar getXMLGregorianCalendar(Date date) { + final XMLGregorianCalendar gregCalendar = dataTypeFactory + .newXMLGregorianCalendar(); + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + gregCalendar.setYear(calendar.get(Calendar.YEAR)); + gregCalendar.setMonth(calendar.get(Calendar.MONTH) + 1); // note the + // correction + // with 1 + gregCalendar.setDay(calendar.get(Calendar.DAY_OF_MONTH)); + return gregCalendar; + } + + /** Return a XMLGregorianCalendar on datetime level (milliseconds) */ + public XMLGregorianCalendar getXMLGregorianCalendarDateTime(Date date) { + final XMLGregorianCalendar gregCalendar = dataTypeFactory + .newXMLGregorianCalendar(); + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + gregCalendar.setYear(calendar.get(Calendar.YEAR)); + gregCalendar.setMonth(calendar.get(Calendar.MONTH) + 1); // correct with + // 1 on + // purpose + gregCalendar.setDay(calendar.get(Calendar.DAY_OF_MONTH)); + gregCalendar.setHour(calendar.get(Calendar.HOUR_OF_DAY)); + gregCalendar.setMinute(calendar.get(Calendar.MINUTE)); + gregCalendar.setSecond(calendar.get(Calendar.SECOND)); + gregCalendar.setMillisecond(calendar.get(Calendar.MILLISECOND)); + return gregCalendar; + } + + /** + * @return Returns an immutable list of the Ecore EDataType for java + * primitives. + */ + public List<EDataType> getEPrimitives() { + return PRIMITIVES_ETYPES_LIST; + } + + /** + * @return Returns true if and only if the the given eDataType is the Ecore + * EDataType for a primitive type. + */ + public boolean isEPrimitive(EDataType eDataType) { + return eDataType != null && isPrimitive(eDataType.getInstanceClass()); + } + + public boolean isPrimitive(Class<?> clz) { + if (clz == null) { + return false; + } + return clz.isPrimitive() || PRIMITIVE_OBJECT_TYPE_LIST.contains(clz); + } + + /** + * @return Returns an immutable list of the Ecore EDataType for java + * primitive wrapper classes. + */ + public List<EDataType> getEWrappers() { + return WRAPPERS_ETYPES_LIST; + } + + /** + * @return Returns true if and only if the the given eDataType is the Ecore + * EDataType for a primitive wrapper class. + */ + public boolean isEWrapper(EDataType eDataType) { + return WRAPPERS_ETYPES_LIST.contains(eDataType); + } + + /** + * @return true if and only if the given dataType is a string datatype. + */ + public boolean isEString(EDataType eDataType) { + // should be eDataType == EString but does not work due to XML type + // implementations + return String.class == eDataType.getInstanceClass(); + } + + public boolean isEDuration(EDataType eDataType) { + String className = null; + if (eDataType.getInstanceClassName() != null) { + className = eDataType.getInstanceClassName(); + } else if (eDataType.getInstanceClass() != null) { + className = eDataType.getInstanceClass().getName(); + } else { + return false; + } + return className.compareTo("javax.xml.datatype.Duration") == 0; + } + + /** + * - * + * + * @return true if and only if the given dataType is a date datatype. + */ + public boolean isEDate(EDataType eDataType, PersistenceOptions po) { + if (eDataType.equals(xmlDateEDataType)) { + return true; + } + /* + * There is some ambiguity around the Java Date class since it can also + * hold time - a conflict with the DateTime class + */ + Class<?> ic = eDataType.getInstanceClass(); + // do a string comparison to prevent another dependency for this teneo + // library. + if (eDataType.getInstanceClassName() != null + && eDataType.getInstanceClassName().compareTo( + po.getXSDDateClass()) == 0) { + return true; + } + return java.util.Date.class == ic || java.util.Calendar.class == ic + || java.sql.Date.class == ic; + } + + /** + * @return true if and only if the given dataType is a datetime/timestamp + * datatype. + */ + public boolean isETime(EDataType eDataType) { + if (eDataType.equals(xmlTimeEDataType)) { + return true; + } + /* + * the InstanceClass for date type can be "Object" for XSD types. I'm + * not sure about ecore itself so I have kept the original check against + * the java classes. + */ + Class<?> ic = eDataType.getInstanceClass(); + // already handled through the first if + // if (ic == Object.class) { + // // could be an XML date type + // return eDataType.equals(xmlDateTimeEDataType); + // } + return java.sql.Timestamp.class == ic || Date.class == ic; + } + + /** + * @return true if and only if the given dataType is a datetime/timestamp + * datatype. + */ + public boolean isEDateTime(EDataType eDataType) { + if (eDataType.equals(xmlDateTimeEDataType)) { + return true; + } + /* + * the InstanceClass for date type can be "Object" for XSD types. I'm + * not sure about ecore itself so I have kept the original check against + * the java classes. + */ + Class<?> ic = eDataType.getInstanceClass(); + // already handled through the first if + // if (ic == Object.class) { + // // could be an XML date type + // return eDataType.equals(xmlDateTimeEDataType); + // } + return java.sql.Timestamp.class == ic || Date.class == ic; + } + + /** + * @return Returns true if and only if the given type is either a primitive + * or a wrapper or string or a date. + */ + public boolean isSimpleType(EDataType eType, PersistenceOptions po) { + // TODO move elsewhere + return isEPrimitive(eType) || isEWrapper(eType) || isEString(eType) + || isEDate(eType, po) || isEDateTime(eType); + } + + /** + * EJB3-SPEC 9.1.16 + * + * @return Returns true if the given eDataType is a Basic type + */ + public boolean isBasicType(EDataType eDataType, PersistenceOptions po) { + // TODO consider also BigInteger, BigDecimal, java.util.Calendar, + // java.sql.Date + // java.sql.Time, java.sql.Timestamp, byte[], Byte[], char[], + // Character[] + // and any other type that implements Serializable + return isSimpleType(eDataType, po) || isEnum(eDataType); + } + + /** + * @return Returns true if the given EDataType is an Ecore enumerated type. + */ + public boolean isEnum(EClassifier eClassifier) { + return (eClassifier instanceof EEnum); + } + + /** + * @return true if the eType is a byte array. + */ + public boolean isByteArray(EDataType eType) { + final Class<?> clazz = eType.getInstanceClass(); + if (clazz != null) { + return (clazz.isArray() && clazz.getComponentType().equals( + Byte.TYPE)); + } + return false; + } +} diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/FieldUtil.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/FieldUtil.java new file mode 100755 index 000000000..f4e2e1fbf --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/FieldUtil.java @@ -0,0 +1,174 @@ +/** + * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights + * reserved. This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal - Initial API and + * implementation </copyright> $Id: FieldUtil.java,v 1.18 2009/07/31 00:38:16 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.util; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Hashtable; + +import org.eclipse.emf.teneo.TeneoException; + +/** + * Contains different util methods. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.18 $ + */ + +public class FieldUtil { + /** The hashtable caches clazz field name combinations */ + private static final Hashtable<String, Object> fieldMethodCache = new Hashtable<String, Object>(); + + /** Sets a field and wraps the exceptions */ + public static Object callMethod(Object obj, String methodName, Object[] params) { + Method method = (Method) fieldMethodCache.get(obj.getClass().getName() + "." + methodName); + + try { + if (method == null) { + method = getMethodInternal(obj.getClass(), methodName, (params == null ? 0 : params.length)); + } + if (method != null) { + fieldMethodCache.put(obj.getClass().getName() + "." + methodName + "." + + (params == null ? 0 : params.length), method); + } else { + throw new TeneoException("Method does not exist " + obj.getClass().getName() + " method; "); + } + + return method.invoke(obj, params); + } catch (Exception e) { + final StringBuffer paramStr = new StringBuffer(); + if (params != null) { + for (Object param : params) { + paramStr.append(" - " + param + " (" + param.getClass().getName() + ")"); + } + } + + throw new TeneoException("Exception " + obj.getClass().getName() + " method; " + methodName + + " with parameters: " + paramStr.toString(), e); + } + } + + /** Sets a field and wraps the exceptions */ + public static void setField(Field field, Object obj, Object value) { + try { + field.set(obj, value); + } catch (IllegalAccessException e) { + throw new TeneoException("IllegalAccessException " + obj.getClass().getName() + " field; " + + field.getName()); + } + } + + /** + * Get the value for a field, first the field is accessed directly if not found then the getter is called. + */ + public static Object callGetter(Object target, String fieldName) { + try { + Method method = getMethodInternal(target.getClass(), "get" + fieldName, 0); + if (method == null) { + method = getMethodInternal(target.getClass(), "is" + fieldName, 0); + } + if (method == null) { + final Field field = getField(target.getClass(), fieldName); + return field.get(target); + } + return callMethod(target, method.getName(), new Object[0]); + } catch (Exception e) { + throw new TeneoException("Exception getting " + fieldName + " from " + target.getClass().getName(), e); + } + } + + /** Set the field directly or through the set method */ + public static void callSetter(Object target, String fieldName, Object value) { + try { + final Method method = getMethodInternal(target.getClass(), "get" + fieldName, 1); + if (method != null) { + callMethod(target, "set" + fieldName, new Object[] { value }); + return; + } + final Field field = getField(target.getClass(), fieldName); + field.set(target, value); + } catch (Exception e) { + throw new TeneoException("Exception setting " + fieldName + " from " + target.getClass().getName() + + " to value " + value + " of type " + (value != null ? value.getClass().getName() : ""), e); + } + } + + /** + * Returns a field using a certain name, walks up the class hierarchy to find the field, will make the field + * accessible also. Is a bit rough because it does a case insensitive search. Note if the field is not found an + * exception is thrown. + */ + public static Field getField(Class<?> clazz, String fieldName) { + Field field = (Field) fieldMethodCache.get(clazz.getName() + "." + fieldName); + + if (field != null) { + return field; + } + + try { + field = getFieldInternal(clazz, fieldName); + if (field == null) { + field = getFieldInternal(clazz, fieldName + "_"); // the way + // emf + // escapes + // fields + } + } catch (Exception e) // todo replace with specific exception + { + throw new TeneoException("Field " + fieldName + " not accessible for class: " + clazz.getName(), e); + } + if (field == null) { + return null; + } + + fieldMethodCache.put(clazz.getName() + "." + fieldName, field); + field.setAccessible(true); + return field; + } + + /** Does the actual search for the field */ + private static Field getFieldInternal(Class<?> clazz, String fieldName) throws Exception { + if (clazz == null) { + return null; + } + + final Field[] fields = clazz.getDeclaredFields(); + for (Field element : fields) { + if (element.getName().compareToIgnoreCase(fieldName) == 0) { + element.setAccessible(true); + return element; + } + } + + return getFieldInternal(clazz.getSuperclass(), fieldName); + } + + /** Does the actual search for the method */ + private static Method getMethodInternal(Class<?> clazz, String methodName, int numOfParams) throws Exception { + if (clazz == null) { + return null; + } + + final Method method = (Method) fieldMethodCache.get(clazz.getName() + "." + methodName); + if (method != null) { + return method; + } + final Method[] methods = clazz.getDeclaredMethods(); + for (Method element : methods) { + if (element.getName().compareToIgnoreCase(methodName) == 0 + && element.getParameterTypes().length == numOfParams) { + element.setAccessible(true); + fieldMethodCache.put(clazz.getName() + "." + methodName + "." + numOfParams, element); + return element; + } + } + + return getMethodInternal(clazz.getSuperclass(), methodName, numOfParams); + } +}
\ No newline at end of file diff --git a/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/StoreUtil.java b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/StoreUtil.java new file mode 100755 index 000000000..3020b34da --- /dev/null +++ b/core/org.eclipse.emf.teneo/src/org/eclipse/emf/teneo/util/StoreUtil.java @@ -0,0 +1,665 @@ +/** + * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights + * reserved. This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal - Initial API and + * implementation </copyright> $Id: StoreUtil.java,v 1.31 2010/03/28 09:20:26 mtaal Exp $ + */ + +package org.eclipse.emf.teneo.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.net.URL; +import java.util.ArrayList; +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.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.common.notify.impl.NotificationImpl; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.common.util.EMap; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EAnnotation; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.impl.EStoreEObjectImpl; +import org.eclipse.emf.ecore.impl.EStoreEObjectImpl.EStoreEList; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.ecore.util.FeatureMapUtil; +import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl; +import org.eclipse.emf.ecore.xml.type.XMLTypePackage; +import org.eclipse.emf.teneo.Constants; +import org.eclipse.emf.teneo.PackageRegistryProvider; +import org.eclipse.emf.teneo.TeneoException; +import org.eclipse.emf.teneo.ecore.EModelResolver; +import org.eclipse.emf.teneo.type.PersistentStoreAdapter; + +/** + * Contains different util methods. + * + * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> + * @version $Revision: 1.31 $ + */ + +public class StoreUtil { + + /** The logger */ + private static Log log = LogFactory.getLog(StoreUtil.class); + + /** Separator between package and the extension */ + public static final char EXTENSION_SEPARATOR = '.'; + + /** Separator in package names */ + private static final char PACKAGE_SEPARATOR = '.'; + + /** + * Separator between segments of an identifying strucuralfeature or edatatype path + */ + public static final char PATH_SEPARATOR = '/'; + + /** The Annotation source name */ + public static final String ANNOTATION_SOURCE = "http:///org/eclipse/emf/ecore/util/ExtendedMetaData"; + + public static void resetSyntheticListInfo(EStructuralFeature eFeature, + Object target) { + if (target == null || eFeature instanceof EAttribute) { + return; + } + PersistentStoreAdapter persistentStoreAdapter = StoreUtil + .getPersistentStoreAdapter((EObject) target); + persistentStoreAdapter.setSyntheticProperty(StoreUtil + .getExtraLazyInverseIndexPropertyName(eFeature), null); + persistentStoreAdapter.setSyntheticProperty(StoreUtil + .getExtraLazyInversePropertyName(eFeature), null); + } + + public static void setSyntheticListIndex(EStructuralFeature eFeature, + Object target, Integer value) { + if (eFeature instanceof EAttribute) { + return; + } + PersistentStoreAdapter persistentStoreAdapter = StoreUtil + .getPersistentStoreAdapter((EObject) target); + persistentStoreAdapter.setSyntheticProperty(StoreUtil + .getExtraLazyInverseIndexPropertyName(eFeature), value); + } + + public static void setSyntheticListOwner(EStructuralFeature eFeature, + Object target, Object owner) { + if (eFeature instanceof EAttribute) { + return; + } + PersistentStoreAdapter persistentStoreAdapter = StoreUtil + .getPersistentStoreAdapter((EObject) target); + persistentStoreAdapter.setSyntheticProperty(StoreUtil + .getExtraLazyInversePropertyName(eFeature), owner); + } + + public static String getExtraLazyInversePropertyName( + EStructuralFeature eFeature) { + return eFeature.getEContainingClass().getName() + + eFeature.getName(); + } + + public static String getExtraLazyInverseIndexPropertyName( + EStructuralFeature eFeature) { + return getExtraLazyInversePropertyName(eFeature) + "idx"; + } + + /** Returns true if the passed EAttribute is a qname */ + public static boolean isQName(EAttribute eAttribute) { + final EDataType eDataType = eAttribute.getEAttributeType(); + return eDataType == XMLTypePackage.eINSTANCE.getQName(); + } + + /** Reads the epackages present in the passed ecore files. */ + public static List<EPackage> readEPackages(String[] ecoreFiles) { + final ResourceSet resourceSet = new ResourceSetImpl(); + resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("*", new EcoreResourceFactoryImpl()); + final ArrayList<EPackage> epackages = new ArrayList<EPackage>(); + for (String element : ecoreFiles) { + + log.debug("Reading ecore file: " + element); + + Resource res = resourceSet.getResource(URI.createFileURI(element), true); + + Iterator<EObject> it = res.getAllContents(); + while (it.hasNext()) { + final EObject obj = it.next(); + if (obj instanceof EPackage) { + final EPackage epack = (EPackage) obj; + final EPackage currentEPackage = PackageRegistryProvider.getInstance().getPackageRegistry() + .getEPackage(epack.getNsURI()); + if (currentEPackage != null) { // use the existing epackage + if (!epackages.contains(currentEPackage)) { + epackages.add(currentEPackage); + } + } else { + PackageRegistryProvider.getInstance().getPackageRegistry().put(epack.getNsURI(), epack); + epackages.add(epack); + } + } + } + } + return epackages; + } + + /** + * Returns true if the passed EStructuralFeature represents a map. Note that the method also handles EAttribute + * (returns false) for convenience reasons. + */ + public static boolean isMap(EStructuralFeature eStructuralFeature) { + if (eStructuralFeature instanceof EAttribute) { + return false; + } + final EReference eref = (EReference) eStructuralFeature; + return isMapEntry(eref.getEReferenceType()); + } + + /** Same method only for an eclass */ + public static boolean isMapEntry(EClass eclass) { + return eclass != null && eclass.getInstanceClass() != null + && Map.Entry.class.isAssignableFrom(eclass.getInstanceClass()) + && eclass.getEStructuralFeatures().size() == 2 && eclass.getEStructuralFeature("key") != null + && eclass.getEStructuralFeature("value") != null; + } + + public static boolean isEStoreList(Object o) { + if (o == null) { + return false; + } + if (o instanceof EStoreEList<?>) { + return true; + } + if (o instanceof EStoreEObjectImpl.BasicEStoreEList<?>) { + return true; + } + return false; + } + + /** The nsprefix, eclass separator */ + // private static final String NSPREFIX_ECLASS_SEPARATOR = "."; + /** Returns the name of the entity used for this feature map entry */ + public static String getEntityName(EStructuralFeature feature) { + assert (FeatureMapUtil.isFeatureMap(feature)); + return feature.getEContainingClass().getName() + "_" + feature.getName(); + } + + /** Returns a loggable string for a efeature */ + public static String toString(EStructuralFeature efeature) { + if (efeature == null) { + return "NULL"; // possibly throw error + } + return efeature.getEContainingClass().getName() + "/" + efeature.getName(); + } + + /** + * Translates an ECLass to a string representation public static String getEClassURI(EClass eclass, String qualify) + * { if (eclass == EOBJECT_ECLASS) { return EOBJECT_ECLASS_URI; } if (qualify == null || + * qualify.compareTo(PersistenceOptions.QUALIFY_ENTITY_NAME_NO) == 0) { return eclass.getName(); } else if + * (qualify.compareTo(PersistenceOptions.QUALIFY_ENTITY_NAME_NSPREFIX) == 0) { return + * eclass.getEPackage().getNsPrefix() + "." + eclass.getName(); } throw new StoreException("Qualify type " + qualify + * + " unknown"); } /** Returns an estructuralfeature on the basis of the name of the eclass and the name of the + * feature itself. public static EStructuralFeature getEStructuralFeature(String eclassURI, String featureName, + * EPackage[] epackages) { EClass eclass = getEClassFromURI(eclassURI, epackages); if (eclass == null) return null; + * return eclass.getEStructuralFeature(featureName); } /* /** Translates an eclass uri back to an eclass / public + * static EClass getEClassFromURI(String theEClassURI) { final Registry packageRegistry = Registry.INSTANCE; final + * EPackage[] epacks = new EPackage[packageRegistry.size()]; int cnt = 0; for (Iterator it = + * packageRegistry.values().iterator(); it.hasNext();) { final EPackage epack = (EPackage) it.next(); epacks[cnt++] + * = epack; } return getEClassFromURI(theEClassURI, epacks); } /** Translates an eclass uri back to an eclass / + * public static EClass getEClassFromURI(String theEClassURI, EPackage[] epackages, EClassNameStrategy nameStrategy) + * { if (theEClassURI.compareTo(EOBJECT_ECLASS_URI) == 0) { return EcorePackage.eINSTANCE.getEObject(); } String + * nsPrefix = null; String eClassName = theEClassURI; if (eClassName.indexOf(NSPREFIX_ECLASS_SEPARATOR) != -1) { + * nsPrefix = theEClassURI.substring(0, eClassName.lastIndexOf(NSPREFIX_ECLASS_SEPARATOR)); eClassName = + * theEClassURI.substring(1 + eClassName.lastIndexOf(NSPREFIX_ECLASS_SEPARATOR)); } ArrayList eclasses = new + * ArrayList(); for (int i = 0; i < epackages.length; i++) { EPackage epack = epackages[i]; if (nsPrefix != null && + * epack.getNsPrefix() != null && epack.getNsPrefix().compareTo(nsPrefix) != 0) { continue; } EClassifier + * eclassifier = epack.getEClassifier(eClassName); if (eclassifier instanceof EClass) { eclasses.add(eclassifier); } + * } if (eclasses.size() == 1) { return (EClass) eclasses.get(0); } else if (eclasses.size() == 0) { return null; // + * throw new StoreException("The uri " + eclassURI + " can not be translated to an eclass"); } else { StringBuffer + * eclassList = new StringBuffer(); for (Iterator it = eclasses.iterator(); it.hasNext();) { EClass eclass = + * (EClass) it.next(); eclassList.append(eclass.getEPackage().getNsURI() + "/" + eclass.getName()); } throw new + * StoreException("The uri " + eClassName + " maps to multiple eclasses: " + eclassList.toString()); } /* int + * lastIndex = eclassURI.lastIndexOf(PATH_SEPARATOR); if (lastIndex == -1) { throw new + * StoreAnnotationsException("The uri: " + eclassURI + " has an illegal format, it can not parsed to an eclass."); } + * final String nsuri = eclassURI.substring(0, lastIndex); final String name = eclassURI.substring(lastIndex + 1); + * final EPackage epack = PackageRegistryProvider.getInstance().getPackageRegistry().getEPackage(nsuri); if (epack + * == null) { throw new StoreAnnotationsException("No package found for the nsuri: " + nsuri + + * " using the eclassURI " + eclassURI); } final EClass eclass = (EClass)epack.getEClassifier(name); if (eclass == + * null) { throw new StoreAnnotationsException("The nsuri " + nsuri + " and eclassname: " + name + + * " does not resolve to an EClass"); } return eclass; / } + */ + /** Sends out a notification of an elist load */ + public static void dispatchEListLoadNotification(final EObject notifier, final EList<? extends EObject> elist, + final EStructuralFeature feature) { + notifier.eNotify(new NotificationImpl(Constants.ELIST_LOAD_NOTIFICATION, null, elist) { + @Override + public Object getNotifier() { + return notifier; + } + + @Override + public Object getFeature() { + return feature; + } + + @Override + public int getFeatureID(Class<?> expectedClass) { + return feature.getFeatureID(); + } + }); + } + + /** + * Returns the string which is used to store the unique identification of this structuralfeature in the db + */ + public static String structuralFeatureToString(EStructuralFeature structuralFeature) { + // the unique id will consist of three part: 1) package NSURI, 2) EClass + // Name, 3) StructuralFeature Name + final String nsuri = structuralFeature.getEContainingClass().getEPackage().getNsURI(); + final String eclassName = structuralFeature.getEContainingClass().getName(); + final String featureName = structuralFeature.getName(); + + return nsuri + PATH_SEPARATOR + eclassName + PATH_SEPARATOR + featureName; + } + + /** Returns true if the passed feature is a wildcard feature */ + public static boolean isWildCard(EStructuralFeature feature) { + EAnnotation eAnnotation = feature.getEAnnotation("http:///org/eclipse/emf/ecore/util/ExtendedMetaData"); + if (eAnnotation == null) { + return false; + } + final String kind = eAnnotation.getDetails().get("kind"); + final String wildcards = eAnnotation.getDetails().get("wildcards"); + return kind != null && kind.compareTo("elementWildcard") == 0 && wildcards != null; + /* + * // todo optimise this with a cache final EAnnotation eannotation = feature + * .getEAnnotation("http:///org/eclipse/emf/ecore/util/ExtendedMetaData" ); if (eannotation != null) { final + * String kind = (String)eannotation.getDetails().get("kind"); final String wildcards = + * (String)eannotation.getDetails().get("wildcards"); if (wildcards != null && kind != null && + * "elementWildcard".compareTo(kind) == 0) { return true; } } return false; + */ + } + + /** Returns true if the passed feature is a wildcard feature */ + public static boolean isMixed(EStructuralFeature feature) { + EAnnotation eAnnotation = feature.getEAnnotation("http:///org/eclipse/emf/ecore/util/ExtendedMetaData"); + if (eAnnotation == null) { + return false; + } + final String kind = eAnnotation.getDetails().get("kind"); + final String name = eAnnotation.getDetails().get("name"); + return kind != null && kind.compareTo("elementWildcard") == 0 && name != null && name.compareTo(":mixed") == 0; + /* + * // todo optimise this with a cache final EAnnotation eannotation = feature + * .getEAnnotation("http:///org/eclipse/emf/ecore/util/ExtendedMetaData" ); if (eannotation != null) { final + * String kind = (String)eannotation.getDetails().get("kind"); final String wildcards = + * (String)eannotation.getDetails().get("wildcards"); if (wildcards != null && kind != null && + * "elementWildcard".compareTo(kind) == 0) { return true; } } return false; + */ + } + + /** Translates a string to a structural feature */ + public static EStructuralFeature stringToStructureFeature(String strid) { + // this method expects a dbid consisting of three parts separated by / + int lastIndex = strid.lastIndexOf(PATH_SEPARATOR); + int beforeLastIndex = strid.lastIndexOf(PATH_SEPARATOR, lastIndex - 1); + + if (lastIndex == -1 || beforeLastIndex == -1) { + throw new TeneoException( + "The database id stored for a structuralfeature used in a featuremap entry is invalid, dbid: " + + strid); + } + + final String nsuri = strid.substring(0, beforeLastIndex); + final String eclassName = strid.substring(beforeLastIndex + 1, lastIndex); + final String featureName = strid.substring(lastIndex + 1); + + final EPackage epack = PackageRegistryProvider.getInstance().getPackageRegistry().getEPackage(nsuri); + if (epack == null) { + throw new TeneoException("The dbid " + strid + " and nsuri: " + nsuri + " does not resolve to an epackage"); + } + + final EClass eclass = (EClass) epack.getEClassifier(eclassName); + if (eclass == null) { + throw new TeneoException("The dbid " + strid + " and eclassname: " + eclassName + + " does not resolve to an eclass"); + } + + final EStructuralFeature structFeature = eclass.getEStructuralFeature(featureName); + if (structFeature == null) { + throw new TeneoException("The dbid " + strid + " and featurename: " + featureName + + " does not resolve to a structural feature"); + } + + return structFeature; + } + + /** + * Returns the string which is used to store the unique identification of the passed edatatype + */ + public static String edatatypeToString(EDataType edatatype) { + // the unique id will consist of two part: 1) package NSURI, 2) Name + final String nsuri = edatatype.getEPackage().getNsURI(); + final String name = edatatype.getName(); + return nsuri + PATH_SEPARATOR + name; + } + + /** Translates a string to an edatatype */ + public static EDataType stringToEDataType(String strid) { + // this method expects a dbid consisting of three parts separated by / + int lastIndex = strid.lastIndexOf(PATH_SEPARATOR); + + if (lastIndex == -1) { + throw new TeneoException("The database id stored for a datatype is invalid, dbid: " + strid); + } + + final String nsuri = strid.substring(0, lastIndex); + final String name = strid.substring(lastIndex + 1); + + final EPackage epack = PackageRegistryProvider.getInstance().getPackageRegistry().getEPackage(nsuri); + if (epack == null) { + throw new TeneoException("The dbid " + strid + " and nsuri: " + nsuri + " does not resolve to an epackage"); + } + + final EDataType edatatype = (EDataType) epack.getEClassifier(name); + if (edatatype == null) { + throw new TeneoException("The dbid " + strid + " and eclassname: " + name + + " does not resolve to an EDataType"); + } + return edatatype; + } + + /** + * Based on the eobject and the fieldname returns the structural feature. Caching should be added here. + */ + public static EStructuralFeature getEStructuralFeature(EObject emfObj, String fieldName) { + return getEStructuralFeature(emfObj.eClass(), fieldName); + } + + /** + * Returns all the direct content based on the eclass of the object, null content is not returned + */ + public static Object[] getObjectContent(EObject eo) { + final ArrayList<Object> result = new ArrayList<Object>(); + for (EStructuralFeature estruct : eo.eClass().getEAllStructuralFeatures()) { + final Object val = eo.eGet(estruct); + if (val != null) { + result.add(val); + } + } + return result.toArray(new Object[result.size()]); + } + + /** + * Based on the eclass and the fieldname returns the structural feature. Caching should be added here. + */ + public static EStructuralFeature getEStructuralFeature(EClass eclass, String fieldName) { + EStructuralFeature reserve = null; + for (EStructuralFeature estruct : eclass.getEAllStructuralFeatures()) { + String name = estruct.getName(); + if (name.compareTo(fieldName) == 0) { + return estruct; + } + if (name.compareToIgnoreCase(fieldName) == 0) { + reserve = estruct; // use this if all else fails + } else if ((name + "_").compareToIgnoreCase(fieldName) == 0) { // reserved + // word + reserve = estruct; // use this if all else fails + } + } + + if (reserve != null) { + return reserve; + } + + return null; + + // throw new StoreException("The fieldname " + fieldName + " is not + // present as a ereference type in the class: " + // + eclass.getName()); + } + + /** + * Returns a list of all ereferences with many of the emfObj. TODO add caching + */ + public static EReference[] getManyGroupEReferences(EObject emfObj) { + final ArrayList<EReference> manyRefs = new ArrayList<EReference>(); + for (EStructuralFeature estruct : emfObj.eClass().getEAllStructuralFeatures()) { + if (estruct instanceof EReference + && (((EReference) estruct).isMany() || isGroupFeature(estruct) || isWildCard(estruct))) { + manyRefs.add((EReference) estruct); + } + } + return manyRefs.toArray(new EReference[manyRefs.size()]); + } + + /** Checks if a structural feature is a group feature */ + public static boolean isGroupFeature(EStructuralFeature estruct) { + final EAnnotation annotation = estruct.getEAnnotation(ANNOTATION_SOURCE); + + if (annotation == null) { + return false; + } + + final EMap<String, String> map = annotation.getDetails(); + for (String key : map.keySet()) { + final String value = map.get(key); + if ("kind".compareTo(key) == 0 && "group".compareTo(value) == 0) { + return true; + } + } + return false; + } + + /** + * Returns the name used for the group feature, if the feature is not a group then null is returned + */ + public static String getGroupName(EStructuralFeature estruct) { + final EAnnotation annotation = estruct.getEAnnotation(ANNOTATION_SOURCE); + + if (annotation == null) { + return null; + } + + boolean isGroup = false; + String name = null; + + final EMap<String, String> map = annotation.getDetails(); + for (String key : map.keySet()) { + final String value = map.get(key); + if ("kind".compareTo(key) == 0 && "group".compareTo(value) == 0) { + // can not return here because we also need to get the name + isGroup = true; + } + if ("name".compareTo(key) == 0) { + name = value; + } + } + + if (isGroup) { + assert (name != null); + return name; + } + + return null; + } + + /** Checks if a feature is an element of the passed group */ + public static boolean isElementOfGroup(EStructuralFeature estruct, EStructuralFeature groupFeature) { + final EStructuralFeature ef = ExtendedMetaData.INSTANCE.getGroup(estruct); + return ef == groupFeature; + /* + * final EAnnotation annotation = estruct.getEAnnotation(ANNOTATION_SOURCE); if (annotation == null) return + * false; final EMap map = annotation.getDetails(); final Iterator keys = map.keySet().iterator(); while + * (keys.hasNext()) { final String key = (String)keys.next(); final String value = (String)map.get(key); // for + * a choice the groupName in the group element starts with # for substitution it // doesn't therefore the + * endsWith if ("group".compareTo(key) == 0 && value != null && groupName.endsWith(value)) { return true; } } + * return false; + */ + } + + /** Checks if a feature is an element of a group */ + public static boolean isElementOfAGroup(EStructuralFeature estruct) { + final EStructuralFeature ef = ExtendedMetaData.INSTANCE.getGroup(estruct); + return ef != null; + /* + * // group elements are always transient if (!estruct.isTransient()) return false; final EAnnotation annotation + * = estruct.getEAnnotation(ANNOTATION_SOURCE); if (annotation == null) return false; final EMap map = + * annotation.getDetails(); final Iterator keys = map.keySet().iterator(); while (keys.hasNext()) { final String + * key = (String)keys.next(); final String value = (String)map.get(key); if ("group".compareTo(key) == 0 && + * value != null) return true; } return false; + */ + } + + /** + * Returns the list of estructuralfeatures which belong to a certain feature map entry + */ + public static List<EStructuralFeature> getFeaturesOfGroup(EAttribute eattr) { + final ArrayList<EStructuralFeature> result = new ArrayList<EStructuralFeature>(); + for (EStructuralFeature efeature : eattr.getEContainingClass().getEStructuralFeatures()) { + if (StoreUtil.isElementOfGroup(efeature, eattr)) { + result.add(efeature); + } + } + return result; + } + + /** + * Returns an array of epackages on the basis of a string with comma separated epackage ns uris + */ + public static EPackage[] getEPackages(String nsuris) { + final String[] epacknsuris = nsuris.split(","); + final EPackage[] epacks = new EPackage[epacknsuris.length]; + for (int i = 0; i < epacknsuris.length; i++) { + final EPackage epack = PackageRegistryProvider.getInstance().getPackageRegistry().getEPackage( + epacknsuris[i]); + if (epack == null) { + throw new TeneoException("EPackage with nsuri: " + epacknsuris[i] + " can not be found,"); + } + epacks[i] = epack; + } + return epacks; + } + + /** Sets a field and wraps the exceptions */ + public static void setField(Field field, Object obj, Object value) { + try { + field.set(obj, value); + } catch (IllegalAccessException e) { + throw new TeneoException("IllegalAccessException " + obj.getClass().getName() + " field; " + + field.getName()); + } + } + + /** + * Determines the list of available mapping files on the basis of the suffix + */ + public static String[] getFileList(String fileName, String additionalLocation) { + final ArrayList<String> result = new ArrayList<String>(); + log.debug(">>>> Building or descriptor file List"); + + if (additionalLocation != null) { + result.add(additionalLocation); + } + + final String[] packagelist = buildPackagelist(); + for (final String packagePath : packagelist) { + final String filePath = packagePath + fileName; + + log.debug("Try path: " + filePath); + + final URL url = StoreUtil.class.getResource(filePath); + if (url != null) // file exists + { + log.debug("!!Found!!"); + result.add(filePath); + } + } + return result.toArray(new String[result.size()]); + } + + /** Build list of possible or descriptor file paths */ + private static String[] buildPackagelist() { + // walk through all the classnames + final ArrayList<String> newPackagePathList = new ArrayList<String>(); + newPackagePathList.add(File.pathSeparator); // add the root package + + // TODO: move this to the EModelResolver! + for (Class<?> clazz : EModelResolver.instance().getAllClassesAndInterfaces()) { + final String className = clazz.getName(); + final int classNameIndex = className.lastIndexOf(PACKAGE_SEPARATOR); + final String trunkClassName = className.substring(0, classNameIndex); + final String startPath = PATH_SEPARATOR + trunkClassName.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR); + + buildPackagePathFromClassName(startPath, newPackagePathList); + } + return newPackagePathList.toArray(new String[newPackagePathList.size()]); + } + + /** Take care of one class */ + private static void buildPackagePathFromClassName(String path, ArrayList<String> newPackagePathList) { + if (newPackagePathList.contains(path + PATH_SEPARATOR)) { + return; + } + newPackagePathList.add(path + PATH_SEPARATOR); + + final int sepIndex = path.lastIndexOf(PATH_SEPARATOR); + if (sepIndex == -1) { + return; + } + buildPackagePathFromClassName(path.substring(0, sepIndex), newPackagePathList); + } + + /** Copies a file */ + public static void copyFile(File src, File dst) { + try { + log.debug("Copy file from " + src.getAbsolutePath() + " to " + dst.getAbsolutePath()); + InputStream in = new FileInputStream(src); + OutputStream out = new FileOutputStream(dst); + + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + in.close(); + out.close(); + } catch (Exception e) { + throw new TeneoException("Exception while copying from/to " + src.getAbsolutePath() + "/" + + dst.getAbsolutePath(), e); + } + } + + /** + * Checks if an object has a HibernatePersistentStoreAdapter and if it doesn't creates one and returns it. + */ + public static PersistentStoreAdapter getPersistentStoreAdapter(EObject eObject) { + for (Adapter adapter : eObject.eAdapters()) { + if (PersistentStoreAdapter.class.isAssignableFrom(adapter.getClass())) { + return (PersistentStoreAdapter) adapter; + } + } + final PersistentStoreAdapter adapter = new PersistentStoreAdapter(); + eObject.eAdapters().add(adapter); + return adapter; + } + +}
\ No newline at end of file |