/** * Copyright (c) 2003-2011 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 - Initial API and implementation */ package org.eclipse.emf.ecore.change.impl; import com.google.gwt.user.client.rpc.GwtTransient; import java.util.Collection; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.NotificationChain; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.ECollections; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EOperation; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.ETypedElement; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.change.ChangeFactory; import org.eclipse.emf.ecore.change.ChangeKind; import org.eclipse.emf.ecore.change.ChangePackage; import org.eclipse.emf.ecore.change.FeatureChange; import org.eclipse.emf.ecore.change.ListChange; import org.eclipse.emf.ecore.impl.ENotificationImpl; import org.eclipse.emf.ecore.impl.EObjectImpl; import org.eclipse.emf.ecore.util.EObjectContainmentEList; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.util.FeatureMap; import org.eclipse.emf.ecore.util.FeatureMapUtil; import org.eclipse.emf.ecore.util.InternalEList; /** * <!-- begin-user-doc --> * An implementation of the model object '<em><b>Feature Change</b></em>'. * <!-- end-user-doc --> * <p> * The following features are implemented: * <ul> * <li>{@link org.eclipse.emf.ecore.change.impl.FeatureChangeImpl#getFeatureName <em>Feature Name</em>}</li> * <li>{@link org.eclipse.emf.ecore.change.impl.FeatureChangeImpl#getDataValue <em>Data Value</em>}</li> * <li>{@link org.eclipse.emf.ecore.change.impl.FeatureChangeImpl#isSet <em>Set</em>}</li> * <li>{@link org.eclipse.emf.ecore.change.impl.FeatureChangeImpl#getValue <em>Value</em>}</li> * <li>{@link org.eclipse.emf.ecore.change.impl.FeatureChangeImpl#getFeature <em>Feature</em>}</li> * <li>{@link org.eclipse.emf.ecore.change.impl.FeatureChangeImpl#getReferenceValue <em>Reference Value</em>}</li> * <li>{@link org.eclipse.emf.ecore.change.impl.FeatureChangeImpl#getListChanges <em>List Changes</em>}</li> * </ul> * </p> * * @generated */ public class FeatureChangeImpl extends EObjectImpl implements FeatureChange { /** * The bit of {@link #eFlags} that is used to represent if feature is a proxy. */ protected static final int EPROXY_FEATURECHANGE = ELAST_EOBJECT_FLAG << 1; /** * The default value of the '{@link #getFeatureName() <em>Feature Name</em>}' attribute. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #getFeatureName() * @generated * @ordered */ protected static final String FEATURE_NAME_EDEFAULT = null; /** * The default value of the '{@link #getDataValue() <em>Data Value</em>}' attribute. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #getDataValue() * @generated * @ordered */ protected static final String DATA_VALUE_EDEFAULT = null; /** * The default value of the '{@link #isSet() <em>Set</em>}' attribute. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #isSet() * @generated * @ordered */ protected static final boolean SET_EDEFAULT = true; /** * The cached value of the '{@link #isSet() <em>Set</em>}' attribute. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #isSet() * @generated * @ordered */ @GwtTransient protected boolean set = SET_EDEFAULT; /** * The default value of the '{@link #getValue() <em>Value</em>}' attribute. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #getValue() * @generated * @ordered */ protected static final Object VALUE_EDEFAULT = null; /** * The cached value of the '{@link #getListChanges() <em>List Changes</em>}' containment reference list. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #getListChanges() * @generated * @ordered */ @GwtTransient protected EList<ListChange> listChanges; @GwtTransient protected EStructuralFeature feature; @GwtTransient protected String featureName; @GwtTransient protected Object value; @GwtTransient protected String valueString; /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ protected FeatureChangeImpl() { super(); } protected FeatureChangeImpl(EStructuralFeature feature, Object value, boolean isSet) { this(); this.feature = feature; setValue(value); this.set = isSet; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override protected EClass eStaticClass() { return ChangePackage.Literals.FEATURE_CHANGE; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public String getFeatureName() { return feature == null ? featureName : feature.getName(); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void setFeatureName(String newFeatureName) { featureName = newFeatureName; feature = null; eFlags &= ~EPROXY_FEATURECHANGE; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void unsetFeatureName() { setFeatureName(null); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public boolean isSetFeatureName() { return featureName != null || feature != null && eContainer() instanceof EObjectToChangesMapEntryImpl; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public String getDataValue() { if (valueString == null) { EStructuralFeature feature = getFeature(); if (feature instanceof EAttribute && !feature.isMany()) { EDataType type = (EDataType)feature.getEType(); valueString = EcoreUtil.convertToString(type, value); } } return valueString; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void setDataValue(String newDataValue) { String oldDataValue = getDataValue(); valueString = newDataValue; value = null; if (eNotificationRequired()) eNotify(new ENotificationImpl(this, Notification.SET, ChangePackage.FEATURE_CHANGE__DATA_VALUE, oldDataValue, newDataValue)); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public boolean isSet() { return set; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public void setSet(boolean newSet) { boolean oldSet = set; set = newSet; if (eNotificationRequired()) eNotify(new ENotificationImpl(this, Notification.SET, ChangePackage.FEATURE_CHANGE__SET, oldSet, set)); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public EStructuralFeature getFeature() { if (feature == null) { if (featureName != null) { EObject container = eContainer(); if (container instanceof EObjectToChangesMapEntryImpl) { EObject typedKey = ((EObjectToChangesMapEntryImpl)container).getTypedKey(); if (typedKey != null) { feature = typedKey.eClass().getEStructuralFeature(featureName); } } } } else if ((eFlags & EPROXY_FEATURECHANGE) !=0) { EStructuralFeature oldFeature = feature; feature = (EStructuralFeature)EcoreUtil.resolve(feature, this); if (feature != oldFeature) { if (eNotificationRequired()) eNotify(new ENotificationImpl(this, Notification.RESOLVE, ChangePackage.FEATURE_CHANGE__FEATURE, oldFeature, feature)); } eFlags &= ~ EPROXY_FEATURECHANGE; } return feature; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public EStructuralFeature basicGetFeature() { return feature; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void setFeature(EStructuralFeature newFeature) { EStructuralFeature oldFeature = basicGetFeature(); feature = newFeature; featureName = null; if (feature != null && feature.eIsProxy()) eFlags |= EPROXY_FEATURECHANGE; else eFlags &= ~EPROXY_FEATURECHANGE; if (eNotificationRequired()) eNotify(new ENotificationImpl(this, Notification.SET, ChangePackage.FEATURE_CHANGE__FEATURE, oldFeature, newFeature)); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void unsetFeature() { setFeature(null); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public boolean isSetFeature() { return feature != null && !(eContainer() instanceof EObjectToChangesMapEntryImpl); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public EObject getReferenceValue() { EObject referenceValue = basicGetReferenceValue(); if (referenceValue != null && referenceValue.eIsProxy()) { EObject oldReferenceValue = referenceValue; referenceValue = EcoreUtil.resolve(referenceValue, this); if (referenceValue != oldReferenceValue) { if (eNotificationRequired()) eNotify(new ENotificationImpl(this, Notification.RESOLVE, ChangePackage.FEATURE_CHANGE__REFERENCE_VALUE, oldReferenceValue, referenceValue)); } } return referenceValue; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public EObject basicGetReferenceValue() { // If there is a feature name, we should try to get the feature so we can correctly determine if we should return the value. // if (feature == null && featureName != null) { getFeature(); } return (feature instanceof EReference && value instanceof EObject) ? (EObject)value : null; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void setReferenceValue(EObject newReferenceValue) { EObject oldReferenceValue = basicGetReferenceValue(); value = newReferenceValue; valueString = null; if (eNotificationRequired()) eNotify(new ENotificationImpl(this, Notification.SET, ChangePackage.FEATURE_CHANGE__REFERENCE_VALUE, oldReferenceValue, newReferenceValue)); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public EList<ListChange> getListChanges() { if (listChanges == null) { listChanges = new EObjectContainmentEList<ListChange>(ListChange.class, this, ChangePackage.FEATURE_CHANGE__LIST_CHANGES); } return listChanges; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public Object getValue() { EStructuralFeature feature = getFeature(); if (feature.getUpperBound() != 1) { if (value == null && eContainer() instanceof EObjectToChangesMapEntryImpl) { value = getListValue((EList<?>)((EObjectToChangesMapEntryImpl)eContainer()).getTypedKey().eGet(feature)); } } else if (feature instanceof EReference) { return getReferenceValue(); } else if (value == null) // feature is instance of EAttribute { EDataType type = (EDataType)feature.getEType(); value = EcoreUtil.createFromString(type, valueString); } return value; } protected void setValue(Object value) { EStructuralFeature feature = getFeature(); if (!eNotificationRequired() || feature.isMany()) { valueString = null; this.value = value; } else { if (feature instanceof EAttribute) { EDataType type = (EDataType)feature.getEType(); setDataValue(EcoreUtil.convertToString(type, value)); this.value = value; } else { setReferenceValue((EObject)value); } } } protected EList<?> getListValue(EList<?> originalList) { if (isSet() && getFeature().getUpperBound() != 1) { if (value instanceof EList<?>) // cached already? { return (EList<?>)value; } EList<Object> changedList = new BasicEList<Object>(originalList); apply(changedList); value = changedList; // cache result return changedList; } return ECollections.EMPTY_ELIST; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void apply(EObject originalObject) { apply(originalObject, false); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void applyAndReverse(EObject originalObject) { apply(originalObject, true); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated NOT */ public void reverse(EObject originalObject) { process(originalObject, true, false); } protected void apply(EObject originalObject, boolean reverse) { process(originalObject, reverse, true); } protected void process(EObject originalObject, boolean reverse, boolean apply) { EStructuralFeature.Internal internalFeature = (EStructuralFeature.Internal)getFeature(); if (internalFeature != null && internalFeature.isChangeable()) { if (!internalFeature.isContainer()) { if (!isSet()) { if (reverse && internalFeature.isMany()) { ListChange listChange = createListChange(getListChanges(), ChangeKind.ADD_LITERAL, 0); if (internalFeature.getEOpposite() != null || internalFeature.isContainment()) { listChange.getValues().addAll((EList<?>)getValue()); } else { listChange.getValues().addAll((EList<?>)originalObject.eGet(internalFeature)); } } if (apply) { originalObject.eUnset(internalFeature); } } else if (internalFeature.isMany()) { if (listChanges != null) { if (internalFeature.isFeatureMap()) { FeatureMap.Internal result = (FeatureMap.Internal)originalObject.eGet(internalFeature); if (apply) { @SuppressWarnings("unchecked") EList<FeatureMap.Entry.Internal> prototype = (EList<FeatureMap.Entry.Internal>)getValue(); EList<FeatureMap.Entry> featureMapEntryList = new BasicEList<FeatureMap.Entry>(prototype.size()); for (FeatureMap.Entry.Internal entry : prototype) { Object entryValue = entry.getValue(); // Create a proper feature map entry. // entry = entry.createEntry(entryValue); featureMapEntryList.add(entry); EStructuralFeature entryFeature = entry.getEStructuralFeature(); if (!FeatureMapUtil.isMany(originalObject, entry.getEStructuralFeature())) { for (int i = 0, size = result.size(); i < size; ++i) { if (result.getEStructuralFeature(i) == entryFeature && !(entryValue == null ? result.getValue(i) == null : entryValue.equals(result.getValue(i)))) { result.set(i, entry); } } } } ECollections.setEList(result, featureMapEntryList); } else { newValue = new BasicEList<Object>(result); } } else if (internalFeature.getEOpposite() != null || internalFeature.isContainment()) { @SuppressWarnings("unchecked") EList<Object> result = (EList<Object>)originalObject.eGet(internalFeature); if (apply) { // Bidirectional references need to use this less efficient approach because some // or all of the changes may already have been made from the other end. // @SuppressWarnings("unchecked") EList<Object> prototype = (EList<Object>)getValue(); ECollections.setEList(result, prototype); } else { newValue = new BasicEList<Object>(result); } } else { @SuppressWarnings("unchecked") EList<Object> applyToList = (EList<Object>)originalObject.eGet(internalFeature); if (apply) { if (reverse) { applyAndReverse(applyToList); } else { apply(applyToList); } } else { reverse(applyToList); } } if (reverse) { ECollections.reverse(getListChanges()); } } } else if (apply) { originalObject.eSet(internalFeature, getValue()); } } if (reverse) { setSet(newIsSet); setValue(newValue); if (!isSet()) { getListChanges().clear(); } } } } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) { switch (featureID) { case ChangePackage.FEATURE_CHANGE__LIST_CHANGES: return ((InternalEList<?>)getListChanges()).basicRemove(otherEnd, msgs); } return super.eInverseRemove(otherEnd, featureID, msgs); } @GwtTransient protected boolean newIsSet; @GwtTransient protected Object newValue; public void preApply(EObject originalObject, boolean reverse) { if (reverse) { EStructuralFeature feature = getFeature(); if (feature == EcorePackage.Literals.ECLASS__EGENERIC_SUPER_TYPES) { newIsSet = !((EClass)originalObject).getEGenericSuperTypes().isEmpty(); newValue = null; } else if (feature == EcorePackage.Literals.ETYPED_ELEMENT__EGENERIC_TYPE) { newValue = ((ETypedElement)originalObject).getEGenericType(); newIsSet = newValue != null; } else if (feature == EcorePackage.Literals.EOPERATION__EGENERIC_EXCEPTIONS) { newIsSet = !((EOperation)originalObject).getEGenericExceptions().isEmpty(); newValue = null; } else if (feature == EcorePackage.Literals.ECLASSIFIER__INSTANCE_TYPE_NAME) { newValue = ((EClassifier)originalObject).getInstanceTypeName(); newIsSet = newValue != null; } else if (feature != null) { newIsSet = originalObject.eIsSet(feature); newValue = feature.isMany() ? null : originalObject.eGet(feature); } } } protected void apply(EList<Object> toList) { for (ListChange listChange : getListChanges()) { listChange.apply(toList); } } protected void applyAndReverse(EList<Object> toList) { for (ListChange listChange : getListChanges()) { listChange.applyAndReverse(toList); } } protected void reverse(EList<Object> toList) { EList<Object> copy = null; for (ListChange listChange : getListChanges()) { if (listChange.getKind() == ChangeKind.REMOVE_LITERAL) { if (copy == null) { copy = new BasicEList<Object>(toList); } listChange.applyAndReverse(copy); } else { listChange.reverse(toList); } } } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override public Object eGet(int featureID, boolean resolve, boolean coreType) { switch (featureID) { case ChangePackage.FEATURE_CHANGE__FEATURE_NAME: return getFeatureName(); case ChangePackage.FEATURE_CHANGE__DATA_VALUE: return getDataValue(); case ChangePackage.FEATURE_CHANGE__SET: return isSet(); case ChangePackage.FEATURE_CHANGE__VALUE: return getValue(); case ChangePackage.FEATURE_CHANGE__FEATURE: if (resolve) return getFeature(); return basicGetFeature(); case ChangePackage.FEATURE_CHANGE__REFERENCE_VALUE: if (resolve) return getReferenceValue(); return basicGetReferenceValue(); case ChangePackage.FEATURE_CHANGE__LIST_CHANGES: return getListChanges(); } return super.eGet(featureID, resolve, coreType); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @SuppressWarnings("unchecked") @Override public void eSet(int featureID, Object newValue) { switch (featureID) { case ChangePackage.FEATURE_CHANGE__FEATURE_NAME: setFeatureName((String)newValue); return; case ChangePackage.FEATURE_CHANGE__DATA_VALUE: setDataValue((String)newValue); return; case ChangePackage.FEATURE_CHANGE__SET: setSet((Boolean)newValue); return; case ChangePackage.FEATURE_CHANGE__FEATURE: setFeature((EStructuralFeature)newValue); return; case ChangePackage.FEATURE_CHANGE__REFERENCE_VALUE: setReferenceValue((EObject)newValue); return; case ChangePackage.FEATURE_CHANGE__LIST_CHANGES: getListChanges().clear(); getListChanges().addAll((Collection<? extends ListChange>)newValue); return; } super.eSet(featureID, newValue); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override public void eUnset(int featureID) { switch (featureID) { case ChangePackage.FEATURE_CHANGE__FEATURE_NAME: unsetFeatureName(); return; case ChangePackage.FEATURE_CHANGE__DATA_VALUE: setDataValue(DATA_VALUE_EDEFAULT); return; case ChangePackage.FEATURE_CHANGE__SET: setSet(SET_EDEFAULT); return; case ChangePackage.FEATURE_CHANGE__FEATURE: unsetFeature(); return; case ChangePackage.FEATURE_CHANGE__REFERENCE_VALUE: setReferenceValue((EObject)null); return; case ChangePackage.FEATURE_CHANGE__LIST_CHANGES: getListChanges().clear(); return; } super.eUnset(featureID); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override public boolean eIsSet(int featureID) { switch (featureID) { case ChangePackage.FEATURE_CHANGE__FEATURE_NAME: return isSetFeatureName(); case ChangePackage.FEATURE_CHANGE__DATA_VALUE: return DATA_VALUE_EDEFAULT == null ? getDataValue() != null : !DATA_VALUE_EDEFAULT.equals(getDataValue()); case ChangePackage.FEATURE_CHANGE__SET: return set != SET_EDEFAULT; case ChangePackage.FEATURE_CHANGE__VALUE: return VALUE_EDEFAULT == null ? getValue() != null : !VALUE_EDEFAULT.equals(getValue()); case ChangePackage.FEATURE_CHANGE__FEATURE: return isSetFeature(); case ChangePackage.FEATURE_CHANGE__REFERENCE_VALUE: return basicGetReferenceValue() != null; case ChangePackage.FEATURE_CHANGE__LIST_CHANGES: return listChanges != null && !listChanges.isEmpty(); } return super.eIsSet(featureID); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ @Override public String toString() { if (eIsProxy()) return super.toString(); StringBuffer result = new StringBuffer(super.toString()); result.append(" (set: "); result.append(set); result.append(')'); return result.toString(); } protected ListChange createListChange(EList<ListChange> changesList, ChangeKind kind, int index) { ListChange listChange = ChangeFactory.eINSTANCE.createListChange(); listChange.setKind(kind); listChange.setIndex(index); changesList.add(listChange); return listChange; } } //FeatureChangeImpl