| /******************************************************************************* |
| * Copyright (c) 2001, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jem.internal.instantiation.base; |
| /* |
| * $RCSfile$ |
| * $Revision$ $Date$ |
| */ |
| |
| import java.util.List; |
| |
| import org.eclipse.emf.common.util.BasicEList; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.*; |
| import org.eclipse.emf.ecore.EStructuralFeature.Internal.DynamicValueHolder; |
| import org.eclipse.emf.ecore.impl.EObjectImpl; |
| import org.eclipse.emf.ecore.impl.EStructuralFeatureImpl; |
| |
| import org.eclipse.jem.internal.instantiation.JavaAllocation; |
| import org.eclipse.jem.java.JavaHelpers; |
| |
| /** |
| * This is the default instance for java model objects. |
| * It should not be referenced directly, the IJavaObjectInstance interface should be |
| * used instead. It is public so that it can be subclassed. |
| */ |
| public abstract class JavaInstance extends EObjectImpl implements IJavaInstance { |
| |
| public JavaHelpers getJavaType(){ |
| return (JavaHelpers) eClass(); |
| } |
| |
| public JavaAllocation getAllocation() { |
| return isSetAllocation() ? (JavaAllocation) eGet(JavaInstantiation.getAllocationFeature(this)) : null; |
| } |
| |
| public boolean isImplicitAllocation() { |
| return isSetAllocation() && getAllocation().isImplicit(); |
| } |
| |
| public boolean isParseTreeAllocation() { |
| return isSetAllocation() && getAllocation().isParseTree(); |
| } |
| |
| /** |
| * Visit the argument with all of the set features in an optimized fashion |
| */ |
| private final static Object NIL = EStructuralFeatureImpl.InternalSettingDelegateSingle.NIL; |
| public Object visitSetFeatures(Visitor aVisitor) { |
| Object result = null; |
| if (eHasSettings()) { |
| JavaInstancePropertiesHolder settings = (JavaInstancePropertiesHolder) eSettings(); |
| |
| Object[] setPropertyValues = settings.eSettings(); |
| if (setPropertyValues != null) { |
| List allFeatures = settings.getAllStructuralFeatures(); |
| for (int i = 0; i < setPropertyValues.length; i++) { |
| Object propertyValue = setPropertyValues[i]; |
| if (propertyValue != null) { |
| // <null> is handled by the placeholder NIL. A setting of true null means not set. A setting of NIL means set to null. |
| if (propertyValue == NIL) |
| propertyValue = null; |
| if ((result = aVisitor.isSet((EStructuralFeature) allFeatures.get(i), propertyValue)) != null) |
| break; |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| public boolean isAnyFeatureSet() { |
| if (eHasSettings()) { |
| JavaInstancePropertiesHolder settings = (JavaInstancePropertiesHolder) eSettings(); |
| |
| Object[] setPropertyValues = settings.eSettings(); |
| if (setPropertyValues != null) { |
| for (int i = 0; i < setPropertyValues.length; i++) { |
| Object propertyValue = setPropertyValues[i]; |
| if (propertyValue != null) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| public boolean isSetAllocation() { |
| EReference allocationFeature = JavaInstantiation.getAllocationFeature(this); |
| return allocationFeature != null && eIsSet(allocationFeature); |
| } |
| |
| public void setAllocation(JavaAllocation allocation) { |
| EReference allocationFeature = JavaInstantiation.getAllocationFeature(this); |
| if (allocationFeature != null) |
| eSet(allocationFeature, allocation); |
| } |
| |
| |
| public String toString() { |
| // EObject's toString is too big for us, so we do a customized one. |
| StringBuffer result = new StringBuffer(getClass().getName()); |
| result.append('@'); |
| result.append(Integer.toHexString(hashCode())); |
| |
| if (eIsProxy()) |
| { |
| result.append(" (eProxyURI: "); //$NON-NLS-1$ |
| result.append(eProxyURI()); |
| result.append(')'); |
| } |
| if(getJavaType() != null){ |
| result.append('{'); |
| result.append(getJavaType().getQualifiedName()); |
| result.append('}'); |
| } |
| |
| try { |
| JavaAllocation allocation = getAllocation(); |
| if (allocation != null) { |
| result.append(':'); //$NON-NLS-1$ |
| result.append(allocation.toString()); |
| } |
| } catch (IllegalArgumentException e) { |
| } catch (NullPointerException e) { |
| // This can occur because sometimes this eClass can't be constructed right and won't have an initstring feature. |
| // In that case an NPE is thrown. |
| } |
| return result.toString(); |
| } |
| |
| protected static class JavaInstancePropertiesHolder extends EPropertiesHolderImpl { |
| private EList allStructuralFeatures; |
| |
| public JavaInstancePropertiesHolder() { |
| } |
| |
| public Object[] eSettings() { |
| return eSettings; |
| } |
| |
| public EList getAllStructuralFeatures() { |
| return allStructuralFeatures; |
| } |
| |
| /* |
| * Clear the cache. This is due to |
| * structural features have changed. |
| */ |
| public void clearCache() { |
| eSettings = null; |
| setEContents(null); |
| setECrossReferences(null); |
| allStructuralFeatures = null; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.emf.ecore.impl.EObjectImpl.EPropertiesHolderImpl#allocateSettings(int) |
| */ |
| public void allocateSettings(int maximumDynamicFeatureID) { |
| if (allStructuralFeatures == null) |
| allStructuralFeatures = getEClass().getEAllStructuralFeatures(); |
| super.allocateSettings(maximumDynamicFeatureID); |
| } |
| |
| |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.emf.ecore.impl.EObjectImpl.EPropertiesHolderImpl#setEContents(org.eclipse.emf.common.util.EList) |
| */ |
| public void setEContents(EList eContents) { |
| if (allStructuralFeatures == null) |
| allStructuralFeatures = getEClass().getEAllStructuralFeatures(); |
| super.setEContents(eContents); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.emf.ecore.impl.EObjectImpl.EPropertiesHolderImpl#setECrossReferences(org.eclipse.emf.common.util.EList) |
| */ |
| public void setECrossReferences(EList eCrossReferences) { |
| if (allStructuralFeatures == null) |
| allStructuralFeatures = getEClass().getEAllStructuralFeatures(); |
| super.setECrossReferences(eCrossReferences); |
| } |
| |
| } |
| |
| protected EPropertiesHolder eProperties() |
| { |
| if (eProperties == null) |
| { |
| eProperties = new JavaInstancePropertiesHolder(); |
| } |
| return eProperties; |
| } |
| |
| /** |
| * @see org.eclipse.emf.ecore.InternalEObject#eSetClass(EClass) |
| */ |
| public void eSetClass(EClass eClass) { |
| super.eSetClass(eClass); |
| migrate(); |
| } |
| |
| /** |
| * @param newEClass New eClass set to. (null) when migrating while not setting a new EClass. |
| */ |
| protected void migrate() { |
| // Note: This is extremelly implementation dependent. It may change for any implementation of EMF. |
| if (eProperties != null && (eProperties.hasSettings() || eProperties.getEContents() != null || eProperties.getECrossReferences() != null)) { |
| // Maybe need to reconstruct settings or clear cache. |
| JavaInstancePropertiesHolder properties = (JavaInstancePropertiesHolder) eProperties; |
| EList oldAllFeatures = properties.getAllStructuralFeatures(); |
| |
| // See if migration needed. |
| if (properties.getEClass().getEAllStructuralFeatures() == oldAllFeatures) |
| return; // No migration needed. |
| |
| Object[] oldSettings = properties.eSettings(); |
| properties.clearCache(); // Clear the cache so we can rebuild it. |
| if (oldSettings == null) { |
| return; // It was just either contents or crossrefs, and they have been appropriately cleared. |
| } |
| |
| // It is assumed that any SF that (by identity) is in |
| // both the old and the new eClass, then it doesn't have any internal changes. It simply changed position |
| // in the settings list. Otherwise, need to see if compatible by same name, and if so, move it. |
| eSettings(); // Create new settings |
| Object[] newSettings = properties.eSettings(); |
| int staticFeatureCnt = eStaticFeatureCount(); |
| for (int oldIndex = 0; oldIndex < oldSettings.length; oldIndex++) { |
| if (oldSettings[oldIndex] != null) { |
| EStructuralFeature sf = (EStructuralFeature) oldAllFeatures.get(oldIndex+staticFeatureCnt); |
| int newIndex = super.eDynamicFeatureID(sf); // To avoid recurse on migrate. |
| if (newIndex > -1) { |
| moveESetting(oldSettings, newSettings, oldIndex, sf, newIndex); |
| } else { |
| // See if it exists by name and is compatible. |
| EStructuralFeature newSF = properties.getEClass().getEStructuralFeature(sf.getName()); |
| if (newSF != null && newSF.getClass().equals(sf.getClass()) && |
| newSF.isMany() == sf.isMany() && newSF.isChangeable() == sf.isChangeable()) { |
| boolean compatible = newSF.isUnique() == sf.isUnique() || !newSF.isUnique(); // If new is not unique, then doesn't matter if old and new are the same |
| if (newSF instanceof EReference) { |
| EReference newRef = (EReference) newSF; |
| EReference ref = (EReference) sf; |
| compatible = newRef.isContainment() == ref.isContainment() && newRef.getEReferenceType().isSuperTypeOf(ref.getEReferenceType()); |
| } else |
| compatible = newSF.getEType().equals(sf.getEType()); |
| |
| if (compatible) { |
| newIndex = eDynamicFeatureID(newSF); |
| moveESetting(oldSettings, newSettings, oldIndex, newSF, newIndex); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private void moveESetting(Object[] oldSettings, Object[] newSettings, int oldIndex, EStructuralFeature sf, int newIndex) { |
| // See if single vs many. |
| if (!sf.isMany()) |
| newSettings[newIndex] = oldSettings[oldIndex]; // Great, we can just move it over. |
| else { |
| // Many is more difficult. Need to create a new dynamic list of right type, and |
| // then just copy over the data from the old one. We create new one by doing a simple eGet. |
| // This will construct an empty one with no notifications going out. |
| // Note: This is extremelly implementation dependent. We will be throwing away the old |
| // oldMany, so it is ok to reuse the actual array of data for the newMany. |
| BasicEList newMany = (BasicEList) eGet(sf); |
| BasicEList oldMany = (BasicEList) oldSettings[oldIndex]; |
| newMany.setData(oldMany.size(), oldMany.data()); |
| } |
| } |
| |
| protected DynamicValueHolder eSettings() { |
| migrate(); |
| return super.eSettings(); |
| } |
| |
| } |