blob: f5b149bee4df858e0698e02aaec7114942f9246d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2006 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();
}
}