diff options
Diffstat (limited to 'eclipselink/org.eclipse.emf.teneo.eclipselink/src/org/eclipse/emf/teneo')
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 |