Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo')
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfCollectionAdjuster.java154
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfHelper.java277
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfInstanceVariableAccessor.java311
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfOwnedValueHolder.java25
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfQueryBasedValueHolder.java107
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfSessionCustomizer.java123
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfSessionEventListener.java61
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfTransparentIndirectionPolicy.java126
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfUnitOfWorkQueryBasedValueHolder.java79
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/IndirectEContainer.java23
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/Utils.java62
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/EListContainerPolicy.java127
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/EclipseLinkEList.java121
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/IndirectEList.java898
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/EMapContainerPolicy.java182
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/EclipseLinkEMap.java92
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/IndirectEMap.java1246
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/internal/messages/Messages.java59
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/internal/messages/messages.properties47
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResource.java34
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResourceFactoryImpl.java26
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResourceImpl.java634
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkURIUtil.java124
-rwxr-xr-xeclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/util/MapEntryId.java67
24 files changed, 5005 insertions, 0 deletions
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfCollectionAdjuster.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfCollectionAdjuster.java
new file mode 100755
index 000000000..6ee52f944
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfCollectionAdjuster.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import java.util.List;
+
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.common.util.EMap;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.teneo.eclipselink.elist.IndirectEList;
+import org.eclipse.emf.teneo.eclipselink.elistfactory.EListFactory;
+import org.eclipse.emf.teneo.eclipselink.emap.IndirectEMap;
+import org.eclipse.emf.teneo.eclipselink.internal.messages.Messages;
+import org.eclipse.persistence.descriptors.ClassDescriptor;
+import org.eclipse.persistence.descriptors.DescriptorEvent;
+import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.mappings.CollectionMapping;
+import org.eclipse.persistence.mappings.DatabaseMapping;
+
+public class EmfCollectionAdjuster extends DescriptorEventAdapter {
+
+ @Override
+ public void postMerge(DescriptorEvent event) {
+ configureEmfCollectionMappings(event);
+ }
+
+ @Override
+ public void postBuild(DescriptorEvent event) {
+ configureEmfCollectionMappings(event);
+ }
+
+ @Override
+ public void postClone(DescriptorEvent event) {
+ configureEmfCollectionMappings(event);
+ }
+
+ /*
+ * After a merge, replace EclipseLinkEList/Map in IndirectEList/Map
+ */
+ // private void introduceEmfCollectionClasses(DescriptorEvent event) {
+ // ClassDescriptor desc = (ClassDescriptor) event.getDescriptor();
+ // EObject original = (EObject) event.getOriginalObject();
+ // EObject source = (EObject) event.getSource();
+ // for (Iterator mI = desc.getMappings().iterator(); mI.hasNext();) {
+ // DatabaseMapping dbMapping = (DatabaseMapping) mI.next();
+ // if (dbMapping.isCollectionMapping()) {
+ // CollectionMapping collectionMapping = (CollectionMapping)dbMapping;
+ // if (isEMapMapping(collectionMapping)) {
+ // configureEMapMapping(source, collectionMapping);
+ // } else {
+ // configureEListMapping(source, collectionMapping);
+ // }
+ // }
+ // }
+ // }
+ private void configureEmfCollectionMappings(DescriptorEvent event) {
+ ClassDescriptor desc = event.getDescriptor();
+ EObject source = (EObject) event.getSource();
+ for (DatabaseMapping dbMapping : desc.getMappings()) {
+ if (dbMapping.isCollectionMapping()) {
+ CollectionMapping collectionMapping = (CollectionMapping) dbMapping;
+ if (isEMapMapping(collectionMapping)) {
+ configureEMapMapping(source, collectionMapping);
+ } else {
+ configureEListMapping(source, collectionMapping);
+ }
+ }
+ }
+ }
+
+ private boolean isEMapMapping(CollectionMapping collectionMapping) {
+ Class<?> containerClass = collectionMapping.getContainerPolicy().getContainerClass();
+ return Helper.classImplementsInterface(containerClass, EMap.class);
+ }
+
+ private void configureEMapMapping(EObject source, CollectionMapping collectionMapping) {
+ if (collectionMapping.usesIndirection()) {
+ Object collectionValue = collectionMapping.getAttributeValueFromObject(source);
+ if (collectionValue instanceof IndirectEMap<?, ?>) {
+ IndirectEMap<?, ?> contentsMap = (IndirectEMap<?, ?>) collectionValue;
+ if (contentsMap.isInstantiated()) {
+ replaceWithEmfMap(source, collectionMapping);
+ } else {
+ EmfTransparentIndirectionPolicy policy = (EmfTransparentIndirectionPolicy) collectionMapping
+ .getIndirectionPolicy();
+ policy.setAttributeOwner(collectionMapping.getAttributeName(), source);
+ }
+ }
+ } else {
+ replaceWithEmfMap(source, collectionMapping);
+ }
+ }
+
+ private void configureEListMapping(EObject source, CollectionMapping collectionMapping) {
+ if (collectionMapping.usesIndirection()) {
+ Object collectionValue = collectionMapping.getAttributeValueFromObject(source);
+ if (collectionValue instanceof IndirectEList<?>) {
+ IndirectEList<?> contentsList = (IndirectEList<?>) collectionValue;
+ if (contentsList.isInstantiated()) {
+ replaceWithEmfList(source, collectionMapping);
+ } else {
+ EmfTransparentIndirectionPolicy policy = (EmfTransparentIndirectionPolicy) collectionMapping
+ .getIndirectionPolicy();
+ policy.setAttributeOwner(collectionMapping.getAttributeName(), source);
+ }
+ }
+ } else {
+ replaceWithEmfList(source, collectionMapping);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <K, V> void replaceWithEmfMap(EObject source, CollectionMapping collectionMapping) {
+ EMap<K, V> contentsMap = (EMap<K, V>) collectionMapping.getAttributeValueFromObject(source);
+ try {
+ // build new EMap using Model
+ EMap<K, V> newMap = EListFactory.eINSTANCE.createEMap(source, collectionMapping.getAttributeName());
+ EmfHelper.getInstance().setEMapContents(contentsMap, newMap);
+ // must set on source after addAll because of notification
+ if (contentsMap instanceof IndirectEMap<?, ?>) {
+ IndirectEMap<K, V> indirectEMap = (IndirectEMap<K, V>) contentsMap;
+ indirectEMap.setDelegate(newMap);
+ } else {
+ collectionMapping.setAttributeValueInObject(source, newMap);
+ }
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(Messages.exception_errorBuildingEListImplementation, e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <E> void replaceWithEmfList(EObject source, CollectionMapping collectionMapping) {
+ List<E> contentsList = (List<E>) collectionMapping.getAttributeValueFromObject(source);
+ try {
+ // build new list using Model
+ BasicEList<E> newList = (BasicEList<E>) EListFactory.eINSTANCE.createEList(source, collectionMapping
+ .getAttributeName());
+ // TODO setData() marks the list as modified--is this a problem?
+ newList.setData(contentsList.size(), contentsList.toArray());
+ collectionMapping.setAttributeValueInObject(source, newList);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(Messages.exception_errorBuildingEListImplementation, e);
+ }
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfHelper.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfHelper.java
new file mode 100755
index 000000000..c20010890
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfHelper.java
@@ -0,0 +1,277 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Map;
+
+import org.eclipse.emf.common.util.BasicEList;
+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.InternalEObject;
+import org.eclipse.emf.ecore.util.EObjectEList;
+import org.eclipse.emf.ecore.util.EcoreEList;
+import org.eclipse.emf.ecore.util.EcoreEMap;
+import org.eclipse.emf.teneo.eclipselink.emap.EclipseLinkEMap;
+import org.eclipse.emf.teneo.eclipselink.internal.messages.Messages;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
+
+public class EmfHelper {
+ private static EmfHelper instance = new EmfHelper();
+ private Field delegateEListField;
+ private Field eObjectEListFeatureIDField;
+ private Field eObjectEListOwnerField;
+ private Field ecoreEMapEntryEClassField;
+ private Field ecoreEMapEntryClassField;
+
+ private EmfHelper() {
+ try {
+ delegateEListField = Helper.getField(BasicEMap.class, "delegateEList"); //$NON-NLS-1$
+ eObjectEListFeatureIDField = Helper.getField(EObjectEList.class, "featureID"); //$NON-NLS-1$
+ eObjectEListOwnerField = Helper.getField(EObjectEList.class, "owner"); //$NON-NLS-1$
+ ecoreEMapEntryEClassField = Helper.getField(EcoreEMap.class, "entryEClass"); //$NON-NLS-1$
+ ecoreEMapEntryClassField = Helper.getField(EcoreEMap.class, "entryClass"); //$NON-NLS-1$
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException(Messages.exception_unableToFindDelegateEListFieldOnBasicEMap, e);
+ }
+ }
+
+ public static EmfHelper getInstance() {
+ return instance;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E, K, V> void setECollectionContents(Collection<E> listContents, EList<E> targetList) {
+ if (listContents instanceof EMap<?, ?>) {
+ EmfHelper.getInstance().setEMapContents((EMap<K, V>) listContents, (EMap<K, V>) targetList);
+ } else {
+ EmfHelper.getInstance().setEListContents(listContents, targetList);
+ }
+ }
+
+ public <E> void setEListContents(Collection<E> listContents, EList<E> targetList) {
+ if (targetList instanceof EcoreEList<?>) {
+ Object[] contentsArray = listContents.toArray();
+ try {
+ basicSetEListContents(targetList, contentsArray);
+ // TODO need to determine if the elements are "eContained"
+ // Need to set container reference on each element in contents array
+ // for (int i = 0; i < contentsArray.length; i++) {
+ // Object element = contentsArray[i];
+ // if (element instanceof BasicEObjectImpl) {
+ // BasicEObjectImpl basicEObjectImpl = (BasicEObjectImpl)
+ // contentsArray[i];
+ // // go directly to eBasicSetContainer and not use inverseAdd because
+ // // inverseAdd tries to remove the object from the owner which leads
+ // to
+ // // an infinite loop--eBasicSetContainer does just what is necessary
+ // basicEObjectImpl.eBasicSetContainer(
+ // (InternalEObject)ecoreEList.getNotifier(),
+ // InternalEObject.EOPPOSITE_FEATURE_BASE - ecoreEList.getFeatureID(),
+ // null);
+ // }
+ // }
+ } catch (Exception e) {
+ new RuntimeException(Messages.exception_cannotSetOwnerOnEList, e);
+ }
+ } else {
+ String msg = NLS.bind(Messages.exception_errorSettingEListContents$0, targetList.getClass());
+ throw new RuntimeException(msg);
+ }
+ }
+
+ public <E> boolean addToEList(EList<E> eList, E element) {
+ if (eList instanceof BasicEList<?>) {
+ int size = eList.size();
+ int newSize = size + 1;
+
+ try {
+ // use reflection to: delegate.grow(index);
+ Method growMethod = PrivilegedAccessHelper.getDeclaredMethod(BasicEList.class, "grow", //$NON-NLS-1$
+ new Class[] { int.class });
+ growMethod.setAccessible(true);
+ growMethod.invoke(eList, new Object[] { newSize });
+ // update EList size
+ Field sizeField = PrivilegedAccessHelper.getDeclaredField(BasicEList.class, "size", true); //$NON-NLS-1$
+ sizeField.set(eList, newSize);
+ // use reflection to: delegate.assign(index, object);
+ Method assignMethod = PrivilegedAccessHelper.getDeclaredMethod(BasicEList.class, "assign", new Class[] { //$NON-NLS-1$
+ int.class, Object.class });
+ assignMethod.setAccessible(true);
+ assignMethod.invoke(eList, new Object[] { size, element });
+ } catch (Exception e) {
+ new RuntimeException(Messages.exception_errorAddingElementToEList, e);
+ }
+ return true;
+ } else {
+ return eList.add(element);
+ }
+ }
+
+ public boolean removeFromEList(EList<?> eList, Object object) {
+ if (eList instanceof BasicEList<?>) {
+ try {
+ // adapted from BasicEList.remove(Object object)
+ // to avoid relationship management
+ int size = eList.size();
+ int index = eList.indexOf(object);
+ Field dataField = Helper.getField(eList.getClass(), "data"); //$NON-NLS-1$
+ Object[] data = (Object[]) dataField.get(eList);
+ if (index >= 0) {
+ int shifted = size - index - 1;
+ if (shifted > 0) {
+ System.arraycopy(data, index + 1, data, index, shifted);
+ }
+ // Don't hold onto a duplicate reference to the last object.
+ data[--size] = null;
+ }
+ // update EList size
+ Field sizeField = PrivilegedAccessHelper.getDeclaredField(BasicEList.class, "size", true); //$NON-NLS-1$
+ sizeField.set(eList, size);
+ } catch (Exception e) {
+ new RuntimeException(Messages.exception_errorRemovingElementFromEList, e);
+ }
+ return true;
+ } else {
+ return eList.remove(object);
+ }
+ }
+
+ private void basicSetEListContents(EList<?> targetList, Object[] contentsArray) throws NoSuchFieldException,
+ IllegalAccessException {
+ Field dataField = Helper.getField(targetList.getClass(), "data"); //$NON-NLS-1$
+ PrivilegedAccessHelper.setValueInField(dataField, targetList, contentsArray);
+ Field sizeField = Helper.getField(targetList.getClass(), "size"); //$NON-NLS-1$
+ PrivilegedAccessHelper.setValueInField(sizeField, targetList, contentsArray.length);
+ }
+
+ // TODO extract common code from addToEMap and setEMapContents to cache
+ // methods and field
+ @SuppressWarnings("unchecked")
+ public <K, V> void setEMapContents(EMap<K, V> mapContents, EMap<K, V> targetMap) {
+ try {
+ if (targetMap instanceof BasicEMap<?, ?>) {
+ BasicEMap<K, V> basicEMap = (BasicEMap<K, V>) targetMap;
+ // set the entry list
+ Method basicEMapEnsureEntryDataExistsMethod = PrivilegedAccessHelper.getDeclaredMethod(BasicEMap.class,
+ "ensureEntryDataExists", new Class[] {}); //$NON-NLS-1$
+ basicEMapEnsureEntryDataExistsMethod.setAccessible(true);
+ basicEMapEnsureEntryDataExistsMethod.invoke(basicEMap, new Object[] {});
+ Method basicEMapDoPutMethod = PrivilegedAccessHelper.getDeclaredMethod(BasicEMap.class, "doPut", //$NON-NLS-1$
+ new Class[] { BasicEMap.Entry.class });
+ basicEMapDoPutMethod.setAccessible(true);
+ for (Object entry : mapContents.entrySet()) {
+ basicEMapDoPutMethod.invoke(basicEMap, new Object[] { entry });
+ }
+ // set the delegate list
+ EList<Map.Entry<K, V>> delegateEList = (EList<Map.Entry<K, V>>) delegateEListField.get(basicEMap);
+ basicSetEListContents(delegateEList, mapContents.toArray());
+ }
+ } catch (Exception e) {
+ new RuntimeException(Messages.exception_errorSettingEMapContents, e);
+ }
+ }
+
+ // TODO extract common code from addToEMap and setEMapContents to cache
+ // methods and field
+ @SuppressWarnings("unchecked")
+ public <K, V> boolean addToEMap(EMap<K, V> eMap, Map.Entry<K, V> entry) {
+ if (eMap instanceof EclipseLinkEMap<?, ?>) {
+ return EmfHelper.getInstance().addToEList(eMap, entry);
+ } else {
+ if (eMap instanceof BasicEMap<?, ?>) {
+ try {
+ BasicEMap<K, V> basicEMap = (BasicEMap<K, V>) eMap;
+ // add the entry
+ Method basicEMapEnsureEntryDataExistsMethod = PrivilegedAccessHelper.getDeclaredMethod(
+ BasicEMap.class, "ensureEntryDataExists", new Class[] {}); //$NON-NLS-1$
+ basicEMapEnsureEntryDataExistsMethod.setAccessible(true);
+ basicEMapEnsureEntryDataExistsMethod.invoke(basicEMap, new Object[] {});
+ Method basicEMapDoPutMethod = PrivilegedAccessHelper.getDeclaredMethod(BasicEMap.class, "doPut", //$NON-NLS-1$
+ new Class[] { BasicEMap.Entry.class });
+ basicEMapDoPutMethod.setAccessible(true);
+ basicEMapDoPutMethod.invoke(basicEMap, new Object[] { entry });
+ // update the delegate list
+ EList<Map.Entry<K, V>> delegateEList = (EList<Map.Entry<K, V>>) delegateEListField.get(basicEMap);
+ addToEList(delegateEList, entry);
+ } catch (Exception e) {
+ new RuntimeException(Messages.exception_errorAddingEntryToEMap, e);
+ }
+ return true;
+ } else {
+ String msg = NLS.bind(Messages.exception_collectionClassNotSupported$0, eMap.getClass().getName());
+ throw new RuntimeException(msg);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <K, V> BasicEList<Map.Entry<K, V>> getEMapDelegateEList(BasicEMap<K, V> originalEMap) {
+ try {
+ return (BasicEList<Map.Entry<K, V>>) delegateEListField.get(originalEMap);
+ } catch (Exception e) {
+ // TODO Throw EclipseLinkEMF Exception
+ throw new RuntimeException(Messages.exception_errorGettingDelegateList);
+ }
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public Class<? extends Map.Entry> getEcoreEMapEntryClass(EcoreEMap<?, ?> map) {
+ try {
+ return (Class<? extends Map.Entry>) ecoreEMapEntryClassField.get(map);
+ } catch (Exception e) {
+ // TODO Throw EclipseLinkEMF Exception
+ throw new RuntimeException(Messages.exception_errorGettingDelegateList);
+ }
+ }
+
+ public EClass getEcoreEMapEntryEClass(EcoreEMap<?, ?> map) {
+ try {
+ return (EClass) ecoreEMapEntryEClassField.get(map);
+ } catch (Exception e) {
+ // TODO Throw EclipseLinkEMF Exception
+ throw new RuntimeException(Messages.exception_errorGettingEntryEClass);
+ }
+ }
+
+ public InternalEObject getEObjectEListOwner(EObjectEList<?> map) {
+ try {
+ return (InternalEObject) eObjectEListOwnerField.get(map);
+ } catch (Exception e) {
+ // TODO Throw EclipseLinkEMF Exception
+ throw new RuntimeException(Messages.exception_errorGettingListOwner);
+ }
+ }
+
+ public void setEObjectEListOwner(EObjectEList<?> list, InternalEObject owner) {
+ try {
+ eObjectEListOwnerField.set(list, owner);
+ } catch (Exception e) {
+ // TODO Throw EclipseLinkEMF Exception
+ throw new RuntimeException(Messages.exception_errorSettingListOwner);
+ }
+ }
+
+ public int getEObjectEListFeatureId(EObjectEList<?> list) {
+ try {
+ return eObjectEListFeatureIDField.getInt(list);
+ } catch (Exception e) {
+ // TODO Throw EclipseLinkEMF Exception
+ throw new RuntimeException(Messages.exception_errorGettingFeatureID);
+ }
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfInstanceVariableAccessor.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfInstanceVariableAccessor.java
new file mode 100755
index 000000000..597b8fed0
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfInstanceVariableAccessor.java
@@ -0,0 +1,311 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.emf.ecore.InternalEObject;
+import org.eclipse.emf.ecore.util.EObjectEList;
+import org.eclipse.persistence.exceptions.DescriptorException;
+import org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
+import org.eclipse.persistence.internal.security.PrivilegedGetMethodReturnType;
+import org.eclipse.persistence.internal.security.PrivilegedGetValueFromField;
+import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker;
+import org.eclipse.persistence.internal.security.PrivilegedSetValueInField;
+import org.eclipse.persistence.mappings.AttributeAccessor;
+import org.eclipse.persistence.mappings.DatabaseMapping;
+
+/**
+ * All attribute access from EclipseLink uses the getter for the getting and direct field access for the set. This
+ * allows EclipseLink to leverage the lazy initialization done for instance variables (esp. collections) and yet avoid
+ * firing events when setting an instance variable during object construction. EmfInstanceVariableAccessor uses
+ * delegation to mix instance variable with method access.
+ *
+ * @author shsmith
+ */
+public class EmfInstanceVariableAccessor extends AttributeAccessor {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ protected static Set<String> nonBeanLikeAttributes;
+ protected static String[] emfAttributesNames = { "eAdapters", "eContainer", "eContainerFeatureID", "eFlags", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ "eProperties" }; //$NON-NLS-1$
+
+ static {
+ nonBeanLikeAttributes = new HashSet<String>();
+ Collections.addAll(nonBeanLikeAttributes, emfAttributesNames);
+ }
+
+ protected Field isSetEField;
+ protected String getMethodName;
+ protected transient Method getMethod;
+
+ /*
+ * Enhance direct attribute access with support for isSetE<AttrName>. Replace accessor with
+ * EmfInstanceVariableAccessor for all mapped attributes except for EMF framework define attributes.
+ */
+ public static void customize(DatabaseMapping dbMapping) {
+ AttributeAccessor accessor = dbMapping.getAttributeAccessor();
+ // if attribute is emf framework attribute, don't set
+ // the getter method--will use direct access instead
+ if (nonBeanLikeAttributes.contains(dbMapping.getAttributeName())) {
+ // use direct attribute access
+ InstanceVariableAttributeAccessor instanceVarAccessor = new InstanceVariableAttributeAccessor();
+ instanceVarAccessor.setAttributeName(accessor.getAttributeName());
+ dbMapping.setAttributeAccessor(instanceVarAccessor);
+ } else {
+ // use direct attribute set, method get
+ EmfInstanceVariableAccessor emfAttributeAccessor = new EmfInstanceVariableAccessor(accessor);
+ dbMapping.setAttributeAccessor(emfAttributeAccessor);
+ }
+ }
+
+ protected EmfInstanceVariableAccessor(AttributeAccessor attributeAccessor) {
+ super();
+ String attrName = attributeAccessor.getAttributeName();
+ setAttributeName(attrName);
+ }
+
+ private String getGetterMethodName(String attrName) {
+ String getterMethodName = null;
+ String getterPrefix = null;
+
+ String leadingChar = String.valueOf(attrName.charAt(0)).toUpperCase();
+ String restOfName = attrName.substring(1);
+ Class<?> attributeType = getAttributeField().getType();
+ if (attributeType.equals(boolean.class) || attributeType.equals(Boolean.class)) {
+ getterPrefix = "is"; //$NON-NLS-1$
+ } else {
+ getterPrefix = "get"; //$NON-NLS-1$
+ }
+ getterMethodName = getterPrefix.concat(leadingChar).concat(restOfName);
+ return getterMethodName;
+ }
+
+ /**
+ * Return the return type of the method accessor.
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Class getAttributeClass() {
+ if (getGetMethod() == null) {
+ return null;
+ }
+
+ return getGetMethodReturnType();
+ }
+
+ /**
+ * Gets the value of an instance variable in the object.
+ */
+ @Override
+ public Object getAttributeValueFromObject(Object anObject) throws DescriptorException {
+ try {
+ // see initializeAttributes for comment
+ if (getGetMethodName() == null) {
+ return getAttributeValueFromObjectUsingDirectAccess(anObject);
+ } else {
+ return getAttributeValueFromObjectUsingGetter(anObject);
+ }
+ } catch (IllegalArgumentException exception) {
+ throw DescriptorException.illegalArgumentWhileGettingValueThruMethodAccessor(getGetMethodName(), anObject
+ .getClass().getName(), exception);
+ } catch (IllegalAccessException exception) {
+ throw DescriptorException.illegalAccessWhileGettingValueThruMethodAccessor(getGetMethodName(), anObject
+ .getClass().getName(), exception);
+ } catch (InvocationTargetException exception) {
+ throw DescriptorException.targetInvocationWhileGettingValueThruMethodAccessor(getGetMethodName(), anObject
+ .getClass().getName(), exception);
+ } catch (NullPointerException exception) {
+ // Some JVM's throw this exception for some very odd reason
+ throw DescriptorException.nullPointerWhileGettingValueThruMethodAccessor(getGetMethodName(), anObject
+ .getClass().getName(), exception);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object getAttributeValueFromObjectUsingGetter(Object anObject) throws IllegalAccessException,
+ InvocationTargetException {
+ if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
+ try {
+ return AccessController.doPrivileged(new PrivilegedMethodInvoker(getGetMethod(), anObject,
+ (Object[]) null));
+ } catch (PrivilegedActionException exception) {
+ Exception throwableException = exception.getException();
+ if (throwableException instanceof IllegalAccessException) {
+ throw DescriptorException.illegalAccessWhileGettingValueThruMethodAccessor(getGetMethodName(),
+ anObject.getClass().getName(), throwableException);
+ } else {
+ throw DescriptorException.targetInvocationWhileGettingValueThruMethodAccessor(getGetMethodName(),
+ anObject.getClass().getName(), throwableException);
+ }
+ }
+ } else {
+ return PrivilegedAccessHelper.invokeMethod(getGetMethod(), anObject, (Object[]) null);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object getAttributeValueFromObjectUsingDirectAccess(Object anObject) throws IllegalAccessException {
+ if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
+ try {
+ return AccessController.doPrivileged(new PrivilegedGetValueFromField(attributeField, anObject));
+ } catch (PrivilegedActionException exception) {
+ throw DescriptorException.illegalAccesstWhileGettingValueThruInstanceVaraibleAccessor(
+ getAttributeName(), anObject.getClass().getName(), exception.getException());
+ }
+ } else {
+ return PrivilegedAccessHelper.getValueFromField(attributeField, anObject);
+ }
+ }
+
+ /**
+ * Return the accessor method for the attribute accessor.
+ */
+ protected Method getGetMethod() {
+ return getMethod;
+ }
+
+ /**
+ * Return the name of theh accessor method for the attribute accessor.
+ */
+ public String getGetMethodName() {
+ return getMethodName;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Class<?> getGetMethodReturnType() {
+ if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
+ try {
+ return (Class<?>) AccessController.doPrivileged(new PrivilegedGetMethodReturnType(getGetMethod()));
+ } catch (PrivilegedActionException exception) {
+ // we should not get here since this call does not throw any checked
+ // exceptions
+ return null;
+ }
+ } else {
+ return PrivilegedAccessHelper.getMethodReturnType(getGetMethod());
+ }
+ }
+
+ /**
+ * Set get and set method after creating these methods by using get and set method names
+ */
+ @Override
+ @SuppressWarnings("rawtypes")
+ public void initializeAttributes(Class theJavaClass) throws DescriptorException {
+ if (getAttributeName() == null) {
+ throw DescriptorException.attributeNameNotSpecified();
+ }
+ try {
+ setAttributeField(Helper.getField(theJavaClass, getAttributeName()));
+ setGetMethodName(getGetterMethodName(getAttributeName()));
+ setGetMethod(Helper.getDeclaredMethod(theJavaClass, getGetMethodName(), (Class[]) null));
+ } catch (NoSuchFieldException exception) {
+ throw DescriptorException.noSuchFieldWhileInitializingAttributesInInstanceVariableAccessor(
+ getAttributeName(), theJavaClass.getName(), exception);
+ } catch (NoSuchMethodException ex) {
+ DescriptorException descriptorException = DescriptorException
+ .noSuchMethodWhileInitializingAttributesInMethodAccessor("", getGetMethodName(), theJavaClass //$NON-NLS-1$
+ .getName());
+ descriptorException.setInternalException(ex);
+ throw descriptorException;
+ } catch (SecurityException exception) {
+ DescriptorException descriptorException = DescriptorException
+ .securityWhileInitializingAttributesInMethodAccessor("", getGetMethodName(), theJavaClass.getName()); //$NON-NLS-1$
+ descriptorException.setInternalException(exception);
+ throw descriptorException;
+ }
+ }
+
+ @Override
+ public boolean isMethodAttributeAccessor() {
+ return false;
+ }
+
+ /**
+ * Set the accessor method for the attribute accessor.
+ */
+ protected void setGetMethod(Method getMethod) {
+ this.getMethod = getMethod;
+ }
+
+ /**
+ * Set the name of the accessor method for the attribute accessor.
+ */
+ public void setGetMethodName(String getMethodName) {
+ this.getMethodName = getMethodName;
+ }
+
+ /**
+ * The attribute name of an object is converted to Field type to access it reflectively
+ */
+ protected transient Field attributeField;
+
+ /**
+ * Returns the value of attributeField.
+ */
+ protected Field getAttributeField() {
+ return attributeField;
+ }
+
+ /**
+ * Returns the declared type of attributeField.
+ */
+ public Class<?> getAttributeType() {
+ return attributeField.getType();
+ }
+
+ /**
+ * Sets the value of the attributeField.
+ */
+ protected void setAttributeField(Field field) {
+ attributeField = field;
+ }
+
+ /**
+ * Sets the value of the instance variable in the object to the value.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setAttributeValueInObject(Object anObject, Object value) throws DescriptorException {
+ try {
+ // PERF: Direct variable access.
+ if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
+ try {
+ AccessController.doPrivileged(new PrivilegedSetValueInField(attributeField, anObject, value));
+ } catch (PrivilegedActionException exception) {
+ throw DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(
+ getAttributeName(), value, exception.getException());
+ }
+ } else {
+ PrivilegedAccessHelper.setValueInField(attributeField, anObject, value);
+ }
+ if (value instanceof EObjectEList<?>) {
+ // need to also set owner "backpointer" in delegateEList
+ EmfHelper.getInstance().setEObjectEListOwner((EObjectEList<?>) value, (InternalEObject) anObject);
+ }
+ } catch (IllegalAccessException accessException) {
+ throw DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(getAttributeName(),
+ value, accessException);
+ }
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfOwnedValueHolder.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfOwnedValueHolder.java
new file mode 100755
index 000000000..629283389
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfOwnedValueHolder.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import org.eclipse.emf.ecore.EObject;
+
+public interface EmfOwnedValueHolder {
+
+ public abstract EObject getOwner();
+
+ public abstract void setOwner(EObject owner);
+
+ public abstract String getOwnerAttrName();
+
+ public abstract void setOwnerAttrName(String ownerAttrName);
+
+} \ No newline at end of file
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfQueryBasedValueHolder.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfQueryBasedValueHolder.java
new file mode 100755
index 000000000..877483c82
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfQueryBasedValueHolder.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import java.util.Collection;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.teneo.eclipselink.elistfactory.EListFactory;
+import org.eclipse.emf.teneo.eclipselink.internal.messages.Messages;
+import org.eclipse.persistence.exceptions.DatabaseException;
+import org.eclipse.persistence.internal.indirection.QueryBasedValueHolder;
+import org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder;
+import org.eclipse.persistence.internal.sessions.AbstractRecord;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.queries.ReadQuery;
+
+public class EmfQueryBasedValueHolder extends QueryBasedValueHolder implements EmfOwnedValueHolder {
+
+ private static final long serialVersionUID = 1L;
+ private EObject owner;
+ private String ownerAttrName;
+
+ @Override
+ /*
+ * Triggers UnitOfWork valueholders directly without triggering the wrapped valueholder (this). <p> When in
+ * transaction and/or for pessimistic locking the UnitOfWorkValueHolder needs to be triggered directly without
+ * triggering the wrapped valueholder. However only the wrapped valueholder knows how to trigger the indirection,
+ * i.e. it may be a batchValueHolder, and it stores all the info like the row and the query. Note: This method is
+ * not thread-safe. It must be used in a synchronizaed manner
+ */
+ public Object instantiateForUnitOfWorkValueHolder(UnitOfWorkValueHolder unitOfWorkValueHolder) {
+ // Set the owner and attribute name of the value holder before
+ // it is asked to instantiate an EMF collection that requires it.
+ EmfUnitOfWorkQueryBasedValueHolder emfValueHolder = (EmfUnitOfWorkQueryBasedValueHolder) unitOfWorkValueHolder;
+ setOwner(emfValueHolder.getOwner());
+ setOwnerAttrName(emfValueHolder.getOwnerAttrName());
+ return super.instantiateForUnitOfWorkValueHolder(unitOfWorkValueHolder);
+ }
+
+ @Override
+ protected Object instantiate(AbstractSession session) throws DatabaseException {
+ return doInstantiate(session);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <E> Object doInstantiate(AbstractSession session) throws DatabaseException {
+ Collection<E> contents = (Collection<E>) super.instantiate(session);
+ EList<E> newList = null;
+ try {
+ newList = EListFactory.eINSTANCE.createEList(getOwner(), getOwnerAttrName());
+ EmfHelper.getInstance().setECollectionContents(contents, newList);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(Messages.exception_errorBuildingEListImplementation, e);
+ }
+ return newList;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.teneo.eclipselink.EmfOwnedValueHolder#getOwner()
+ */
+ public EObject getOwner() {
+ return owner;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.teneo.eclipselink.EmfOwnedValueHolder#setOwner(org.eclipse.emf.ecore.EObject)
+ */
+ public void setOwner(EObject owner) {
+ this.owner = owner;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.teneo.eclipselink.EmfOwnedValueHolder#getOwnerAttrName()
+ */
+ public String getOwnerAttrName() {
+ return ownerAttrName;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.teneo.eclipselink.EmfOwnedValueHolder#setOwnerAttrName(java.lang.String)
+ */
+ public void setOwnerAttrName(String ownerAttrName) {
+ this.ownerAttrName = ownerAttrName;
+ }
+
+ public EmfQueryBasedValueHolder(ReadQuery query, AbstractRecord row, AbstractSession session) {
+ super(query, row, session);
+ }
+
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfSessionCustomizer.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfSessionCustomizer.java
new file mode 100755
index 000000000..a6f9e4a2f
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfSessionCustomizer.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+
+import org.eclipse.emf.common.util.EMap;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.teneo.eclipselink.elist.EListContainerPolicy;
+import org.eclipse.emf.teneo.eclipselink.elist.EclipseLinkEList;
+import org.eclipse.emf.teneo.eclipselink.elist.IndirectEList;
+import org.eclipse.emf.teneo.eclipselink.emap.EclipseLinkEMap;
+import org.eclipse.emf.teneo.eclipselink.emap.IndirectEMap;
+import org.eclipse.persistence.config.SessionCustomizer;
+import org.eclipse.persistence.descriptors.ClassDescriptor;
+import org.eclipse.persistence.exceptions.DescriptorException;
+import org.eclipse.persistence.exceptions.EclipseLinkException;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.indirection.NoIndirectionPolicy;
+import org.eclipse.persistence.internal.queries.ContainerPolicy;
+import org.eclipse.persistence.mappings.CollectionMapping;
+import org.eclipse.persistence.mappings.DatabaseMapping;
+import org.eclipse.persistence.queries.ReadAllQuery;
+import org.eclipse.persistence.sessions.Session;
+
+/**
+ * This customization class will modify the session to: 1. Provide attribute accessors that will maintain the isSet
+ * attribute associated with any declared attribute 2. Replace the standard container policies with EMF aware policies
+ * 3. Register a listener on any descriptor with collections to provide postBuild/postClone customization (esp. assign
+ * collection owner) This entire solution is based around the mappings being configured as: 1. The container policy is
+ * set to Map or List--normal settings 2. The container class is specified as Map or List--normal settings 3. The
+ * appropriate method is configured for the map's key
+ *
+ * @author Shaun Smith
+ * @author Doug Clarke
+ * @author Stephan Eberle
+ */
+public class EmfSessionCustomizer implements SessionCustomizer {
+
+ protected static EmfCollectionAdjuster collectionAdjuster = new EmfCollectionAdjuster();
+
+ /**
+ * Invoked after the session is created but prior to login where the mappings are validated. This method walks the
+ * descriptors and mapping replacing the container policy where needed.
+ */
+ @SuppressWarnings("unchecked")
+ public void customize(Session session) throws EclipseLinkException {
+ // register listener that will delete unnecessary EMap Entries at commit
+ // time
+ session.getEventManager().addListener(new EmfSessionEventListener());
+ for (ClassDescriptor descriptor : (Collection<ClassDescriptor>) session.getDescriptors().values()) {
+ if (Helper.classImplementsInterface(descriptor.getJavaClass(), EObject.class)) {
+ boolean shouldAttachListener = false;
+ // adjust relationship mappings
+ for (Object element : descriptor.getMappings()) {
+ DatabaseMapping dbMapping = (DatabaseMapping) element;
+ enhanceAttributeAccesssor(dbMapping);
+ if (dbMapping.isCollectionMapping()) {
+ CollectionMapping collectionMapping = (CollectionMapping) dbMapping;
+ adjustListMapping(collectionMapping);
+ shouldAttachListener = true;
+ }
+ }
+ if (shouldAttachListener) {
+ descriptor.getEventManager().addListener(collectionAdjuster);
+ }
+ }
+ }
+ }
+
+ private boolean isMapCollection(CollectionMapping collectionMapping) throws EclipseLinkException {
+
+ boolean result = false;
+ try {
+ String attributeName = collectionMapping.getAttributeName();
+ Class<?> mappedClass = collectionMapping.getDescriptor().getJavaClass();
+ Field collectionField = Helper.getField(mappedClass, attributeName);
+ result = Helper.classImplementsInterface(collectionField.getType(), EMap.class);
+ } catch (NoSuchFieldException exception) {
+ throw DescriptorException.noFieldNameForMapping(collectionMapping);
+ }
+ return result;
+ }
+
+ private void adjustListMapping(CollectionMapping collectionMapping) throws EclipseLinkException {
+
+ ContainerPolicy newContainerPolicy = new EListContainerPolicy(collectionMapping.getAttributeName());
+ collectionMapping.setContainerPolicy(newContainerPolicy);
+ // we must also update the mapping selection criteria's container policy
+ ReadAllQuery selectionQuery = (ReadAllQuery) collectionMapping.getSelectionQuery();
+ selectionQuery.setContainerPolicy(newContainerPolicy);
+
+ if (collectionMapping.usesIndirection()) {
+ collectionMapping.setIndirectionPolicy(new EmfTransparentIndirectionPolicy());
+ if (isMapCollection(collectionMapping)) {
+ newContainerPolicy.setContainerClass(IndirectEMap.class);
+ } else {
+ newContainerPolicy.setContainerClass(IndirectEList.class);
+ }
+ } else {
+ collectionMapping.setIndirectionPolicy(new NoIndirectionPolicy());
+ if (isMapCollection(collectionMapping)) {
+ newContainerPolicy.setContainerClass(EclipseLinkEMap.class);
+ } else {
+ newContainerPolicy.setContainerClass(EclipseLinkEList.class);
+ }
+ }
+
+ }
+
+ private void enhanceAttributeAccesssor(DatabaseMapping dbMapping) {
+ EmfInstanceVariableAccessor.customize(dbMapping);
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfSessionEventListener.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfSessionEventListener.java
new file mode 100755
index 000000000..9e8c6308e
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfSessionEventListener.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import java.lang.reflect.Field;
+
+import org.eclipse.emf.common.util.BasicEMap;
+import org.eclipse.emf.ecore.impl.EObjectImpl;
+import org.eclipse.emf.teneo.eclipselink.internal.messages.Messages;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
+import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
+import org.eclipse.persistence.sessions.Session;
+import org.eclipse.persistence.sessions.SessionEvent;
+import org.eclipse.persistence.sessions.SessionEventAdapter;
+
+public class EmfSessionEventListener extends SessionEventAdapter {
+
+ public EmfSessionEventListener() {
+ super();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.persistence.sessions.SessionEventAdapter#preCalculateUnitOfWorkChangeSet(org.eclipse.persistence.
+ * sessions.SessionEvent)
+ */
+ @Override
+ public void preCalculateUnitOfWorkChangeSet(SessionEvent event) {
+ Field eContainerField;
+ try {
+ eContainerField = Helper.getField(EObjectImpl.class, "eContainer"); //$NON-NLS-1$
+ Session session = event.getSession();
+ UnitOfWorkImpl uow = (UnitOfWorkImpl) session;
+ for (Object object : uow.getCloneMapping().keySet()) {
+ if (object instanceof BasicEMap.Entry<?, ?>) {
+ EObjectImpl entry = (EObjectImpl) object;
+ if (entry.eContainer() == null) {
+ // restore the eContainer reference so the primary keys can be
+ // calculated
+ EObjectImpl backupEntry = (EObjectImpl) uow.getBackupClone(entry);
+ PrivilegedAccessHelper.setValueInField(eContainerField, entry, backupEntry.eContainer());
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(Messages.exception_errorDeletingEMapEntries, e);
+ }
+ }
+
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfTransparentIndirectionPolicy.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfTransparentIndirectionPolicy.java
new file mode 100755
index 000000000..c27922cab
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfTransparentIndirectionPolicy.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.persistence.descriptors.changetracking.ChangeTracker;
+import org.eclipse.persistence.descriptors.changetracking.CollectionChangeTracker;
+import org.eclipse.persistence.exceptions.DescriptorException;
+import org.eclipse.persistence.indirection.IndirectContainer;
+import org.eclipse.persistence.indirection.ValueHolder;
+import org.eclipse.persistence.indirection.ValueHolderInterface;
+import org.eclipse.persistence.internal.indirection.DatabaseValueHolder;
+import org.eclipse.persistence.internal.indirection.TransparentIndirectionPolicy;
+import org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder;
+import org.eclipse.persistence.internal.sessions.AbstractRecord;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
+import org.eclipse.persistence.mappings.ForeignReferenceMapping;
+import org.eclipse.persistence.queries.ReadQuery;
+
+public class EmfTransparentIndirectionPolicy extends TransparentIndirectionPolicy {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Object valueFromQuery(ReadQuery query, AbstractRecord row, AbstractSession session) {
+ return this.buildIndirectContainer(new EmfQueryBasedValueHolder(query, row, session));
+ }
+
+ public void setAttributeOwner(String attrName, EObject owner) {
+ Object value = getMapping().getAttributeValueFromObject(owner);
+ IndirectContainer container = (IndirectContainer) value;
+ ValueHolderInterface valueHolder = container.getValueHolder();
+ EmfOwnedValueHolder emfOwnedValueHolder = (EmfOwnedValueHolder) valueHolder;
+ emfOwnedValueHolder.setOwner(owner);
+ emfOwnedValueHolder.setOwnerAttrName(attrName);
+ }
+
+ /**
+ * Return a clone of the attribute.
+ *
+ * @param buildDirectlyFromRow
+ * indicates that we are building the clone directly from a row as opposed to building the original from
+ * the row, putting it in the shared cache, and then cloning the original.
+ */
+ @Override
+ public Object cloneAttribute(Object attributeValue, Object original, Object clone, UnitOfWorkImpl unitOfWork,
+ boolean buildDirectlyFromRow) {
+ ValueHolderInterface valueHolder = null;
+ Object container = null;
+ if (attributeValue instanceof IndirectContainer) {
+ valueHolder = ((IndirectContainer) attributeValue).getValueHolder();
+ }
+ if (!buildDirectlyFromRow && unitOfWork.isOriginalNewObject(original)) {
+ // CR#3156435 Throw a meaningful exception if a serialized/dead value
+ // holder is detected.
+ // This can occur if an existing serialized object is attempt to be
+ // registered as new.
+ if (valueHolder instanceof DatabaseValueHolder && !((DatabaseValueHolder) valueHolder).isInstantiated()
+ && ((DatabaseValueHolder) valueHolder).getSession() == null
+ && !((DatabaseValueHolder) valueHolder).isSerializedRemoteUnitOfWorkValueHolder()) {
+ throw DescriptorException.attemptToRegisterDeadIndirection(original, getMapping());
+ }
+ if (getMapping().getRelationshipPartner() == null) {
+ container = getMapping().buildCloneForPartObject(attributeValue, original, clone, unitOfWork, false);
+ } else {
+ if (!(attributeValue instanceof IndirectContainer)) {
+ valueHolder = new ValueHolder(attributeValue);
+ }
+ AbstractRecord row = null;
+ if (valueHolder instanceof DatabaseValueHolder) {
+ row = ((DatabaseValueHolder) valueHolder).getRow();
+ }
+
+ // If a new object is being cloned then we must build a new
+ // UOWValueHolder
+ // this is so that new clones can also have their relationships managed
+ // here the code instantiates the valueholder in a privledged manner
+ // because a
+ // UOWValueHolder will assume the objects in the collection are existing
+ // if the valueholder
+ // Goes through it's own instantiation process.
+ UnitOfWorkValueHolder newValueHolder = getMapping().createUnitOfWorkValueHolder(valueHolder, original,
+ clone, row, unitOfWork, buildDirectlyFromRow);
+ container = buildIndirectContainer(newValueHolder);
+ Object cloneCollection = getMapping().buildCloneForPartObject(attributeValue, original, clone,
+ unitOfWork, false);
+ newValueHolder.privilegedSetValue(cloneCollection);
+ newValueHolder.setInstantiated();
+ }
+ } else {
+ if (!(attributeValue instanceof IndirectContainer)) {
+ valueHolder = new ValueHolder(attributeValue);
+ }
+ AbstractRecord row = null;
+ if (valueHolder instanceof DatabaseValueHolder) {
+ row = ((DatabaseValueHolder) valueHolder).getRow();
+ }
+ // TODO: This method is overriden solely to change a single line
+ // so it doesn't delegate value holder creation to the mapping which is a
+ // ForeignReferenceMapping that hard wires the value holder creation--no policy!
+ //
+ // container = buildIndirectContainer(getMapping().createUnitOfWorkValueHolder(valueHolder, original, clone,
+ // row, unitOfWork, buildDirectlyFromRow));
+ container = buildIndirectContainer(new EmfUnitOfWorkQueryBasedValueHolder(valueHolder, clone,
+ (ForeignReferenceMapping) getMapping(), row, unitOfWork));
+ }
+ if ((getMapping().getDescriptor().getObjectChangePolicy().isObjectChangeTrackingPolicy())
+ && (((ChangeTracker) clone)._persistence_getPropertyChangeListener() != null)
+ && (container instanceof CollectionChangeTracker)) {
+ ((CollectionChangeTracker) container).setTrackedAttributeName(getMapping().getAttributeName());
+ ((CollectionChangeTracker) container)._persistence_setPropertyChangeListener(((ChangeTracker) clone)
+ ._persistence_getPropertyChangeListener());
+ }
+ return container;
+ }
+
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfUnitOfWorkQueryBasedValueHolder.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfUnitOfWorkQueryBasedValueHolder.java
new file mode 100755
index 000000000..d4d480e80
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/EmfUnitOfWorkQueryBasedValueHolder.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import java.util.Collection;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.teneo.eclipselink.elistfactory.EListFactory;
+import org.eclipse.emf.teneo.eclipselink.internal.messages.Messages;
+import org.eclipse.persistence.exceptions.DatabaseException;
+import org.eclipse.persistence.indirection.ValueHolderInterface;
+import org.eclipse.persistence.internal.indirection.UnitOfWorkQueryValueHolder;
+import org.eclipse.persistence.internal.sessions.AbstractRecord;
+import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
+import org.eclipse.persistence.mappings.ForeignReferenceMapping;
+
+public class EmfUnitOfWorkQueryBasedValueHolder extends UnitOfWorkQueryValueHolder implements EmfOwnedValueHolder {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ private EObject owner;
+ private String ownerAttrName;
+
+ public EmfUnitOfWorkQueryBasedValueHolder(ValueHolderInterface attributeValue, Object clone,
+ ForeignReferenceMapping mapping, AbstractRecord row, UnitOfWorkImpl unitOfWork) {
+ super(attributeValue, clone, mapping, row, unitOfWork);
+ }
+
+ @Override
+ protected Object instantiate() throws DatabaseException {
+ return doInstantiate();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <E> Object doInstantiate() throws DatabaseException {
+ ValueHolderInterface wrappedVH = getWrappedValueHolder();
+ if (wrappedVH instanceof EmfQueryBasedValueHolder) {
+ EmfQueryBasedValueHolder emfVH = (EmfQueryBasedValueHolder) wrappedVH;
+ emfVH.setOwner(getOwner());
+ emfVH.setOwnerAttrName(getOwnerAttrName());
+ }
+ Collection<E> contents = (Collection<E>) super.instantiate();
+ EList<E> newList = null;
+ try {
+ newList = EListFactory.eINSTANCE.createEList(getOwner(), getOwnerAttrName());
+ EmfHelper.getInstance().setECollectionContents(contents, newList);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(Messages.exception_errorBuildingEListImplementation, e);
+ }
+ return newList;
+ }
+
+ public EObject getOwner() {
+ return owner;
+ }
+
+ public void setOwner(EObject owner) {
+ this.owner = owner;
+ }
+
+ public String getOwnerAttrName() {
+ return ownerAttrName;
+ }
+
+ public void setOwnerAttrName(String ownerAttrName) {
+ this.ownerAttrName = ownerAttrName;
+ }
+
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/IndirectEContainer.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/IndirectEContainer.java
new file mode 100755
index 000000000..d2055c00d
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/IndirectEContainer.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import org.eclipse.persistence.indirection.IndirectContainer;
+
+public interface IndirectEContainer<E> extends IndirectContainer {
+ /**
+ * Add and object to the EMF container without triggering notification or updating the owner.
+ *
+ * @param element
+ * @return boolean indicating success
+ */
+ boolean eAdd(E element);
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/Utils.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/Utils.java
new file mode 100755
index 000000000..6c5b8f196
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/Utils.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink;
+
+import java.lang.reflect.Field;
+
+import org.eclipse.persistence.exceptions.DescriptorException;
+import org.eclipse.persistence.internal.helper.ConversionManager;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
+
+public class Utils {
+
+ /**
+ * Sets the value of the instance variable in the object to the value.
+ *
+ * @param field
+ * TODO
+ */
+ public static void setAttributeValueInObject(Object anObject, Object value, Field field) throws DescriptorException {
+ String fieldName = field.getName();
+ try {
+ // PERF: Direct variable access.
+ PrivilegedAccessHelper.setValueInField(field, anObject, value);
+ } catch (IllegalArgumentException exception) {
+ throw DescriptorException.illegalArgumentWhileSettingValueThruInstanceVariableAccessor(fieldName, field
+ .getType().getName(), value, exception);
+ } catch (IllegalAccessException exception) {
+ throw DescriptorException.illegalAccessWhileSettingValueThruInstanceVariableAccessor(fieldName, anObject
+ .getClass().getName(), value, exception);
+ } catch (NullPointerException exception) {
+ try {
+ if (anObject != null) {
+ Class<?> fieldClass = field.getType();
+ if (Helper.isPrimitiveWrapper(fieldClass) && value == null) {
+ PrivilegedAccessHelper.setValueInField(field, anObject, ConversionManager.getDefaultManager()
+ .convertObject(new Integer(0), fieldClass));
+ } else {
+ throw DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(fieldName,
+ value, exception);
+ }
+ } else {
+ // Some JVM's throw this exception for some very odd reason
+ throw DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(fieldName,
+ value, exception);
+ }
+ } catch (IllegalAccessException accessException) {
+ throw DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(fieldName, value,
+ exception);
+ }
+ }
+ }
+
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/EListContainerPolicy.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/EListContainerPolicy.java
new file mode 100755
index 000000000..bd7ad2cb1
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/EListContainerPolicy.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.elist;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.common.util.BasicEMap.Entry;
+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.InternalEObject;
+import org.eclipse.emf.ecore.util.EObjectEList;
+import org.eclipse.emf.ecore.util.EcoreEMap;
+import org.eclipse.emf.teneo.eclipselink.EmfHelper;
+import org.eclipse.emf.teneo.eclipselink.IndirectEContainer;
+import org.eclipse.persistence.exceptions.QueryException;
+import org.eclipse.persistence.internal.queries.ListContainerPolicy;
+
+/**
+ * TODO should split EMap functionality out into EMapContainerPolicy
+ */
+public class EListContainerPolicy extends ListContainerPolicy {
+
+ private static final long serialVersionUID = 1L;
+
+ private String attributeName; // needed to construct appropriate container in
+
+ // clone
+
+ public EListContainerPolicy(String attributeName) {
+ super(EclipseLinkEList.class);
+ this.attributeName = attributeName;
+ }
+
+ @Override
+ protected boolean removeFrom(Object key, Object element, Object container) {
+ try {
+ if (container instanceof EcoreEMap<?, ?>) {
+ EcoreEMap<?, ?> map = (EcoreEMap<?, ?>) container;
+ map.basicRemove(element, null);
+ return true;
+ } else {
+ if (container instanceof BasicEList<?>) {
+ BasicEList<?> eList = (BasicEList<?>) container;
+ return EmfHelper.getInstance().removeFromEList(eList, element);
+ } else {
+ return ((Collection<?>) container).remove(element);
+ }
+ }
+ } catch (UnsupportedOperationException ex) {
+ throw QueryException.methodNotValid(element, "remove(Object element)"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * INTERNAL: Add element into a container which implements the Collection interface.
+ *
+ * @param element
+ * java.lang.Object
+ * @param container
+ * java.lang.Object
+ * @return boolean indicating whether the container changed
+ */
+ public boolean addInto(Object key, Object element, Object container) {
+ return doAddInto(key, element, container);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <E, K, V> boolean doAddInto(Object key, Object element, Object container) {
+ if (container instanceof IndirectEContainer<?>) {
+ IndirectEContainer<E> indirectEContainer = (IndirectEContainer<E>) container;
+ return indirectEContainer.eAdd((E) element);
+ } else {
+ if (container instanceof BasicEList<?>) {
+ return EmfHelper.getInstance().addToEList((EList<E>) container, (E) element);
+ } else {
+ return EmfHelper.getInstance().addToEMap((EMap<K, V>) container, (Map.Entry<K, V>) element);
+ }
+ }
+ }
+
+ @Override
+ public Object cloneFor(Object container) {
+ return doCloneFor(container);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <K, V> Object doCloneFor(Object container) {
+ EcoreEMap<K, V> containerClone = null;
+ EmfHelper helper = EmfHelper.getInstance();
+ if (container instanceof EcoreEMap<?, ?>) {
+ EcoreEMap<K, V> contentsMap = (EcoreEMap<K, V>) container;
+ BasicEList<Map.Entry<K, V>> delegateEList = helper.getEMapDelegateEList(contentsMap);
+ Class<?> entryClass = helper.getEcoreEMapEntryClass(contentsMap);
+ EClass entryEClass = helper.getEcoreEMapEntryEClass(contentsMap);
+ if (delegateEList instanceof EObjectEList<?>) {
+ EObjectEList<Map.Entry<K, V>> eobjectEList = (EObjectEList<Map.Entry<K, V>>) delegateEList;
+ InternalEObject owner = helper.getEObjectEListOwner(eobjectEList);
+ int featureID = helper.getEObjectEListFeatureId(eobjectEList);
+ containerClone = new EcoreEMap<K, V>(entryEClass, entryClass, owner, featureID);
+ helper.setEMapContents(contentsMap, containerClone);
+ } else {
+ BasicEList<Entry<K, V>> delegateListClone = (BasicEList<Entry<K, V>>) delegateEList.clone();
+ containerClone = new EcoreEMap<K, V>(entryEClass, entryClass, delegateListClone);
+ }
+ } else {
+ // EList container class may vary, need to lookup clone method.
+ // TODO cache clone methods by class
+ return invokeCloneMethodOn(getCloneMethod(container.getClass()), container);
+ }
+ return containerClone;
+ }
+
+ public String getAttributeName() {
+ return attributeName;
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/EclipseLinkEList.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/EclipseLinkEList.java
new file mode 100755
index 000000000..01454ba64
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/EclipseLinkEList.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.elist;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.emf.common.notify.NotificationChain;
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.InternalEList;
+import org.eclipse.emf.teneo.eclipselink.internal.messages.Messages;
+
+public class EclipseLinkEList<E> extends BasicEList<E> implements InternalEList.Unsettable<E>,
+ EStructuralFeature.Setting {
+
+ private static final long serialVersionUID = 1L;
+
+ public EclipseLinkEList() {
+ super();
+ }
+
+ public EclipseLinkEList(Collection<? extends E> collection) {
+ super(collection);
+ }
+
+ public EclipseLinkEList(int size, Object[] data) {
+ super(size, data);
+ }
+
+ public EclipseLinkEList(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ @Override
+ public ListIterator<E> basicListIterator(int index) {
+ return super.basicListIterator(index);
+ }
+
+ @Override
+ public Iterator<E> basicIterator() {
+ return super.basicIterator();
+ }
+
+ @Override
+ public ListIterator<E> basicListIterator() {
+ return super.basicListIterator();
+ }
+
+ @Override
+ public List<E> basicList() {
+ return super.basicList();
+ }
+
+ public NotificationChain basicRemove(Object object, NotificationChain notifications) {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public NotificationChain basicAdd(E object, NotificationChain notifications) {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public boolean isSet() {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public void unset() {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public Object get(boolean resolve) {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public EObject getEObject() {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public EStructuralFeature getEStructuralFeature() {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public void set(Object newValue) {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public boolean basicContains(Object arg0) {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public boolean basicContainsAll(Collection<?> arg0) {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public int basicIndexOf(Object arg0) {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public int basicLastIndexOf(Object arg0) {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public Object[] basicToArray() {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+
+ public <T> T[] basicToArray(T[] arg0) {
+ throw new RuntimeException(Messages.exception_unusableTemporaryPlaceholderClass);
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/IndirectEList.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/IndirectEList.java
new file mode 100755
index 000000000..634ca9259
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/elist/IndirectEList.java
@@ -0,0 +1,898 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.elist;
+
+import java.beans.PropertyChangeListener;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.emf.common.notify.NotificationChain;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.InternalEList;
+import org.eclipse.emf.teneo.eclipselink.EmfHelper;
+import org.eclipse.emf.teneo.eclipselink.IndirectEContainer;
+import org.eclipse.emf.teneo.eclipselink.internal.messages.Messages;
+import org.eclipse.persistence.indirection.ValueHolder;
+import org.eclipse.persistence.indirection.ValueHolderInterface;
+import org.eclipse.persistence.internal.helper.Helper;
+import org.eclipse.persistence.internal.indirection.UnitOfWorkQueryValueHolder;
+
+public class IndirectEList<E> extends ArrayList<E> implements IndirectEContainer<E>, InternalEList.Unsettable<E>,
+ EStructuralFeature.Setting {
+
+ private static final long serialVersionUID = 1L;
+
+ /** Reduce type casting. */
+ protected InternalEList<E> delegate;
+
+ /** Delegate indirection behavior to a value holder. */
+ protected ValueHolderInterface valueHolder;
+
+ /** Change tracking listener. */
+ private transient PropertyChangeListener changeListener;
+
+ /** The mapping attribute name, used to raise change events. */
+ private transient String attributeName;
+
+ /** Store initial size for lazy init. */
+ protected int initialCapacity = 10;
+
+ /**
+ * PUBLIC: Construct an empty IndirectEList so that its internal data array has size <tt>10</tt> and its standard
+ * capacity increment is zero.
+ */
+ public IndirectEList() {
+
+ this(10);
+ }
+
+ /**
+ * PUBLIC: Construct an empty IndirectEList with the specified initial capacity and with its capacity increment
+ * equal to zero.
+ *
+ * @param initialCapacity
+ * the initial capacity of the vector
+ * @exception IllegalArgumentException
+ * if the specified initial capacity is negative
+ */
+ public IndirectEList(int initialCapacity) {
+
+ this(initialCapacity, 0);
+ }
+
+ /**
+ * PUBLIC: Construct an empty IndirectEList with the specified initial capacity and capacity increment.
+ *
+ * @param initialCapacity
+ * the initial capacity of the vector
+ * @param capacityIncrement
+ * the amount by which the capacity is increased when the vector overflows
+ * @exception IllegalArgumentException
+ * if the specified initial capacity is negative
+ */
+ public IndirectEList(int initialCapacity, int capacityIncrement) {
+
+ super(0);
+ this.initialize(initialCapacity, capacityIncrement);
+ }
+
+ /**
+ * PUBLIC: Construct an IndirectEList containing the elements of the specified collection, in the order they are
+ * returned by the collection's iterator.
+ *
+ * @param collection
+ * a collection containing the elements to construct this IndirectEList with.
+ */
+ public IndirectEList(Collection<? extends E> collection) {
+
+ super(0);
+ this.initialize(collection);
+ }
+
+ /**
+ * @see java.util.Vector#add(int, E)
+ */
+ @Override
+ public void add(int index, E element) {
+
+ this.getDelegate().add(index, element);
+ raiseAddChangeEvent(element);
+ }
+
+ /**
+ * Raise the add change event and relationship maintainence.
+ */
+ protected void raiseAddChangeEvent(E element) {
+ if (hasBeenRegistered()) {
+ ((UnitOfWorkQueryValueHolder) getValueHolder()).updateForeignReferenceSet(element, null);
+ }
+ }
+
+ /**
+ * Raise the remove change event.
+ */
+ protected void raiseRemoveChangeEvent(Object object) {
+ if (hasBeenRegistered()) {
+ ((UnitOfWorkQueryValueHolder) getValueHolder()).updateForeignReferenceRemove(object);
+ }
+ }
+
+ /**
+ * @see java.util.Vector#add(E)
+ */
+ @Override
+ public synchronized boolean add(E element) {
+
+ this.getDelegate().add(element);
+ raiseAddChangeEvent(element);
+ return true;
+ }
+
+ /**
+ * @see java.util.Vector#addAll(int, java.util.Collection)
+ */
+ @Override
+ public synchronized boolean addAll(int index, Collection<? extends E> collection) {
+
+ Iterator<? extends E> elements = collection.iterator();
+ // Must trigger add events if tracked or uow.
+ if (hasBeenRegistered() || hasEclipseLinkPropertyChangeListener()) {
+ while (elements.hasNext()) {
+ this.add(index, elements.next());
+ index++;
+ }
+ return true;
+ }
+
+ return this.getDelegate().addAll(index, collection);
+
+ }
+
+ /**
+ * @see java.util.Vector#addAll(java.util.Collection)
+ */
+ @Override
+ public synchronized boolean addAll(Collection<? extends E> collection) {
+
+ // Must trigger add events if tracked or uow.
+ if (hasBeenRegistered() || hasEclipseLinkPropertyChangeListener()) {
+ Iterator<? extends E> elements = collection.iterator();
+ while (elements.hasNext()) {
+ this.add(elements.next());
+ }
+ return true;
+ }
+
+ return getDelegate().addAll(collection);
+ }
+
+ /**
+ * @see java.util.Vector#addElement(E)
+ */
+ public synchronized void addElement(E element) {
+
+ this.add(element);
+ }
+
+ /**
+ * PUBLIC: Return the freshly-built delegate.
+ */
+ @SuppressWarnings("unchecked")
+ protected InternalEList<E> buildDelegate() {
+
+ return (InternalEList<E>) getValueHolder().getValue();
+ }
+
+ /**
+ * @see java.util.Vector#clear()
+ */
+ @Override
+ public void clear() {
+
+ if (hasBeenRegistered() || hasEclipseLinkPropertyChangeListener()) {
+ Iterator<E> elements = this.iterator();
+ while (elements.hasNext()) {
+ E element = elements.next();
+ elements.remove();
+ this.raiseRemoveChangeEvent(element);
+ }
+ } else {
+ this.getDelegate().clear();
+ }
+ }
+
+ /**
+ * PUBLIC:
+ *
+ * @see java.util.Vector#clone() This will result in a database query if necessary.
+ */
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public synchronized Object clone() {
+
+ IndirectEList<E> result = (IndirectEList<E>) super.clone();
+ try {
+ Method cloneMethod = this.getDelegate().getClass().getMethod("clone", new Class[] {}); //$NON-NLS-1$
+ result.delegate = (InternalEList<E>) cloneMethod.invoke(this.getDelegate(), new Object[] {});
+ } catch (Exception e) {
+ throw new RuntimeException(Messages.exception_errorInvokingCloneOnDelegate, e);
+ }
+ result.attributeName = null;
+ result.changeListener = null;
+ return result;
+ }
+
+ /**
+ * PUBLIC:
+ *
+ * @see java.util.List#contains(java.lang.Object)
+ */
+ @Override
+ public boolean contains(Object object) {
+
+ return this.getDelegate().contains(object);
+ }
+
+ /**
+ * @see java.util.List#containsAll(java.util.Collection)
+ */
+ @Override
+ public synchronized boolean containsAll(Collection<?> c) {
+
+ return this.getDelegate().containsAll(c);
+ }
+
+ /**
+ * @see java.util.List#equals(E)
+ */
+ @Override
+ public synchronized boolean equals(Object object) {
+
+ return this.getDelegate().equals(object);
+ }
+
+ /**
+ * @see java.util.List#get(int)
+ */
+ @Override
+ public synchronized E get(int index) {
+
+ return this.getDelegate().get(index);
+ }
+
+ /**
+ * PUBLIC: Check whether the contents have been read from the database. If they have not, read them and set the
+ * delegate.
+ */
+ protected synchronized InternalEList<E> getDelegate() {
+
+ if (delegate == null) {
+ delegate = this.buildDelegate();
+ }
+ return delegate;
+ }
+
+ /**
+ * PUBLIC: Return the valueHolder.
+ */
+ public synchronized ValueHolderInterface getValueHolder() {
+
+ if (valueHolder == null) {
+ valueHolder = new ValueHolder(new EclipseLinkEList<E>(this.initialCapacity));
+ }
+ return valueHolder;
+ }
+
+ /**
+ * INTERNAL: return whether this IndirectEList has been registered with the UnitOfWork
+ */
+ public boolean hasBeenRegistered() {
+
+ return getValueHolder() instanceof UnitOfWorkQueryValueHolder;
+ }
+
+ /**
+ * INTERNAL:
+ *
+ * @see java.util.Vector#hashCode()
+ */
+ @Override
+ public synchronized int hashCode() {
+
+ return this.getDelegate().hashCode();
+ }
+
+ /**
+ * @see java.util.Vector#indexOf(java.lang.Object)
+ */
+ @Override
+ public int indexOf(Object object) {
+
+ return this.getDelegate().indexOf(object);
+ }
+
+ /**
+ * Initialize the instance.
+ */
+ protected void initialize(int initialCapacity, int capacityIncrement) {
+
+ this.initialCapacity = initialCapacity;
+ this.delegate = null;
+ this.valueHolder = null;
+ }
+
+ /**
+ * Initialize the instance.
+ */
+ protected void initialize(Collection<? extends E> collection) {
+
+ this.delegate = null;
+ EclipseLinkEList<E> temp = new EclipseLinkEList<E>(collection);
+ this.valueHolder = new ValueHolder(temp);
+ }
+
+ /**
+ * @see java.util.Vector#isEmpty()
+ */
+ @Override
+ public boolean isEmpty() {
+
+ return this.getDelegate().isEmpty();
+ }
+
+ /**
+ * PUBLIC: Return whether the contents have been read from the database.
+ */
+ public boolean isInstantiated() {
+
+ return this.getValueHolder().isInstantiated();
+ }
+
+ /**
+ * @see java.util.AbstractList#iterator()
+ */
+ @Override
+ public Iterator<E> iterator() {
+
+ // Must wrap the interator to raise the remove event.
+ return new Iterator<E>() {
+
+ Iterator<E> delegateIterator = IndirectEList.this.getDelegate().iterator();
+ E currentElement;
+
+ public boolean hasNext() {
+
+ return this.delegateIterator.hasNext();
+ }
+
+ public E next() {
+
+ this.currentElement = this.delegateIterator.next();
+ return this.currentElement;
+ }
+
+ public void remove() {
+
+ this.delegateIterator.remove();
+ IndirectEList.this.raiseRemoveChangeEvent(this.currentElement);
+ }
+ };
+ }
+
+ /**
+ * @see java.util.Vector#lastIndexOf(java.lang.Object)
+ */
+ @Override
+ public int lastIndexOf(Object object) {
+
+ return this.getDelegate().lastIndexOf(object);
+ }
+
+ /**
+ * @see java.util.AbstractList#listIterator()
+ */
+ @Override
+ public ListIterator<E> listIterator() {
+
+ return this.listIterator(0);
+ }
+
+ /**
+ * @see java.util.AbstractList#listIterator(int)
+ */
+ @Override
+ public ListIterator<E> listIterator(final int index) {
+
+ // Must wrap the interator to raise the remove event.
+ return new ListIterator<E>() {
+
+ ListIterator<E> delegateIterator = IndirectEList.this.getDelegate().listIterator(index);
+ E currentElement;
+
+ public boolean hasNext() {
+
+ return this.delegateIterator.hasNext();
+ }
+
+ public boolean hasPrevious() {
+
+ return this.delegateIterator.hasPrevious();
+ }
+
+ public int previousIndex() {
+
+ return this.delegateIterator.previousIndex();
+ }
+
+ public int nextIndex() {
+
+ return this.delegateIterator.nextIndex();
+ }
+
+ public E next() {
+
+ this.currentElement = this.delegateIterator.next();
+ return this.currentElement;
+ }
+
+ public E previous() {
+
+ this.currentElement = this.delegateIterator.previous();
+ return this.currentElement;
+ }
+
+ public void remove() {
+
+ this.delegateIterator.remove();
+ IndirectEList.this.raiseRemoveChangeEvent(this.currentElement);
+ }
+
+ public void set(E element) {
+
+ this.delegateIterator.set(element);
+ IndirectEList.this.raiseRemoveChangeEvent(this.currentElement);
+ IndirectEList.this.raiseAddChangeEvent(element);
+ }
+
+ public void add(E element) {
+
+ this.delegateIterator.add(element);
+ IndirectEList.this.raiseAddChangeEvent(element);
+ }
+ };
+ }
+
+ /**
+ * @see java.util.Vector#remove(int)
+ */
+ @Override
+ public synchronized E remove(int index) {
+
+ E value = this.getDelegate().remove(index);
+ this.raiseRemoveChangeEvent(value);
+ return value;
+ }
+
+ /**
+ * @see java.util.Vector#remove(java.lang.Object)
+ */
+ @Override
+ public boolean remove(Object object) {
+
+ if (this.getDelegate().remove(object)) {
+ this.raiseRemoveChangeEvent(object);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @see java.util.Vector#removeAll(java.util.Collection)
+ */
+ @Override
+ public synchronized boolean removeAll(Collection<?> collection) {
+
+ // Must trigger remove events if tracked or uow.
+ if (hasBeenRegistered() || hasEclipseLinkPropertyChangeListener()) {
+ Iterator<?> objects = collection.iterator();
+ while (objects.hasNext()) {
+ this.remove(objects.next());
+ }
+ return true;
+ }
+ return this.getDelegate().removeAll(collection);
+ }
+
+ /**
+ * @see java.util.Vector#removeElement(E)
+ */
+ public synchronized boolean removeElement(E obj) {
+
+ return this.remove(obj);
+ }
+
+ /**
+ * @see java.util.Vector#removeElementAt(int)
+ */
+ public synchronized void removeElementAt(int index) {
+
+ this.remove(index);
+ }
+
+ /**
+ * @see java.util.Vector#retainAll(java.util.Collection)
+ */
+ @Override
+ public synchronized boolean retainAll(Collection<?> collection) {
+
+ // Must trigger remove events if tracked or uow.
+ if (hasBeenRegistered() || hasEclipseLinkPropertyChangeListener()) {
+ Iterator<E> elements = getDelegate().iterator();
+ while (elements.hasNext()) {
+ E element = elements.next();
+ if (!collection.contains(element)) {
+ elements.remove();
+ this.raiseRemoveChangeEvent(element);
+ }
+ }
+ return true;
+ }
+ return this.getDelegate().retainAll(collection);
+ }
+
+ /**
+ * @see java.util.Vector#set(int, E)
+ */
+ @Override
+ public synchronized E set(int index, E element) {
+
+ E oldValue = this.getDelegate().set(index, element);
+ this.raiseRemoveChangeEvent(oldValue);
+ this.raiseAddChangeEvent(element);
+ return oldValue;
+ }
+
+ /**
+ * @see java.util.Vector#setElementAt(E, int)
+ */
+ public synchronized void setElementAt(E obj, int index) {
+
+ this.set(index, obj);
+ }
+
+ // /**
+ // * Since the value holder may be instantiated by the mapping and not the
+ // policy
+ // * @see ForeignReferenceMapping#createUnitOfWorkValueHolder
+ // * @see TransparentIndirectionPolicy#cloneAttribute
+ // * we may receive a UnitOfWorkQueryValueHolder which we have to
+ // * switch with an EmfQueryBasedValueHolder
+ // */
+ // public void setValueHolder(ValueHolderInterface valueHolder) {
+ // if (valueHolder instanceof UnitOfWorkQueryValueHolder) {
+ // UnitOfWorkQueryValueHolder uowValueHolder =
+ // (UnitOfWorkQueryValueHolder)valueHolder;
+ // this.valueHolder = new EmfQueryBasedValueHolder(uowValueHolder);
+ // } else {
+ // this.valueHolder = valueHolder;
+ // }
+ // this.delegate = null;
+ // }
+ //
+
+ public void setValueHolder(ValueHolderInterface valueHolder) {
+
+ this.valueHolder = valueHolder;
+ this.delegate = null;
+ }
+
+ /**
+ * @see java.util.Vector#size()
+ */
+ @Override
+ public int size() {
+
+ return this.getDelegate().size();
+ }
+
+ /**
+ * @see java.util.Vector#subList(int, int)
+ */
+ @Override
+ public List<E> subList(int fromIndex, int toIndex) {
+
+ return this.getDelegate().subList(fromIndex, toIndex);
+ }
+
+ /**
+ * @see java.util.Vector#toArray()
+ */
+ @Override
+ public synchronized Object[] toArray() {
+
+ return this.getDelegate().toArray();
+ }
+
+ /**
+ * @see java.util.Vector#toArray(java.lang.Object[])
+ */
+ @Override
+ public synchronized <T> T[] toArray(T[] a) {
+
+ return this.getDelegate().toArray(a);
+ }
+
+ /**
+ * PUBLIC: Use the java.util.Vector#toString(); but wrap it with braces to indicate there is a bit of indirection.
+ * Don't allow this method to trigger a database read.
+ *
+ * @see java.util.Vector#toString()
+ */
+ @Override
+ public String toString() {
+
+ if (ValueHolderInterface.shouldToStringInstantiate) {
+ return this.getDelegate().toString();
+ }
+ if (this.isInstantiated()) {
+ return "{" + this.getDelegate().toString() + "}"; //$NON-NLS-1$ //$NON-NLS-2$
+ } else {
+ return "{" + Helper.getShortClassName(this.getClass()) + ": not instantiated}"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ /**
+ * Return the property change listener for change tracking.
+ */
+ public PropertyChangeListener getEclipseLinkPropertyChangeListener() {
+
+ return changeListener;
+ }
+
+ /**
+ * Return if the collection has a property change listener for change tracking.
+ */
+ public boolean hasEclipseLinkPropertyChangeListener() {
+
+ return this.changeListener != null;
+ }
+
+ /**
+ * Set the property change listener for change tracking.
+ */
+ public void setEclipseLinkPropertyChangeListener(PropertyChangeListener changeListener) {
+
+ this.changeListener = changeListener;
+ }
+
+ /**
+ * Return the mapping attribute name, used to raise change events.
+ */
+ public String getEclipseLinkAttributeName() {
+
+ return attributeName;
+ }
+
+ /**
+ * Set the mapping attribute name, used to raise change events. This is required if the change listener is set.
+ */
+ public void setEclipseLinkAttributeName(String attributeName) {
+
+ this.attributeName = attributeName;
+ }
+
+ public void move(int newPosition, E element) {
+
+ this.getDelegate().move(newPosition, element);
+ this.raiseRemoveChangeEvent(element);
+ this.raiseAddChangeEvent(element);
+ }
+
+ public E move(int newPosition, int oldPosition) {
+
+ E element = this.getDelegate().move(newPosition, oldPosition);
+ this.raiseRemoveChangeEvent(element);
+ this.raiseAddChangeEvent(element);
+ return element;
+ }
+
+ public NotificationChain basicAdd(E element, NotificationChain notifications) {
+
+ this.getDelegate().basicAdd(element, notifications);
+ raiseAddChangeEvent(element);
+ return notifications;
+ }
+
+ public NotificationChain basicRemove(Object object, NotificationChain notifications) {
+
+ this.getDelegate().basicRemove(object, notifications);
+ raiseRemoveChangeEvent(object);
+ return notifications;
+ }
+
+ public Iterator<E> basicIterator() {
+
+ // Must wrap the interator to raise the remove event.
+ return new Iterator<E>() {
+
+ Iterator<E> delegateIterator = IndirectEList.this.getDelegate().basicIterator();
+ E currentElement;
+
+ public boolean hasNext() {
+
+ return this.delegateIterator.hasNext();
+ }
+
+ public E next() {
+
+ this.currentElement = this.delegateIterator.next();
+ return this.currentElement;
+ }
+
+ public void remove() {
+
+ this.delegateIterator.remove();
+ IndirectEList.this.raiseRemoveChangeEvent(this.currentElement);
+ }
+ };
+ }
+
+ public List<E> basicList() {
+
+ return this.getDelegate().basicList();
+ }
+
+ public ListIterator<E> basicListIterator() {
+
+ return this.getDelegate().basicListIterator();
+ }
+
+ public ListIterator<E> basicListIterator(int index) {
+
+ return this.getDelegate().basicListIterator(index);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEList#addUnique(int, E)
+ */
+ public void addUnique(int index, E element) {
+
+ this.getDelegate().addUnique(index, element);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEList#addUnique(E)
+ */
+ public void addUnique(E element) {
+
+ this.getDelegate().addUnique(element);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEList#basicGet(int)
+ */
+ public E basicGet(int index) {
+
+ return this.getDelegate().basicGet(index);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEList#setUnique(int, E)
+ */
+ public E setUnique(int index, E element) {
+
+ return this.getDelegate().setUnique(index, element);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.ArrayList#ensureCapacity(int)
+ */
+ @Override
+ public void ensureCapacity(int minCapacity) {
+
+ // InternalELists don't support ensureCapacity so no-op
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.ArrayList#trimToSize()
+ */
+ @Override
+ public void trimToSize() {
+
+ // InternalELists don't support trimToSize so no-op
+ }
+
+ public boolean eAdd(E element) {
+
+ InternalEList<E> delegate = this.getDelegate();
+ return EmfHelper.getInstance().addToEList(delegate, element);
+ }
+
+ public boolean isSet() {
+
+ return ((InternalEList.Unsettable<E>) this.getDelegate()).isSet();
+ }
+
+ public void unset() {
+
+ ((InternalEList.Unsettable<E>) this.getDelegate()).unset();
+ }
+
+ public EObject getEObject() {
+
+ return ((EStructuralFeature.Setting) this.getDelegate()).getEObject();
+ }
+
+ public EStructuralFeature getEStructuralFeature() {
+
+ return ((EStructuralFeature.Setting) this.getDelegate()).getEStructuralFeature();
+ }
+
+ public Object get(boolean resolve) {
+
+ return ((EStructuralFeature.Setting) this.getDelegate()).get(resolve);
+ }
+
+ public void set(Object newValue) {
+
+ ((EStructuralFeature.Setting) this.getDelegate()).set(newValue);
+ }
+
+ public boolean addAllUnique(Collection<? extends E> arg0) {
+ return this.getDelegate().addAllUnique(arg0);
+ }
+
+ public boolean addAllUnique(int arg0, Collection<? extends E> arg1) {
+ return this.getDelegate().addAllUnique(arg0, arg1);
+ }
+
+ public boolean basicContains(Object arg0) {
+ return this.getDelegate().basicContains(arg0);
+ }
+
+ public boolean basicContainsAll(Collection<?> arg0) {
+ return this.getDelegate().basicContainsAll(arg0);
+ }
+
+ public int basicIndexOf(Object arg0) {
+ return this.getDelegate().basicIndexOf(arg0);
+ }
+
+ public int basicLastIndexOf(Object arg0) {
+ return this.getDelegate().basicIndexOf(arg0);
+ }
+
+ public Object[] basicToArray() {
+ return this.getDelegate().basicToArray();
+ }
+
+ public <T> T[] basicToArray(T[] arg0) {
+ return this.getDelegate().basicToArray(arg0);
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/EMapContainerPolicy.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/EMapContainerPolicy.java
new file mode 100755
index 000000000..cf25beeb6
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/EMapContainerPolicy.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.emap;
+
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.eclipse.emf.common.util.BasicEMap;
+import org.eclipse.emf.common.util.EMap;
+import org.eclipse.persistence.exceptions.QueryException;
+import org.eclipse.persistence.internal.queries.CollectionContainerPolicy;
+import org.eclipse.persistence.internal.queries.ContainerPolicy;
+import org.eclipse.persistence.internal.queries.MapContainerPolicy;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.sessions.Session;
+
+/**
+ * <p>
+ * <b>Purpose</b>: A MapContainerPolicy is ContainerPolicy whose container class implements the Map interface.
+ * <p>
+ * <p>
+ * <b>Responsibilities</b>: Provide the functionality to operate on an instance of a Map.
+ *
+ * @see MapContainerPolicy
+ * @see ContainerPolicy
+ * @see CollectionContainerPolicy
+ */
+public class EMapContainerPolicy extends MapContainerPolicy {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * INTERNAL: Construct a new policy.
+ */
+ public EMapContainerPolicy() {
+ super(BasicEMap.class);
+ }
+
+ /**
+ * INTERNAL: Construct a new policy for the specified class.
+ */
+ public EMapContainerPolicy(Class<?> containerClass) {
+ super(containerClass);
+ }
+
+ /**
+ * INTERNAL: Construct a new policy for the specified class name.
+ */
+ public EMapContainerPolicy(String containerClassName) {
+ super(containerClassName);
+ }
+
+ @Override
+ public Class<?> getInterfaceType() {
+ return EMap.class;
+ }
+
+ /**
+ * INTERNAL: Return the size of container.
+ */
+ @Override
+ public int sizeFor(Object container) {
+ return ((EMap<?, ?>) container).size();
+ }
+
+ /**
+ * INTERNAL: Validate the container type.
+ */
+ @Override
+ public boolean isValidContainer(Object container) {
+ // PERF: Use instanceof which is inlined, not isAssignable which is very
+ // inefficent.
+ return container instanceof EMap<?, ?>;
+ }
+
+ /**
+ * INTERNAL: Remove element from container which implements the Map interface.
+ */
+ public boolean removeFromWithIdentity(Object element, Object container, Session session) {
+ boolean found = false;
+ Vector<Object> knownKeys = new Vector<Object>(1);
+ try {
+ Iterator<?> iterator = ((EMap<?, ?>) container).keySet().iterator();
+ while (iterator.hasNext()) {
+ Object key = iterator.next();
+ if (((EMap<?, ?>) container).get(key) == element) {
+ knownKeys.addElement(key);
+ found = true;
+ }
+ }
+ if (found) {
+ for (int index = 0; index < knownKeys.size(); ++index) {
+ ((EMap<?, ?>) container).remove(knownKeys.elementAt(index));
+ }
+ }
+ return found;
+ } catch (UnsupportedOperationException ex) {
+ throw QueryException.methodNotValid(container, "remove(Object element)"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * INTERNAL: Remove element from container which implements the Map interface.
+ */
+ @Override
+ public boolean removeFrom(Object key, Object element, Object container, AbstractSession session) {
+ try {
+ Object returnValue = null;
+ if (key != null) {
+ returnValue = ((EMap<?, ?>) container).remove(key);
+ } else {
+ returnValue = ((EMap<?, ?>) container).remove(keyFrom(element, session));
+ }
+ if (returnValue == null) {
+ return false;
+ } else {
+ return true;
+ }
+ } catch (UnsupportedOperationException ex) {
+ throw QueryException.methodNotValid(container, "remove(Object element)"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * INTERNAL: Return an Iterator for the given container.
+ */
+ @Override
+ public Object iteratorFor(Object container) {
+ return ((EMap<?, ?>) container).values().iterator();
+ }
+
+ /**
+ * INTERNAL: Return the true if element exists in container.
+ *
+ * @return boolean true if container 'contains' element
+ */
+ @Override
+ protected boolean contains(Object element, Object container) {
+ return ((EMap<?, ?>) container).containsValue(element);
+ }
+
+ /**
+ * INTERNAL: Remove all the elements from container.
+ */
+ @Override
+ public void clear(Object container) {
+ try {
+ ((EMap<?, ?>) container).clear();
+ } catch (UnsupportedOperationException ex) {
+ throw QueryException.methodNotValid(container, "clear()"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * INTERNAL: Add element into container which implements the Map interface.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean addInto(Object key, Object element, Object container, AbstractSession session) {
+ Object wrapped = element;
+ if (hasElementDescriptor()) {
+ wrapped = getElementDescriptor().getObjectBuilder().wrapObject(element, session);
+ }
+ try {
+ if (key != null) {
+ return ((EMap<Object, Object>) container).put(key, wrapped) != null;
+ } else {
+ return ((EMap<Object, Object>) container).put(keyFrom(element, session), wrapped) != null;
+ }
+ } catch (ClassCastException ex1) {
+ throw QueryException.mapKeyNotComparable(element, container);
+ }
+ }
+} \ No newline at end of file
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/EclipseLinkEMap.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/EclipseLinkEMap.java
new file mode 100755
index 000000000..b6599f892
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/EclipseLinkEMap.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.emap;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.emf.common.util.EMap;
+import org.eclipse.emf.teneo.eclipselink.elist.EclipseLinkEList;
+import org.eclipse.emf.teneo.eclipselink.internal.messages.Messages;
+
+public class EclipseLinkEMap<K, V> extends EclipseLinkEList<Map.Entry<K, V>> implements EMap<K, V> {
+
+ private static final long serialVersionUID = 1L;
+
+ public EclipseLinkEMap() {
+ super();
+ }
+
+ public EclipseLinkEMap(Collection<? extends Map.Entry<K, V>> collection) {
+ super(collection);
+ }
+
+ public EclipseLinkEMap(int size, Object[] data) {
+ super(size, data);
+ }
+
+ public EclipseLinkEMap(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ public V get(Object key) {
+ throw new UnsupportedOperationException(Messages.exception_getNotImplemented);
+ }
+
+ public V put(Object key, Object value) {
+ throw new UnsupportedOperationException(Messages.exception_putNotImplemented);
+ }
+
+ public void putAll(Map<? extends K, ? extends V> map) {
+ throw new UnsupportedOperationException(Messages.exception_putAllNotImplemented);
+ }
+
+ public void putAll(EMap<? extends K, ? extends V> map) {
+ throw new UnsupportedOperationException(Messages.exception_putAllNotImplemented);
+ }
+
+ public int indexOfKey(Object key) {
+ throw new UnsupportedOperationException(Messages.exception_indexOfKeyNotImplemented);
+ }
+
+ public boolean containsKey(Object key) {
+ throw new UnsupportedOperationException(Messages.exception_containsKeyNotImplemented);
+ }
+
+ public boolean containsValue(Object value) {
+ throw new UnsupportedOperationException(Messages.exception_containsValueNotImplemented);
+ }
+
+ public V removeKey(Object key) {
+ throw new UnsupportedOperationException(Messages.exception_removeKeyNotImplemented);
+ }
+
+ public Map<K, V> map() {
+ throw new UnsupportedOperationException(Messages.exception_mapNotImplemented);
+ }
+
+ /**
+ * returns the list contents (of Entries) as a set
+ */
+ public Set<Map.Entry<K, V>> entrySet() {
+ return new HashSet<Map.Entry<K, V>>(this);
+ }
+
+ public Set<K> keySet() {
+ throw new UnsupportedOperationException(Messages.exception_keySetNotImplmplemented);
+ }
+
+ public Collection<V> values() {
+ throw new UnsupportedOperationException(Messages.exception_valuesNotImplemented);
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/IndirectEMap.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/IndirectEMap.java
new file mode 100755
index 000000000..7c550076a
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/emap/IndirectEMap.java
@@ -0,0 +1,1246 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.emap;
+
+import java.beans.PropertyChangeListener;
+import java.lang.reflect.Method;
+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.eclipse.emf.common.notify.NotificationChain;
+import org.eclipse.emf.common.util.BasicEMap;
+import org.eclipse.emf.common.util.EMap;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.InternalEList;
+import org.eclipse.emf.teneo.eclipselink.EmfHelper;
+import org.eclipse.emf.teneo.eclipselink.IndirectEContainer;
+import org.eclipse.emf.teneo.eclipselink.internal.messages.Messages;
+import org.eclipse.persistence.indirection.ValueHolder;
+import org.eclipse.persistence.indirection.ValueHolderInterface;
+import org.eclipse.persistence.internal.helper.Helper;
+
+public class IndirectEMap<K, V> extends BasicEMap<K, V> implements IndirectEContainer<Map.Entry<K, V>>,
+ InternalEList.Unsettable<Map.Entry<K, V>>, EStructuralFeature.Setting {
+
+ private static final long serialVersionUID = 1L;
+
+ /** Reduce type casting */
+ protected EMap<K, V> delegate;
+
+ /** Delegate indirection behavior to a value holder */
+ protected ValueHolderInterface valueHolder;
+
+ /** Change tracking listener. */
+ private transient PropertyChangeListener changeListener;
+
+ /** The mapping attribute name, used to raise change events. */
+ private transient String attributeName;
+
+ /** Store initial size for lazy init. */
+ protected int initialCapacity = 11;
+
+ /** Store load factor for lazy init. */
+ protected float loadFactor = 0.75f;
+
+ /**
+ * PUBLIC: Construct a new, empty EmfIndirectEMap with a default capacity and load factor.
+ */
+ public IndirectEMap() {
+
+ this(11);
+ }
+
+ /**
+ * PUBLIC: Construct a new, empty EmfIndirectEMap with the specified initial capacity and default load factor.
+ *
+ * @param initialCapacity
+ * the initial capacity of the hashtable
+ */
+ public IndirectEMap(int initialCapacity) {
+
+ this(initialCapacity, 0.75f);
+ }
+
+ /**
+ * PUBLIC: Construct a new, empty EmfIndirectEMap with the specified initial capacity and load factor.
+ *
+ * @param initialCapacity
+ * the initial capacity of the hashtable
+ * @param loadFactor
+ * a number between 0.0 and 1.0
+ * @exception IllegalArgumentException
+ * if the initial capacity is less than or equal to zero, or if the load factor is less than or equal
+ * to zero
+ */
+ public IndirectEMap(int initialCapacity, float loadFactor) {
+
+ super(0);
+ this.initialize(initialCapacity, loadFactor);
+ }
+
+ /**
+ * PUBLIC: Construct a new EmfIndirectEMap with the same mappings as the given Map. The EmfIndirectEMap is created
+ * with a capacity of twice the number of entries in the given Map or 11 (whichever is greater), and a default load
+ * factor, which is 0.75.
+ *
+ * @param map
+ * a map containing the mappings to use
+ */
+ public IndirectEMap(Map<K, V> map) {
+
+ super(0);
+ this.initialize(map);
+ }
+
+ /**
+ * Used during postBuild/Merge/Clone to replace generic EclipseLinkEMap with the correct EMF collection class
+ *
+ * @param eMap
+ */
+ public void setDelegate(EMap<K, V> eMap) {
+ getValueHolder().setValue(eMap);
+ delegate = eMap;
+ }
+
+ /**
+ * Return the freshly-built delegate.
+ */
+ @SuppressWarnings("unchecked")
+ protected EMap<K, V> buildDelegate() {
+
+ return (EMap<K, V>) getValueHolder().getValue();
+ }
+
+ /**
+ * @see java.util.Hashtable#clear()
+ */
+ @Override
+ public synchronized void clear() {
+
+ if (hasEclipseLinkPropertyChangeListener()) {
+ Iterator<K> objects = this.keySet().iterator();
+ while (objects.hasNext()) {
+ Object object = objects.next();
+ objects.remove();
+ this.raiseRemoveChangeEvent(object, this.get(object));
+ }
+ } else {
+ this.getDelegate().clear();
+ }
+ }
+
+ /**
+ * @see java.util.Hashtable#clone() This will result in a database query if necessary.
+ */
+
+ /*
+ * There are 3 situations when clone() is called: 1. The developer actually wants to clone the collection (typically
+ * to modify one of the 2 resulting collections). In which case the contents must be read from the database. 2. A
+ * UnitOfWork needs a clone (or backup clone) of the collection. But the UnitOfWork checks "instantiation" before
+ * cloning collections ("un-instantiated" collections are not cloned). 3. A MergeManager needs an extra copy of the
+ * collection (because the "backup" and "target" are the same object?). But the MergeManager checks "instantiation"
+ * before merging collections (again, "un-instantiated" collections are not merged).
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public synchronized Object clone() {
+
+ IndirectEMap<K, V> result = (IndirectEMap<K, V>) super.clone();
+ try {
+ Method cloneMethod = this.getDelegate().getClass().getMethod("clone", new Class[] {}); //$NON-NLS-1$
+ result.delegate = (EMap<K, V>) cloneMethod.invoke(this.getDelegate(), new Object[] {});
+ } catch (Exception e) {
+ throw new RuntimeException(Messages.exception_errorInvokingCloneOnDelegate, e);
+ }
+ return result;
+ }
+
+ /**
+ * @see java.util.Hashtable#contains(java.lang.Object)
+ */
+ @Override
+ public boolean contains(Object value) {
+
+ return this.getDelegate().contains(value);
+ }
+
+ /**
+ * @see java.util.Hashtable#containsKey(java.lang.Object)
+ */
+ @Override
+ public boolean containsKey(Object key) {
+
+ return this.getDelegate().containsKey(key);
+ }
+
+ /**
+ * @see java.util.Hashtable#containsValue(java.lang.Object)
+ */
+ @Override
+ public boolean containsValue(Object value) {
+
+ return this.getDelegate().containsValue(value);
+ }
+
+ /**
+ * @see java.util.Hashtable#entrySet()
+ */
+ @Override
+ public Set<Map.Entry<K, V>> entrySet() {
+
+ return new Set<Map.Entry<K, V>>() {
+
+ Set<Map.Entry<K, V>> delegateSet = IndirectEMap.this.getDelegate().entrySet();
+
+ public int size() {
+
+ return this.delegateSet.size();
+ }
+
+ public boolean isEmpty() {
+
+ return this.delegateSet.isEmpty();
+ }
+
+ public boolean contains(Object o) {
+
+ return this.delegateSet.contains(o);
+ }
+
+ public Iterator<Map.Entry<K, V>> iterator() {
+
+ return new Iterator<Map.Entry<K, V>>() {
+
+ Iterator<Map.Entry<K, V>> delegateIterator = delegateSet.iterator();
+ Map.Entry<K, V> currentEntry;
+
+ public boolean hasNext() {
+
+ return this.delegateIterator.hasNext();
+ }
+
+ public Map.Entry<K, V> next() {
+
+ this.currentEntry = this.delegateIterator.next();
+ return this.currentEntry;
+ }
+
+ public void remove() {
+
+ raiseRemoveChangeEvent(currentEntry.getKey(), currentEntry.getValue());
+ this.delegateIterator.remove();
+ }
+ };
+ }
+
+ public Object[] toArray() {
+
+ return this.delegateSet.toArray();
+ }
+
+ public <T> T[] toArray(T a[]) {
+
+ return this.delegateSet.toArray(a);
+ }
+
+ public boolean add(Map.Entry<K, V> entry) {
+
+ return this.delegateSet.add(entry);
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean remove(Object object) {
+
+ if (!(object instanceof Map.Entry<?, ?>)) {
+ return false;
+ }
+ return IndirectEMap.this.remove(((Map.Entry<K, V>) object).getKey());
+ }
+
+ public boolean containsAll(Collection<?> collection) {
+
+ return this.delegateSet.containsAll(collection);
+ }
+
+ public boolean addAll(Collection<? extends Map.Entry<K, V>> collection) {
+
+ return this.delegateSet.addAll(collection);
+ }
+
+ public boolean retainAll(Collection<?> collection) {
+
+ boolean result = false;
+ Iterator<Map.Entry<K, V>> entries = delegateSet.iterator();
+ while (entries.hasNext()) {
+ Map.Entry<K, V> entry = entries.next();
+ if (!collection.contains(entry)) {
+ entries.remove();
+ raiseRemoveChangeEvent(entry.getKey(), entry.getValue());
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public boolean removeAll(Collection<?> collection) {
+
+ boolean result = false;
+ for (Object object : collection) {
+ if (!(object instanceof Map.Entry)) {
+ continue;
+ }
+ Object removed = IndirectEMap.this.remove(((Map.Entry<K, V>) object).getKey());
+ if (removed != null) {
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ public void clear() {
+
+ IndirectEMap.this.clear();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ return this.delegateSet.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+
+ return this.delegateSet.hashCode();
+ }
+ };
+ }
+
+ /**
+ * @see java.util.Hashtable#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object o) {
+
+ return this.getDelegate().equals(o);
+ }
+
+ /**
+ * @see java.util.Hashtable#get(java.lang.Object)
+ */
+ @Override
+ public V get(Object key) {
+
+ return this.getDelegate().get(key);
+ }
+
+ /**
+ * Check whether the contents have been read from the database. If they have not, read them and set the delegate.
+ */
+ protected synchronized EMap<K, V> getDelegate() {
+
+ if (delegate == null) {
+ delegate = this.buildDelegate();
+ }
+ return delegate;
+ }
+
+ /**
+ * Return the mapping attribute name, used to raise change events.
+ */
+ public String getEclipseLinkAttributeName() {
+
+ return attributeName;
+ }
+
+ /**
+ * Return the property change listener for change tracking.
+ */
+ public PropertyChangeListener getEclipseLinkPropertyChangeListener() {
+
+ return changeListener;
+ }
+
+ /**
+ * PUBLIC: Return the valueHolder.
+ */
+ public synchronized ValueHolderInterface getValueHolder() {
+
+ // PERF: lazy initialize value holder and vector as are normally set after
+ // creation.
+ if (valueHolder == null) {
+ valueHolder = new ValueHolder(new EclipseLinkEMap<K, V>(initialCapacity));
+ }
+ return valueHolder;
+ }
+
+ /**
+ * @see java.util.Hashtable#hashCode()
+ */
+ @Override
+ public int hashCode() {
+
+ return this.getDelegate().hashCode();
+ }
+
+ /**
+ * Return if the collection has a property change listener for change tracking.
+ */
+ public boolean hasEclipseLinkPropertyChangeListener() {
+
+ return this.changeListener != null;
+ }
+
+ /**
+ * Initialize the instance.
+ */
+ protected void initialize(int initialCapacity, float loadFactor) {
+
+ this.delegate = null;
+ this.loadFactor = loadFactor;
+ this.initialCapacity = initialCapacity;
+ this.valueHolder = null;
+ }
+
+ /**
+ * Initialize the instance.
+ */
+ protected void initialize(Map<K, V> map) {
+
+ this.delegate = null;
+ BasicEMap<K, V> temp = new BasicEMap<K, V>(map);
+
+ this.valueHolder = new ValueHolder(temp);
+ }
+
+ /**
+ * @see java.util.Hashtable#isEmpty()
+ */
+ @Override
+ public boolean isEmpty() {
+
+ return this.getDelegate().isEmpty();
+ }
+
+ /**
+ * PUBLIC: Return whether the contents have been read from the database.
+ */
+ public boolean isInstantiated() {
+
+ return this.getValueHolder().isInstantiated();
+ }
+
+ /**
+ * @see java.util.Hashtable#keySet()
+ */
+ @Override
+ public Set<K> keySet() {
+
+ return new Set<K>() {
+
+ Set<K> delegateSet = IndirectEMap.this.getDelegate().keySet();
+
+ public int size() {
+
+ return this.delegateSet.size();
+ }
+
+ public boolean isEmpty() {
+
+ return this.delegateSet.isEmpty();
+ }
+
+ public boolean contains(Object o) {
+
+ return this.delegateSet.contains(o);
+ }
+
+ public Iterator<K> iterator() {
+
+ return new Iterator<K>() {
+
+ Iterator<K> delegateIterator = delegateSet.iterator();
+ K currentKey;
+
+ public boolean hasNext() {
+
+ return this.delegateIterator.hasNext();
+ }
+
+ public K next() {
+
+ this.currentKey = this.delegateIterator.next();
+ return this.currentKey;
+ }
+
+ public void remove() {
+
+ IndirectEMap.this.raiseRemoveChangeEvent(currentKey, IndirectEMap.this.getDelegate().get(
+ currentKey));
+ this.delegateIterator.remove();
+ }
+ };
+ }
+
+ public Object[] toArray() {
+
+ return this.delegateSet.toArray();
+ }
+
+ public <T> T[] toArray(T a[]) {
+
+ return this.delegateSet.toArray(a);
+ }
+
+ public boolean add(K key) {
+
+ return this.delegateSet.add(key);
+ }
+
+ public boolean remove(Object object) {
+
+ return IndirectEMap.this.remove(object);
+ }
+
+ public boolean containsAll(Collection<?> collection) {
+
+ return this.delegateSet.containsAll(collection);
+ }
+
+ public boolean addAll(Collection<? extends K> collection) {
+
+ return this.delegateSet.addAll(collection);
+ }
+
+ public boolean retainAll(Collection<?> collection) {
+
+ boolean result = false;
+ Iterator<K> keys = delegateSet.iterator();
+ while (keys.hasNext()) {
+ K key = keys.next();
+ if (!collection.contains(key)) {
+ keys.remove();
+ IndirectEMap.this.raiseRemoveChangeEvent(key, IndirectEMap.this.getDelegate().get(key));
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ public boolean removeAll(Collection<?> collection) {
+
+ boolean result = false;
+ for (Object object : collection) {
+ if (IndirectEMap.this.remove(object)) {
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ public void clear() {
+
+ IndirectEMap.this.clear();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ return this.delegateSet.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+
+ return this.delegateSet.hashCode();
+ }
+ };
+
+ }
+
+ /**
+ * @see java.util.Hashtable#put(java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public synchronized V put(K key, V value) {
+
+ V oldValue = this.getDelegate().put(key, value);
+ if (oldValue != null) {
+ raiseRemoveChangeEvent(key, oldValue);
+ }
+ raiseAddChangeEvent(key, value);
+ return oldValue;
+ }
+
+ @Override
+ public synchronized void putAll(Map<? extends K, ? extends V> map) {
+
+ // Must trigger add events if tracked or uow.
+ if (hasEclipseLinkPropertyChangeListener()) {
+ Iterator<? extends K> objects = map.keySet().iterator();
+ while (objects.hasNext()) {
+ K key = objects.next();
+ this.put(key, map.get(key));
+ }
+ } else {
+ this.getDelegate().putAll(map);
+ }
+ }
+
+ /**
+ * @see java.util.Hashtable#rehash()
+ */
+ protected void rehash() {
+
+ throw new UnsupportedOperationException(Messages.exception_rehashNotImplemented);
+ }
+
+ /**
+ * Raise the add change event and relationship maintainence.
+ */
+ protected void raiseAddChangeEvent(Object key, Object value) {
+ // this is where relationship maintenance would go
+ }
+
+ /**
+ * Raise the remove change event.
+ */
+ protected void raiseRemoveChangeEvent(Object key, Object value) {
+ // this is where relationship maintenance would go
+ }
+
+ @Override
+ public Map.Entry<K, V> remove(int index) {
+
+ Map.Entry<K, V> removed = this.getDelegate().remove(index);
+ if (removed != null) {
+ raiseRemoveChangeEvent(null, removed);
+ }
+ return removed;
+ }
+
+ @Override
+ public boolean remove(Object object) {
+
+ boolean removed = this.getDelegate().remove(object);
+ if (removed) {
+ raiseRemoveChangeEvent(null, object);
+ }
+ return removed;
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> collection) {
+
+ boolean removed = this.getDelegate().removeAll(collection);
+ if (removed) {
+ // TODO raise correct events
+ raiseRemoveChangeEvent(null, null);
+ }
+ return removed;
+ }
+
+ @Override
+ public V removeKey(Object key) {
+
+ V removed = this.getDelegate().removeKey(key);
+ if (removed != null) {
+ raiseRemoveChangeEvent(key, removed);
+ }
+ return removed;
+ }
+
+ /**
+ * Set the mapping attribute name, used to raise change events. This is required if the change listener is set.
+ */
+ public void setEclipseLinkAttributeName(String attributeName) {
+
+ this.attributeName = attributeName;
+ }
+
+ /**
+ * Set the property change listener for change tracking.
+ */
+ public void setEclipseLinkPropertyChangeListener(PropertyChangeListener changeListener) {
+
+ this.changeListener = changeListener;
+ }
+
+ /**
+ * PUBLIC: Set the value holder.
+ */
+ public void setValueHolder(ValueHolderInterface valueHolder) {
+
+ this.delegate = null;
+ this.valueHolder = valueHolder;
+ }
+
+ /**
+ * @see java.util.Hashtable#size()
+ */
+ @Override
+ public int size() {
+
+ return this.getDelegate().size();
+ }
+
+ /**
+ * PUBLIC: Use the Hashtable.toString(); but wrap it with braces to indicate there is a bit of indirection. Don't
+ * allow this method to trigger a database read.
+ *
+ * @see java.util.Hashtable#toString()
+ */
+ @Override
+ public String toString() {
+
+ if (ValueHolderInterface.shouldToStringInstantiate) {
+ return this.getDelegate().toString();
+ }
+ if (this.isInstantiated()) {
+ return "{" + this.getDelegate().toString() + "}"; //$NON-NLS-1$ //$NON-NLS-2$
+ } else {
+ return "{" + Helper.getShortClassName(this.getClass()) + ": not instantiated}"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ @Override
+ public Iterator<Map.Entry<K, V>> iterator() {
+
+ // Must wrap the interator to raise the remove event.
+ return new Iterator<Map.Entry<K, V>>() {
+
+ Iterator<Map.Entry<K, V>> delegateIterator = IndirectEMap.this.getDelegate().iterator();
+ Map.Entry<K, V> currentObject;
+
+ public boolean hasNext() {
+
+ return this.delegateIterator.hasNext();
+ }
+
+ public Map.Entry<K, V> next() {
+
+ this.currentObject = this.delegateIterator.next();
+ return this.currentObject;
+ }
+
+ public void remove() {
+
+ this.delegateIterator.remove();
+ Map.Entry<K, V> currentEntry = this.currentObject;
+ IndirectEMap.this.raiseRemoveChangeEvent(currentEntry.getKey(), currentEntry.getValue());
+ }
+ };
+ }
+
+ /**
+ * @see java.util.Hashtable#values()
+ */
+ @Override
+ public Collection<V> values() {
+
+ return new Collection<V>() {
+
+ protected Collection<V> delegateCollection = IndirectEMap.this.getDelegate().values();
+
+ public int size() {
+
+ return delegateCollection.size();
+ }
+
+ public boolean isEmpty() {
+
+ return delegateCollection.isEmpty();
+ }
+
+ public boolean contains(Object o) {
+
+ return delegateCollection.contains(o);
+ }
+
+ public Iterator<V> iterator() {
+
+ return new Iterator<V>() {
+
+ Iterator<V> delegateIterator = delegateCollection.iterator();
+ V currentValue;
+
+ public boolean hasNext() {
+
+ return this.delegateIterator.hasNext();
+ }
+
+ public V next() {
+
+ this.currentValue = this.delegateIterator.next();
+ return this.currentValue;
+ }
+
+ public void remove() {
+
+ Iterator<Map.Entry<K, V>> iterator = IndirectEMap.this.getDelegate().entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<K, V> entry = iterator.next();
+ if (entry.getValue().equals(currentValue)) {
+ IndirectEMap.this.raiseRemoveChangeEvent(entry.getKey(), entry.getValue());
+ }
+
+ }
+ this.delegateIterator.remove();
+ }
+ };
+ }
+
+ public Object[] toArray() {
+
+ return this.delegateCollection.toArray();
+ }
+
+ public <T> T[] toArray(T a[]) {
+
+ return this.delegateCollection.toArray(a);
+ }
+
+ public boolean add(V value) {
+
+ return this.delegateCollection.add(value);
+ }
+
+ public boolean remove(Object object) {
+
+ Iterator<Map.Entry<K, V>> iterator = IndirectEMap.this.getDelegate().entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<K, V> entry = iterator.next();
+ if (entry.getValue().equals(object)) {
+ IndirectEMap.this.raiseRemoveChangeEvent(entry.getKey(), entry.getValue());
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean containsAll(Collection<?> collection) {
+
+ return this.delegateCollection.containsAll(collection);
+ }
+
+ public boolean addAll(Collection<? extends V> collection) {
+
+ return this.delegateCollection.addAll(collection);
+ }
+
+ public boolean removeAll(Collection<?> collection) {
+
+ boolean result = false;
+ for (Object name : collection) {
+ if (remove(name)) {
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ public boolean retainAll(Collection<?> collection) {
+
+ boolean result = false;
+ for (Iterator<Map.Entry<K, V>> iterator = IndirectEMap.this.entrySet().iterator(); iterator.hasNext();) {
+ Map.Entry<K, V> entry = iterator.next();
+ if (!collection.contains(entry.getValue())) {
+ iterator.remove();
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ public void clear() {
+
+ IndirectEMap.this.clear();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ return this.delegateCollection.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+
+ return this.delegateCollection.hashCode();
+ }
+
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#add(int, java.lang.Object)
+ */
+ @Override
+ public void add(int index, Map.Entry<K, V> object) {
+
+ this.getDelegate().add(index, object);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#add(java.lang.Object)
+ */
+ @Override
+ public boolean add(Map.Entry<K, V> object) {
+
+ return this.getDelegate().add(object);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#addAll(java.util.Collection)
+ */
+ @Override
+ public boolean addAll(Collection<? extends Map.Entry<K, V>> collection) {
+
+ return this.getDelegate().addAll(collection);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#addAll(int, java.util.Collection)
+ */
+ @Override
+ public boolean addAll(int index, Collection<? extends Map.Entry<K, V>> collection) {
+
+ return this.getDelegate().addAll(index, collection);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#containsAll(java.util.Collection)
+ */
+ @Override
+ public boolean containsAll(Collection<?> collection) {
+
+ return this.getDelegate().containsAll(collection);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#get(int)
+ */
+ @Override
+ public Entry<K, V> get(int index) {
+
+ return (Entry<K, V>) this.getDelegate().get(index);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#indexOf(java.lang.Object)
+ */
+ @Override
+ public int indexOf(Object object) {
+
+ return this.getDelegate().indexOf(object);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#indexOfKey(java.lang.Object)
+ */
+ @Override
+ public int indexOfKey(Object key) {
+
+ return this.getDelegate().indexOfKey(key);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#lastIndexOf(java.lang.Object)
+ */
+ @Override
+ public int lastIndexOf(Object object) {
+
+ return this.getDelegate().lastIndexOf(object);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#listIterator()
+ */
+ @Override
+ public ListIterator<Map.Entry<K, V>> listIterator() {
+
+ return this.getDelegate().listIterator();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#listIterator(int)
+ */
+ @Override
+ public ListIterator<Map.Entry<K, V>> listIterator(int index) {
+
+ return this.getDelegate().listIterator(index);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#map()
+ */
+ @Override
+ public Map<K, V> map() {
+
+ return this.getDelegate().map();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#move(int, int)
+ */
+ @Override
+ public Map.Entry<K, V> move(int targetIndex, int sourceIndex) {
+
+ return this.getDelegate().move(targetIndex, sourceIndex);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#move(int, java.lang.Object)
+ */
+ @Override
+ public void move(int index, Map.Entry<K, V> object) {
+
+ this.getDelegate().move(index, object);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#putAll(org.eclipse.emf.common.util.EMap)
+ */
+ @Override
+ public void putAll(EMap<? extends K, ? extends V> map) {
+
+ this.getDelegate().putAll(map);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#retainAll(java.util.Collection)
+ */
+ @Override
+ public boolean retainAll(Collection<?> collection) {
+
+ return this.getDelegate().retainAll(collection);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#set(int, java.lang.Object)
+ */
+ @Override
+ public Map.Entry<K, V> set(int index, Map.Entry<K, V> object) {
+
+ return this.getDelegate().set(index, object);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#subList(int, int)
+ */
+ @Override
+ public List<Map.Entry<K, V>> subList(int start, int end) {
+
+ return this.getDelegate().subList(start, end);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#toArray()
+ */
+ @Override
+ public Object[] toArray() {
+
+ return this.getDelegate().toArray();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.emf.common.util.BasicEMap#toArray(java.lang.Object[])
+ */
+ @Override
+ public <T> T[] toArray(T[] array) {
+
+ return this.getDelegate().toArray(array);
+ }
+
+ public boolean eAdd(Map.Entry<K, V> entry) {
+
+ EMap<K, V> delegate = this.getDelegate();
+ return EmfHelper.getInstance().addToEMap(delegate, entry);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void addUnique(Map.Entry<K, V> object) {
+
+ ((InternalEList<Map.Entry<K, V>>) this.getDelegate()).addUnique(object);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void addUnique(int index, Map.Entry<K, V> object) {
+
+ ((InternalEList<Map.Entry<K, V>>) this.getDelegate()).addUnique(index, object);
+ }
+
+ @SuppressWarnings("unchecked")
+ public NotificationChain basicAdd(Map.Entry<K, V> object, NotificationChain notifications) {
+
+ return ((InternalEList<Map.Entry<K, V>>) this.getDelegate()).basicAdd(object, notifications);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map.Entry<K, V> basicGet(int index) {
+
+ return ((InternalEList<Map.Entry<K, V>>) this.getDelegate()).basicGet(index);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Iterator<Map.Entry<K, V>> basicIterator() {
+
+ return ((InternalEList<Map.Entry<K, V>>) this.getDelegate()).basicIterator();
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<Map.Entry<K, V>> basicList() {
+
+ return ((InternalEList<Map.Entry<K, V>>) this.getDelegate()).basicList();
+ }
+
+ @SuppressWarnings("unchecked")
+ public ListIterator<Map.Entry<K, V>> basicListIterator() {
+
+ return ((InternalEList<Map.Entry<K, V>>) this.getDelegate()).basicListIterator();
+ }
+
+ @SuppressWarnings("unchecked")
+ public ListIterator<Map.Entry<K, V>> basicListIterator(int index) {
+
+ return ((InternalEList<Map.Entry<K, V>>) this.getDelegate()).basicListIterator(index);
+ }
+
+ @SuppressWarnings("unchecked")
+ public NotificationChain basicRemove(Object object, NotificationChain notifications) {
+
+ return ((InternalEList<Map.Entry<K, V>>) this.getDelegate()).basicRemove(object, notifications);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map.Entry<K, V> setUnique(int index, Map.Entry<K, V> object) {
+
+ return ((InternalEList<Map.Entry<K, V>>) this.getDelegate()).setUnique(index, object);
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean isSet() {
+
+ return ((InternalEList.Unsettable<Map.Entry<K, V>>) this.getDelegate()).isSet();
+ }
+
+ @SuppressWarnings("unchecked")
+ public void unset() {
+
+ ((InternalEList.Unsettable<Map.Entry<K, V>>) this.getDelegate()).unset();
+ }
+
+ public EObject getEObject() {
+
+ return ((EStructuralFeature.Setting) this.getDelegate()).getEObject();
+ }
+
+ public EStructuralFeature getEStructuralFeature() {
+
+ return ((EStructuralFeature.Setting) this.getDelegate()).getEStructuralFeature();
+ }
+
+ public Object get(boolean resolve) {
+
+ return ((EStructuralFeature.Setting) this.getDelegate()).get(resolve);
+ }
+
+ public void set(Object newValue) {
+
+ ((EStructuralFeature.Setting) this.getDelegate()).set(newValue);
+ }
+
+ public boolean addAllUnique(Collection<? extends java.util.Map.Entry<K, V>> collection) {
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean addAllUnique(int index, Collection<? extends java.util.Map.Entry<K, V>> collection) {
+ return ((InternalEList.Unsettable<Map.Entry<K, V>>) getDelegate()).addAllUnique(index, collection);
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean basicContains(Object object) {
+ return ((InternalEList.Unsettable<Map.Entry<K, V>>) getDelegate()).basicContains(object);
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean basicContainsAll(Collection<?> collection) {
+ return ((InternalEList.Unsettable<Map.Entry<K, V>>) getDelegate()).basicContainsAll(collection);
+ }
+
+ @SuppressWarnings("unchecked")
+ public int basicIndexOf(Object object) {
+ return ((InternalEList.Unsettable<Map.Entry<K, V>>) getDelegate()).basicIndexOf(object);
+ }
+
+ @SuppressWarnings("unchecked")
+ public int basicLastIndexOf(Object object) {
+ return ((InternalEList.Unsettable<Map.Entry<K, V>>) getDelegate()).basicLastIndexOf(object);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object[] basicToArray() {
+ return ((InternalEList.Unsettable<Map.Entry<K, V>>) getDelegate()).basicToArray();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T[] basicToArray(T[] array) {
+ return ((InternalEList.Unsettable<Map.Entry<K, V>>) getDelegate()).basicToArray(array);
+ }
+
+} \ No newline at end of file
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/internal/messages/Messages.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/internal/messages/Messages.java
new file mode 100755
index 000000000..cd56c7d3b
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/internal/messages/Messages.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.internal.messages;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.emf.teneo.eclipselink.internal.messages.messages"; //$NON-NLS-1$
+ public static String assert_cannotSaveUnloadedResource;
+ public static String assert_classloaderMustHavePersistenceUnit$0OnClasspath;
+ public static String assert_invalidObjectId;
+ public static String assert_unableToCreateEntityManangerFactoryforPersistenceUnit$0;
+ public static String assert_uriMustContainContentsQueryString;
+ public static String assert_uriMustContainEclipseLinkScheme;
+ public static String assert_uriMustContainPersistenceUnitSegment;
+ public static String exception_cannotSetOwnerOnEList;
+ public static String exception_collectionClassNotSupported$0;
+ public static String exception_containsKeyNotImplemented;
+ public static String exception_containsValueNotImplemented;
+ public static String exception_errorAddingElementToEList;
+ public static String exception_errorAddingEntryToEMap;
+ public static String exception_errorBuildingEListImplementation;
+ public static String exception_errorDeletingEMapEntries;
+ public static String exception_errorGettingDelegateList;
+ public static String exception_errorGettingEntryEClass;
+ public static String exception_errorGettingFeatureID;
+ public static String exception_errorGettingListOwner;
+ public static String exception_errorInvokingCloneOnDelegate;
+ public static String exception_errorRemovingElementFromEList;
+ public static String exception_errorSettingEListContents$0;
+ public static String exception_errorSettingEMapContents;
+ public static String exception_errorSettingListOwner;
+ public static String exception_getNotImplemented;
+ public static String exception_indexOfKeyNotImplemented;
+ public static String exception_keySetNotImplmplemented;
+ public static String exception_mapNotImplemented;
+ public static String exception_putAllNotImplemented;
+ public static String exception_putNotImplemented;
+ public static String exception_rehashNotImplemented;
+ public static String exception_removeKeyNotImplemented;
+ public static String exception_unableToFindDelegateEListFieldOnBasicEMap;
+ public static String exception_unusableTemporaryPlaceholderClass;
+ public static String exception_valuesNotImplemented;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/internal/messages/messages.properties b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/internal/messages/messages.properties
new file mode 100755
index 000000000..660a3d577
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/internal/messages/messages.properties
@@ -0,0 +1,47 @@
+# ====================================================================
+# To code developer:
+# Do NOT change the properties between this line and the
+# "%%% END OF TRANSLATED PROPERTIES %%%" line.
+# Make a new property name, append to the end of the file and change
+# the code to use the new property.
+# ====================================================================
+
+# ====================================================================
+# %%% END OF TRANSLATED PROPERTIES %%%
+# ====================================================================
+
+assert_cannotSaveUnloadedResource=Cannot save a resource which is not loaded.
+assert_classloaderMustHavePersistenceUnit$0OnClasspath=Persistence unit property "PersistenceUnitProperties.CLASSLOADER" must be initialized with some classloader which has persistence unit named "{0}" on its classpath.
+assert_invalidObjectId=Invalid object id (expected format: ownerTypeName|idAttributeName="id").
+assert_unableToCreateEntityManangerFactoryforPersistenceUnit$0=Unable to create entity manager factory for persistence unit named "{0}".
+assert_uriMustContainContentsQueryString=Argument for parameter "uri" must contain a query string representing an alias of a query for the contents of this resource.
+assert_uriMustContainEclipseLinkScheme=Argument for parameter "uri" must contain an "eclipselink:" scheme.
+assert_uriMustContainPersistenceUnitSegment=Argument for parameter "uri" must contain one segment representing the name of a Persistence Unit.
+exception_cannotSetOwnerOnEList=Cannot set owner on Elist.
+exception_collectionClassNotSupported$0=Collection class not supported: {0}
+exception_containsKeyNotImplemented=EclipseLinkEMap\#containsKey() not implemented.
+exception_containsValueNotImplemented=EclipseLinkEMap\#containsValue() not implemented.
+exception_errorAddingElementToEList=Error while adding element to EList.
+exception_errorAddingEntryToEMap=Error while adding to EMap.
+exception_errorBuildingEListImplementation=Error while building correct EList implementation.
+exception_errorDeletingEMapEntries=Error while deleting EMap Entries.
+exception_errorGettingDelegateList=Error while getting delegate list.
+exception_errorGettingEntryEClass=Error while getting entryEClass.
+exception_errorGettingFeatureID=Error while getting featureID.
+exception_errorGettingListOwner=Error while getting list owner.
+exception_errorInvokingCloneOnDelegate=Error while invoking clone method on delegate.
+exception_errorRemovingElementFromEList=Error while removing element from EList.
+exception_errorSettingEListContents$0=Error while setting EList contents: unsupported collection class: {0}
+exception_errorSettingEMapContents=Error while setting EMap contents.
+exception_errorSettingListOwner=Error while setting list owner.
+exception_getNotImplemented=EclipseLinkEMap\#get() not implemented
+exception_indexOfKeyNotImplemented=EclipseLinkEMap\#indexOfKey() not implemented.
+exception_keySetNotImplmplemented=EclipseLinkEMap\#keySet() not implemented.
+exception_mapNotImplemented=EclipseLinkEMap\#map() not implemented.
+exception_putAllNotImplemented=EclipseLinkEMap\#putAll() not implemented.
+exception_putNotImplemented=EclipseLinkEMap\#put() not implemented
+exception_rehashNotImplemented=IndirectEMap\#rehash() not implemented.
+exception_removeKeyNotImplemented=EclipseLinkEMap\#removeKey() not implemented.
+exception_unableToFindDelegateEListFieldOnBasicEMap=Unable to find delegateEList field on BasicEMap.
+exception_unusableTemporaryPlaceholderClass=EclipseLinkEList is a temporary placeholder; the usage of this class implies a bug in Teneo.
+exception_valuesNotImplemented=EclipseLinkEMap\#values() not implemented.
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResource.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResource.java
new file mode 100755
index 000000000..fdd34a066
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResource.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.resource;
+
+import java.util.Map;
+
+import org.eclipse.emf.ecore.resource.Resource;
+
+/**
+ * This interface extends EMF's {@link Resource} interface and can be used to fine-tune the behavior of EclipseLink
+ * resources when loading or saving contents from or to a relational database.
+ */
+public interface EclipseLinkResource extends Resource {
+
+ /**
+ * Returns the map of options that, in addition to the overriding options specified during load, are used to to
+ * control load behavior.
+ */
+ Map<Object, Object> getDefaultLoadOptions();
+
+ /**
+ * Returns the map of options that, in addition to the overriding options specified during save, are used to to
+ * control save behavior.
+ */
+ Map<Object, Object> getDefaultSaveOptions();
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResourceFactoryImpl.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResourceFactoryImpl.java
new file mode 100755
index 000000000..6ae7a6d5a
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResourceFactoryImpl.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.resource;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.impl.ResourceFactoryImpl;
+
+public class EclipseLinkResourceFactoryImpl extends ResourceFactoryImpl {
+
+ public EclipseLinkResourceFactoryImpl() {
+ }
+
+ @Override
+ public Resource createResource(URI uri) {
+ return new EclipseLinkResourceImpl(uri);
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResourceImpl.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResourceImpl.java
new file mode 100755
index 000000000..45d43a53b
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkResourceImpl.java
@@ -0,0 +1,634 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.common.util.WrappedException;
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.InternalEObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+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.EcoreUtil.ContentTreeIterator;
+import org.eclipse.emf.teneo.eclipselink.IndirectEContainer;
+import org.eclipse.emf.teneo.eclipselink.internal.messages.Messages;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.persistence.config.PersistenceUnitProperties;
+import org.eclipse.persistence.jpa.osgi.PersistenceProvider;
+
+/**
+ * <p>
+ * TODO Replace session configuration with persistence unit
+ * </p>
+ * <p>
+ * TODO Double check relevance of database session and unit of work and their destruction or release
+ * </p>
+ * <p>
+ * TODO Check relevance of session customizers and pre-login listeners
+ * </p>
+ * This class implements the {@link EclipseLinkResource} interface and represents an EclipseLink resource.
+ * <p>
+ * EclipseLink resources provide a link between modeling using EMF and persistence using relational databases and
+ * EclipseLink O/R mapping. They are implementations of EMF's intrinsic persistence API and can be used to load, work
+ * on, and save EMF models in a relational database according to a given EclipseLink O/R mapping definition and a
+ * EclipseLink session configuration. As such, EclipseLink resources offer the following benefits:
+ * </p>
+ * <ul>
+ * <li>Database persistence for EMF models through same API as XML/XMI file persistence</li>
+ * <li>Fully tree-oriented handling of models during load and save</li>
+ * <li>Automatic registration/deletion of objects being added/removed in EMF model in underlying EclipseLink unit of
+ * work</li>
+ * <li>Support of cross-resource references and lazy loading across EclipseLink and XML/XMI resources</li>
+ * </ul>
+ * <p>
+ * However, several features of regular EMF resources do not apply here, notably the input and output streams handed
+ * along during loading and saving, and the support for zip files. The usage of tracking modification is discouraged
+ * even for XML/XMI resources and therefore has been disabled completely.
+ * </p>
+ * <p>
+ * A EclipseLink resource typically contains a certain subset of objects stored in the underlying database. It is
+ * identified by the URI passed to or created by its constructor. This URI must or will be a EclipseLink URI and
+ * indicates an optional database login, a database session declared in the EclipseLink sessions configuration
+ * (sessions.xml), and a query returning the objects which constitute the contents of the EclipseLink resource.
+ * </p>
+ * <p>
+ * Technically, a EclipseLink resource encapsulates a EclipseLink database session and unit of work. Both are created
+ * lazily either when the EclipseLink resource is loaded or when it is filled with contents. Their destruction or
+ * release respectively takes place during unlaod. The same database session may be shared among multiple EclipseLink
+ * resources but each EclipseLink resource will have an individual unit of work. In order to guarantee a consistent
+ * lifecycle management of database session(s) and unit(s) of work, applications have to make sure that EclipseLink
+ * resources are always explicitly unloaded when they are no longer needed.
+ * </p>
+ * <p>
+ * EclipseLink supports the customization of database sessions for all kinds of purposes. In the context of EMF, this is
+ * typically required for O/R mapping of EMF enumerators which are not covered by EclipseLink's built-in set of
+ * enumeration types. However, as the database session is encapsulated inside the EclipseLink resource it should not and
+ * typically never will be accessed directly through the application. Therefore, applications have to provide a session
+ * pre-login listener and register it with EclipseLink through the EclipseLink sessions configuration. This pre-login
+ * listener is invoked by EclipseLink when a EclipseLink resource is loaded or created and filled with contents and
+ * gives an oppertunity to do session customizations according to application-specific requirements.
+ * </p>
+ */
+public class EclipseLinkResourceImpl extends ResourceImpl implements EclipseLinkResource {
+
+ protected class EntityManagerFactoryInstance {
+
+ private EntityManagerFactory entityManagerFactory;
+ private int resourceInstanceCount;
+
+ public EntityManagerFactoryInstance(EntityManagerFactory entityManagerFactory) {
+
+ this.entityManagerFactory = entityManagerFactory;
+ resourceInstanceCount = 0;
+ }
+
+ public EntityManagerFactory getEntityManagerFactory() {
+
+ return entityManagerFactory;
+ }
+
+ public int getResourceInstanceCount() {
+
+ return resourceInstanceCount;
+ }
+
+ public void setResourceInstanceCount(int resourceInstanceCount) {
+
+ this.resourceInstanceCount = resourceInstanceCount;
+ }
+ }
+
+ private static Map<String, EntityManagerFactoryInstance> persistenceUnitNameToEntityManagerFactoryInstanceMap = new WeakHashMap<String, EntityManagerFactoryInstance>();
+
+ private String persistenceUnitName;
+ private String contentsQuery;
+ private EntityManager entityManager;
+ private boolean readingContentsFromDatabase;
+ private List<EObject> eObjectsToBeRemovedFromDatabase = new ArrayList<EObject>();
+
+ /**
+ * Creates an instance with the given {@link org.eclipse.emf.common.util.URI URI}.
+ * <p>
+ * The {@link org.eclipse.emf.common.util.URI URI} must be a EclipseLink {@link org.eclipse.emf.common.util.URI URI}
+ * . EclipseLink {@link org.eclipse.emf.common.util.URI URI}s must have the following format:
+ * </p>
+ * <ul>
+ * <code>eclipselink://loginAlias/sessionName?contentsQueryAlias</code>
+ * </ul>
+ * <p>
+ * and comprise the following components:
+ * </p>
+ * <ul>
+ * <li>A protocol which is always <code><b>eclipselink:</b></code></li>
+ * <li>An optional authority which is an <b>alias for</b> a <b>database login</b> to be used. It refers to a
+ * {@link org.eclipse.persistence.sessions.DatabaseLogin DatabaseLogin} instance which has been made available
+ * through the {@link EclipseLinkSettingsRegistry}. When omitted, the database login specified within the
+ * EclipseLink session configuration will be taken as a default.</li>
+ * <li>A segment representing the <b>name of</b> the applicable <b>database session</b> declared in the EclipseLink
+ * sessions configuration. The sessions configuration is expected to be provided in a file on the application's
+ * classpath, and therefore doesn't need to be specified explicitly. The name of this file has either to be left at
+ * its default <code>sesssions.xml</code> or must be specified using
+ * {@link org.eclipse.emf.teneo.eclipselink.resource.EclipseLinkResource.OPTION_SESSIONS_CONFIGURATION_FILE_NAME
+ * OPTION_SESSIONS_CONFIGURATION_FILE_NAME}.</li>
+ * <li>A query string which is an <b>alias for</b> a <b>contents query</b>. It refers to a
+ * {@link org.eclipse.persistence.queries.ReadAllQuery ReadAllQuery} instance which has been made available through
+ * the {@link EclipseLinkSettingsRegistry}. If the alias is a qualified Java class name and no matching contents
+ * query can be found, a new {@link org.eclipse.persistence.queries.ReadAllQuery contents query} for reading all
+ * instances of the given class will be created on the fly and added to the {@link EclipseLinkSettingsRegistry}.
+ * </ul>
+ *
+ * @param uri
+ * the EclipseLink {@link org.eclipse.emf.common.util.URI URI} identifying the set of database objects to
+ * be contained in this resource. Must not be null.
+ * @return the newly created {@link EclipseLinkResourceImpl} instance.
+ */
+ public EclipseLinkResourceImpl(URI uri) {
+ super(uri);
+
+ Assert.isLegal(EclipseLinkURIUtil.isEclipseLinkURI(uri), Messages.assert_uriMustContainEclipseLinkScheme);
+ Assert.isLegal(uri.segmentCount() == 1, Messages.assert_uriMustContainPersistenceUnitSegment);
+ Assert.isLegal(uri.hasQuery(), Messages.assert_uriMustContainContentsQueryString);
+
+ persistenceUnitName = uri.segments()[0];
+ contentsQuery = uri.query();
+
+ initDefaultOptions();
+ }
+
+ /**
+ * Creates an instance with the given EclipseLink settings.
+ *
+ * @param loginAlias
+ * the alias for a database login to be used. It refers to a
+ * {@link org.eclipse.persistence.sessions.DatabaseLogin DatabaseLogin} instance which has been made
+ * available through the {@link EclipseLinkSettingsRegistry}. If set to null, the database login
+ * specified within the EclipseLink session configuration will be taken as a default.
+ * @param persistenceUnitName
+ * the name of applicable database session declared in the EclipseLink sessions configuration. Must not
+ * be null nor empty. The sessions configuration is expected to be provided in a file on the
+ * application's classpath, and therefore doesn't need to be specified explicitly. The name of this file
+ * has either to be left at its default <code>sesssions.xml</code> or must be specified using
+ * {@link org.eclipse.emf.teneo.eclipselink.resource.EclipseLinkResource.OPTION_SESSIONS_CONFIGURATION_FILE_NAME
+ * OPTION_SESSIONS_CONFIGURATION_FILE_NAME}.
+ * @param contentsQuery
+ * the alias for a contents query. Must not be null nor empty. It refers to a
+ * {@link org.eclipse.persistence.queries.ReadAllQuery ReadAllQuery} instance which has been made
+ * available through the {@link EclipseLinkSettingsRegistry}. If the alias is a qualified Java class name
+ * and no matching contents query can be found, a new
+ * {@link org.eclipse.persistence.queries.ReadAllQuery contents query} for reading all instances of the
+ * given class will be created on the fly and added to the {@link EclipseLinkSettingsRegistry}.
+ * @return the newly created {@link EclipseLinkResourceImpl} instance.
+ */
+ public EclipseLinkResourceImpl(String persistenceUnitName, String contentsQuery) {
+ uri = EclipseLinkURIUtil.createEclipseLinkURI(persistenceUnitName, contentsQuery);
+
+ this.persistenceUnitName = persistenceUnitName;
+ this.contentsQuery = contentsQuery;
+
+ initDefaultOptions();
+ }
+
+ public Map<Object, Object> getDefaultSaveOptions() {
+ return defaultSaveOptions;
+ }
+
+ public Map<Object, Object> getDefaultLoadOptions() {
+ return defaultLoadOptions;
+ }
+
+ @Override
+ public void setURI(URI uri) {
+ // cannot change the URI of a EclipseLinkResource on the fly
+ }
+
+ /**
+ * A notifying list implementation for supporting {@link Resource#getContents} on databases.
+ */
+ protected class DatabaseContentsEList<E extends Object & EObject> extends ContentsEList<E> {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void loaded() {
+ if (!isLoaded()) {
+ Map<?, ?> options = defaultLoadOptions;
+ ResourceSet resourceSet = getResourceSet();
+ if (resourceSet != null) {
+ options = mergeMaps(resourceSet.getLoadOptions(), options);
+ }
+ openDatabase(options);
+ }
+ super.loaded();
+ }
+ }
+
+ @Override
+ public EList<EObject> getContents() {
+ if (contents == null) {
+ contents = new DatabaseContentsEList<EObject>();
+ }
+ return contents;
+ }
+
+ /**
+ * Returns the URI {@link org.eclipse.emf.common.util.URI#fragment fragment} that, when passed to
+ * {@link org.eclipse.emf.ecore.resource.Resource#getEObject getEObject} will return the given object.
+ * <p>
+ * This method is involved in saving resources with cross-resource references and setting objects to be proxies
+ * after unloading resources of any kind. Both Ecore ID attribute based and structured URI
+ * {@link org.eclipse.emf.common.util.URI#fragment fragment}s as per
+ * {@link org.eclipse.emf.ecore.InternalEObject#eURIFragmentSegment InternalEObject.eURIFragmentSegment} are
+ * supported. When an ID attribute has been defined on the {@link org.eclipse.emf.ecore.EObject#eClass eClass} of
+ * the given object, the URI {@link org.eclipse.emf.common.util.URI#fragment fragment} will not only consist of the
+ * ID itself but also contain the qualified owner class name as well as the name of the ID attribute. The resulting
+ * format is as follows:
+ * </p>
+ * <ul>
+ * <code>ownerTypeName|idAttributeName='id'</code>
+ * </ul>
+ * .
+ *
+ * @param eObject
+ * the object to identify.
+ * @return the URI {@link org.eclipse.emf.common.util.URI#fragment fragment} for this object.
+ */
+ @Override
+ public String getURIFragment(EObject eObject) {
+ String result;
+ String id = EcoreUtil.getID(eObject);
+ if (id != null) {
+ // create decorated ID
+ EAttribute eIDAttribute = eObject.eClass().getEIDAttribute();
+ result = eObject.eClass().getName() + "|" + eIDAttribute.getName() + "='" + id + "'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ } else {
+ result = super.getURIFragment(eObject);
+ }
+ return result;
+ }
+
+ /**
+ * Returns the object identified by the given ID.
+ * <p>
+ * This method is involved in loading resources with cross-resource references. It is called when an Ecore ID
+ * attribute based URI {@link org.eclipse.emf.common.util.URI#fragment fragment} needs to be resolved. In the
+ * original implementation, this is done by iterating deeply over the resource's contents until the object with the
+ * given ID is found. While this approach is fine in XML/XMI files with little to medium numbers of objects, it
+ * would be a bad idea to do so when dealing with databases where typically large or very large numbers of objects
+ * are stored. Therefore, EclipseLink resources rely on dynamically created
+ * {@link org.eclipse.persistence.queries.ReadObjectQuery ReadObjectQueries} when retrieving objects for Ecore ID
+ * attribute based URI {@link org.eclipse.emf.common.util.URI#fragment fragments}. In order to enable such queries
+ * to be created, the URI {@link org.eclipse.emf.common.util.URI#fragment fragment} must not only consist of the ID
+ * itself but has also to contain the qualified owner class name as well as the name of the ID attribute. The
+ * expected format is as follows:
+ * </p>
+ * <ul>
+ * <code>ownerTypeName|idAttributeName='id'</code>
+ * </ul>
+ * .
+ *
+ * @param fragment
+ * the URI {@link org.eclipse.emf.common.util.URI#fragment fragment} for an object to be retrieved.
+ * @return the retrieved object.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ protected EObject getEObjectByID(String fragment) {
+ try {
+ EObject result = null;
+
+ // break decorated ID into its components
+ String[] idComponents = fragment.split("\\||='|'"); //$NON-NLS-1$
+
+ // validate the decorated ID's components
+ boolean valid = true;
+ if (idComponents.length < 3) {
+ valid = false;
+ } else {
+ for (int i = 0; i < 3; i++) {
+ if (idComponents[i] == null || idComponents[i].trim().length() == 0) {
+ valid = false;
+ break;
+ }
+ }
+ }
+ Assert.isLegal(valid, Messages.assert_invalidObjectId);
+
+ // build and execute query based on the decorated ID's components;
+ // take first object in case that query result is not unique
+ String queryString = "select o from " + idComponents[0] + " o where o." + idComponents[1] + " = '" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + idComponents[2] + "'"; //$NON-NLS-1$
+ List<EObject> eObjects = entityManager.createQuery(queryString).getResultList();
+ if (eObjects.size() > 0) {
+ result = eObjects.get(0);
+ }
+
+ return result;
+ } catch (RuntimeException exception) {
+ throw new WrappedException(exception);
+ }
+ }
+
+ @Override
+ public void attached(EObject eObject) {
+ // if attached object is a previously detached one simply remove it from
+ // the to be removed list
+ if (eObjectsToBeRemovedFromDatabase.contains(eObject)) {
+ eObjectsToBeRemovedFromDatabase.remove(eObject);
+ return;
+ }
+
+ // be sure that object to be attached has been created by application
+ // and is not just read from database
+ if (!readingContentsFromDatabase) {
+ // TODO replace content iterator by automatic validation/fixing of orm
+ // cascading settings
+ Iterator<EObject> allContents = new EclipseLinkContentTreeIterator<EObject>(Collections
+ .singletonList(eObject), true);
+ while (allContents.hasNext()) {
+ EObject eContainedObject = allContents.next();
+ entityManager.persist(eContainedObject);
+ }
+ }
+ }
+
+ @Override
+ public void detached(EObject eObject) {
+ // don't delete detached objects immediately in entity manager, but keep
+ // them in a to be removed list first so that they can easily reattached if
+ // required by application
+ eObjectsToBeRemovedFromDatabase.add(eObject);
+ }
+
+ @Override
+ public final void save(Map<?, ?> options) throws IOException {
+ Assert.isTrue(isLoaded, Messages.assert_cannotSaveUnloadedResource);
+
+ // writing to database goes via entity manager rather than output stream
+ save(null, options);
+ }
+
+ @Override
+ public final void load(Map<?, ?> options) throws IOException {
+ if (!isLoaded) {
+ try {
+ // reading from database goes via entity manager rather than input
+ // stream
+ load(null, options);
+ } catch (WrappedException exception) {
+ setLoaded(false);
+ throw exception;
+ }
+ }
+ }
+
+ /**
+ * An iterator over the tree contents of a collection of EObjects, Resources, and ResourceSets.
+ * <p>
+ * It provides a special iterator for EObject.eContents that can be configured to be aware of EclipseLink
+ * indirection. This is useful in cases where an iterator over all available content objects, which have already
+ * been read from the database, is required but a retrieval of new content objects from the database has to be
+ * avoided. I.e. the iterator will only walk through contained objects which exist as clones in the underlying
+ * EclipseLink unit of work. All other contained objects will be skipped and therefore won't get on demand loaded.
+ * </p>
+ */
+ public static class EclipseLinkContentTreeIterator<E> extends ContentTreeIterator<E> {
+
+ private static final long serialVersionUID = 1L;
+
+ private boolean loadOnDemand;
+
+ public EclipseLinkContentTreeIterator(Collection<?> emfObjects, boolean loadOnDemand) {
+
+ super(emfObjects);
+ this.loadOnDemand = loadOnDemand;
+ }
+
+ @Override
+ public Iterator<? extends EObject> getEObjectChildren(EObject eObject) {
+ Iterator<? extends EObject> result;
+ if (!loadOnDemand) {
+ List<EReference> eLoadedContainmentReferences = new ArrayList<EReference>();
+ for (EReference eReference : eObject.eClass().getEAllContainments()) {
+ if (eReference.isMany()) {
+ EList<?> eChildrenList = (EList<?>) eObject.eGet(eReference);
+ if (eChildrenList instanceof IndirectEContainer<?>) {
+ IndirectEContainer<?> indirectEContainer = (IndirectEContainer<?>) eChildrenList;
+ if (indirectEContainer.isInstantiated()) {
+ eLoadedContainmentReferences.add(eReference);
+ }
+ } else {
+ eLoadedContainmentReferences.add(eReference);
+ }
+ } else {
+ // TODO fix to check woven ValueHolder
+ // Object childObject = eObject.eGet(eReference);
+ // ProxyIndirectionPolicy policy = new ProxyIndirectionPolicy();
+ // if (policy.objectIsInstantiated(childObject)) {
+ eLoadedContainmentReferences.add(eReference);
+ // }
+ }
+ }
+ result = new EContentsEList<EObject>(eObject, eLoadedContainmentReferences).iterator();
+ } else {
+ result = super.getEObjectChildren(eObject);
+ }
+ return result;
+ }
+ }
+
+ @Override
+ protected void doSave(OutputStream outputStream, Map<?, ?> options) throws IOException {
+ Assert.isNotNull(options);
+
+ try {
+ // actually delete objects which have previously been detached
+ if (eObjectsToBeRemovedFromDatabase.size() > 0) {
+ for (EObject eObject : eObjectsToBeRemovedFromDatabase) {
+ // TODO replace content iterator by automatic validation/fixing of orm
+ // cascading settings
+ Iterator<EObject> allContents = new EclipseLinkContentTreeIterator<EObject>(Collections
+ .singletonList(eObject), true);
+ while (allContents.hasNext()) {
+ EObject eContainedObject = allContents.next();
+ entityManager.remove(eContainedObject);
+ }
+ }
+ eObjectsToBeRemovedFromDatabase.clear();
+ }
+
+ // commit but continue transaction
+ entityManager.getTransaction().commit();
+ entityManager.getTransaction().begin();
+ } catch (RuntimeException exception) {
+ throw new WrappedException(exception);
+ }
+ }
+
+ @Override
+ protected void doLoad(InputStream inputStream, Map<?, ?> options) throws IOException {
+ openDatabase(options);
+ readContentsFromDatabase(options);
+ }
+
+ @Override
+ protected void doUnload() {
+ Iterator<EObject> allContents = new EclipseLinkContentTreeIterator<EObject>(new ArrayList<EObject>(
+ getContents()), false);
+
+ // this guard is needed to ensure that clear doesn't make the resource
+ // become loaded
+ if (!getContents().isEmpty()) {
+ getContents().clear();
+ }
+ getErrors().clear();
+ getWarnings().clear();
+
+ while (allContents.hasNext()) {
+ InternalEObject eObject = (InternalEObject) allContents.next();
+ unloaded(eObject);
+ }
+
+ closeDatabase();
+ }
+
+ @Override
+ public final boolean isTrackingModification() {
+ return false;
+ }
+
+ @Override
+ public final void setTrackingModification(boolean isTrackingModification) {
+ // do nothing
+ }
+
+ @Override
+ public boolean isModified() {
+ return false;
+ }
+
+ @Override
+ public void setModified(boolean isModified) {
+ // do nothing
+ }
+
+ protected void initDefaultOptions() {
+ defaultSaveOptions = new HashMap<Object, Object>();
+
+ defaultLoadOptions = new HashMap<Object, Object>();
+ defaultLoadOptions.put("eclipselink.logging.level", "FINE"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ protected void openDatabase(Map<?, ?> options) {
+ Assert.isNotNull(options);
+
+ try {
+ // entity manager factory for given persistence unit name already
+ // existing?
+ EntityManagerFactoryInstance entityManagerFactoryInstance;
+ if (!persistenceUnitNameToEntityManagerFactoryInstanceMap.containsKey(persistenceUnitName)) {
+ // create and register new entity manager factory for given persistence
+ // unit name according to sessions configuration file
+ Assert.isTrue(options.containsKey(PersistenceUnitProperties.CLASSLOADER), NLS.bind(
+ Messages.assert_classloaderMustHavePersistenceUnit$0OnClasspath, persistenceUnitName));
+ EntityManagerFactory entityManagerFactory = new PersistenceProvider().createEntityManagerFactory(
+ persistenceUnitName, options);
+ Assert.isTrue(entityManagerFactory != null, NLS.bind(
+ Messages.assert_unableToCreateEntityManangerFactoryforPersistenceUnit$0, persistenceUnitName));
+ entityManagerFactoryInstance = new EntityManagerFactoryInstance(entityManagerFactory);
+ persistenceUnitNameToEntityManagerFactoryInstanceMap.put(persistenceUnitName,
+ entityManagerFactoryInstance);
+ } else {
+ // retrieve existing entity manager factory for given persistence unit
+ // name
+ entityManagerFactoryInstance = persistenceUnitNameToEntityManagerFactoryInstanceMap
+ .get(persistenceUnitName);
+ }
+
+ // increase EclipseLink resource instance counter for underlying entity
+ // manager factory
+ int count = entityManagerFactoryInstance.getResourceInstanceCount();
+ count++;
+ entityManagerFactoryInstance.setResourceInstanceCount(count);
+
+ // create entity manager and begin transaction
+ EntityManagerFactory entityManagerFactory = entityManagerFactoryInstance.getEntityManagerFactory();
+ entityManager = entityManagerFactory.createEntityManager();
+ entityManager.getTransaction().begin();
+ } catch (RuntimeException exception) {
+ closeDatabase();
+ throw new WrappedException(exception);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void readContentsFromDatabase(Map<?, ?> options) {
+ try {
+ // execute contents query and initialize contents list
+ readingContentsFromDatabase = true;
+ List<EObject> contents = entityManager.createQuery(contentsQuery).getResultList();
+ getContents().addAll(contents);
+ readingContentsFromDatabase = false;
+ } catch (RuntimeException exception) {
+ throw new WrappedException(exception);
+ }
+ }
+
+ protected void closeDatabase() {
+ // entity manager existing and open?
+ if (entityManager != null && entityManager.isOpen()) {
+ // close entity manager
+ entityManager.close();
+ }
+
+ // entity manager factory for given persistence unit name existing?
+ if (persistenceUnitNameToEntityManagerFactoryInstanceMap.containsKey(persistenceUnitName)) {
+ // decrease EclipseLink resource instance counter for underlying entity
+ // manager factory
+ EntityManagerFactoryInstance entityManagerFactoryInstance = persistenceUnitNameToEntityManagerFactoryInstanceMap
+ .get(persistenceUnitName);
+ int count = entityManagerFactoryInstance.getResourceInstanceCount();
+ if (count > 0) {
+ count--;
+ entityManagerFactoryInstance.setResourceInstanceCount(count);
+ }
+
+ // close and unregister entity manager factory for given persistence unit
+ // name when all EclipseLink resource instances relying on it have been
+ // unloaded
+ if (count == 0) {
+ EntityManagerFactory entityManagerFactory = entityManagerFactoryInstance.getEntityManagerFactory();
+ entityManagerFactory.close();
+ persistenceUnitNameToEntityManagerFactoryInstanceMap.remove(persistenceUnitName);
+ }
+ }
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkURIUtil.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkURIUtil.java
new file mode 100755
index 000000000..1f8c5e441
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/resource/EclipseLinkURIUtil.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.resource;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+/**
+ * This class contains convenient static methods for working with EclipseLink resources.
+ */
+public class EclipseLinkURIUtil {
+
+ public static final String URI_SCHEME_ECLIPSELINK = "eclipselink"; //$NON-NLS-1$
+
+ /**
+ * Creates a EclipseLink {@link org.eclipse.emf.common.util.URI URI} from the given EclipseLink settings.
+ * <p>
+ * The resulting EclipseLink {@link org.eclipse.emf.common.util.URI URI} will have the following format:
+ * </p>
+ * <ul>
+ * <code>eclipselink://loginAlias/sessionName?contentsQueryAlias</code>
+ * </ul>
+ * </p>
+ *
+ * @param loginAlias
+ * the alias for a database login to be used. It refers to a
+ * {@link org.eclipse.persistence.sessions.DatabaseLogin DatabaseLogin} instance which has been made
+ * available through the {@link EclipseLinkSettingsRegistry}. If set to null, the database login
+ * specified within the EclipseLink session configuration will be taken as a default.
+ * @param persistenceUnitName
+ * the name of applicable database session declared in the EclipseLink sessions configuration. Must not
+ * be null nor empty. The sessions configuration is expected to be provided in a file on the
+ * application's classpath, and therefore doesn't need to be specified explicitly. The name of this file
+ * has either to be left at its default <code>sesssions.xml</code> or must be specified using
+ * {@link org.eclipse.emf.teneo.eclipselink.common.resource.EclipseLinkResource.OPTION_SESSIONS_CONFIGURATION_FILE_NAME
+ * OPTION_SESSIONS_CONFIGURATION_FILE_NAME}.
+ * @param contentsQuery
+ * the alias for a contents query. Must not be null nor empty. It refers to a
+ * {@link org.eclipse.persistence.queries.ReadAllQuery ReadAllQuery} instance which has been made
+ * available through the {@link EclipseLinkSettingsRegistry}. If the alias is a qualified Java class name
+ * and no matching contents query can be found, a new
+ * {@link org.eclipse.persistence.queries.ReadAllQuery contents query} for reading all instances of the
+ * given class will be created on the fly and added to the {@link EclipseLinkSettingsRegistry}.
+ * @return the newly created EclipseLink {@link org.eclipse.emf.common.util.URI URI} instance.
+ */
+ public static URI createEclipseLinkURI(String persistenceUnitName, String contentsQuery) {
+ Assert.isLegal(persistenceUnitName != null && persistenceUnitName.trim().length() > 0);
+ Assert.isLegal(contentsQuery != null && contentsQuery.trim().length() > 0);
+
+ String result = URI_SCHEME_ECLIPSELINK + ":///" + persistenceUnitName + "?" + contentsQuery; //$NON-NLS-1$ //$NON-NLS-2$
+ return URI.createURI(result);
+ }
+
+ /**
+ * Checks if given {@link org.eclipse.emf.common.util.URI URI} is a EclipseLink URI.
+ *
+ * @param uri
+ * the {@link org.eclipse.emf.common.util.URI URI} to be analyzed. Must not be null.
+ * @return true if URI is a EclipseLink URI, false otherwise.
+ */
+ public static boolean isEclipseLinkURI(URI uri) {
+ Assert.isNotNull(uri);
+
+ return URI_SCHEME_ECLIPSELINK.equals(uri.scheme());
+ }
+
+ /**
+ * TODO add comment
+ *
+ * @param qualifiedType
+ * @return
+ */
+ public static String createContentsInstancesOfQuery(EClass contentsType) {
+ Assert.isNotNull(contentsType);
+
+ return "select o from " + contentsType.getName() + " o"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Creates a {@link org.eclipse.persistence.queries.ReadAllQuery contents query} for a given type with a given value
+ * of a given feature.
+ *
+ * @param contentsType
+ * the type which the contents query has to be created for. Must not be null.
+ * @param feature
+ * the feature on given type which is relevant to the contents query. Must not be null.
+ * @param value
+ * the value which contents query to be created will check for equality on given feature of objects with
+ * given type. Must not be null.
+ * @return the newly created {@link org.eclipse.persistence.queries.ReadAllQuery contents query}
+ */
+ public static String createContentsEqualQuery(EClass contentsType, EStructuralFeature feature, Object value) {
+ Assert.isNotNull(contentsType);
+ Assert.isNotNull(feature);
+ Assert.isNotNull(value);
+
+ return "select o from " + contentsType.getName() + " o where o." + feature.getName() + " = '" + value + "'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+
+ /**
+ * TODO add comment
+ */
+ public static String createContentsExampleQuery(EObject contentsExample) {
+ Assert.isNotNull(contentsExample);
+
+ EAttribute idAttribute = contentsExample.eClass().getEIDAttribute();
+ if (idAttribute != null) {
+ return createContentsEqualQuery(contentsExample.eClass(), idAttribute, contentsExample.eGet(idAttribute));
+ }
+ return ""; //$NON-NLS-1$
+ }
+}
diff --git a/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/util/MapEntryId.java b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/util/MapEntryId.java
new file mode 100755
index 000000000..9ee1be39c
--- /dev/null
+++ b/eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo/eclipselink/util/MapEntryId.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle and Geensys.
+ * 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:
+ * Oracle and Geensys - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.teneo.eclipselink.util;
+
+public class MapEntryId {
+ private String key;
+ private long id;
+
+ public MapEntryId() {
+ }
+
+ public MapEntryId(String key, long id) {
+ this.key = key;
+ this.id = id;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = PRIME * result + (int) (id ^ (id >>> 32));
+ result = PRIME * result + ((key == null) ? 0 : key.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final MapEntryId other = (MapEntryId) obj;
+ if (id != other.id) {
+ return false;
+ }
+ if (key == null) {
+ if (other.key != null) {
+ return false;
+ }
+ } else if (!key.equals(other.key)) {
+ return false;
+ }
+ return true;
+ }
+
+} \ No newline at end of file

Back to the top