blob: b7d7696a878da0bd1585249ef367ba6e4162654c [file] [log] [blame]
/**
*/
package org.eclipse.emf.cdo.evolution.impl;
import org.eclipse.emf.cdo.evolution.Change;
import org.eclipse.emf.cdo.evolution.ChangeKind;
import org.eclipse.emf.cdo.evolution.ElementChange;
import org.eclipse.emf.cdo.evolution.Evolution;
import org.eclipse.emf.cdo.evolution.EvolutionFactory;
import org.eclipse.emf.cdo.evolution.EvolutionPackage;
import org.eclipse.emf.cdo.evolution.Migration;
import org.eclipse.emf.cdo.evolution.ModelSet;
import org.eclipse.emf.cdo.evolution.ModelSetChange;
import org.eclipse.emf.cdo.evolution.PropertyChange;
import org.eclipse.emf.cdo.evolution.Release;
import org.eclipse.emf.cdo.evolution.util.ElementHandler;
import org.eclipse.emf.cdo.evolution.util.ElementRunnable;
import org.eclipse.emf.cdo.evolution.util.IDAnnotation;
import org.eclipse.emf.internal.cdo.CDOObjectImpl;
import org.eclipse.net4j.util.ObjectUtil;
import org.eclipse.net4j.util.collection.CollectionUtil;
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.EClass;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.InternalEList;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* <!-- begin-user-doc -->
* An implementation of the model object '<em><b>Model Set</b></em>'.
* <!-- end-user-doc -->
* <p>
* The following features are implemented:
* </p>
* <ul>
* <li>{@link org.eclipse.emf.cdo.evolution.impl.ModelSetImpl#getChange <em>Change</em>}</li>
* <li>{@link org.eclipse.emf.cdo.evolution.impl.ModelSetImpl#getMigrations <em>Migrations</em>}</li>
* </ul>
*
* @generated
*/
public abstract class ModelSetImpl extends CDOObjectImpl implements ModelSet
{
private static final boolean COMPARE_CONTAINMENT = false;
private static final boolean DEBUG_IDS = false;
private static final boolean DEBUG_MATCH = false;
private static final boolean DEBUG_COMPARE = false;
private static final boolean DEBUG_CLEANUP = false;
private boolean changeInvalid;
private ModelSetImpl emptyModelSet;
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
protected ModelSetImpl()
{
super();
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
protected EClass eStaticClass()
{
return EvolutionPackage.Literals.MODEL_SET;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
protected int eStaticFeatureCount()
{
return 0;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public ModelSetChange getChangeGen()
{
return (ModelSetChange)eDynamicGet(EvolutionPackage.MODEL_SET__CHANGE, EvolutionPackage.Literals.MODEL_SET__CHANGE, true, true);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated NOT
*/
public ModelSetChange getChange()
{
ModelSetChangeImpl change = (ModelSetChangeImpl)getChangeGen();
if (change != null && change.getOldModelSet() != getOldModelSet())
{
// Can happen after undoing a release.
change = null;
}
if (change == null || changeInvalid)
{
ModelSetChangeImpl result;
Map<EModelElement, ElementChange> previousElementChanges;
if (change != null)
{
previousElementChanges = change.reset();
result = change;
}
else
{
ModelSet oldModelSet = getOldModelSet();
ModelSet[] modelSetChain = createModelSetChain(oldModelSet, this);
previousElementChanges = null;
result = (ModelSetChangeImpl)EvolutionFactory.eINSTANCE.createModelSetChange(modelSetChain);
}
compareElements(result, previousElementChanges);
try
{
eSetDeliver(false);
setChange(change = result);
}
finally
{
eSetDeliver(true);
changeInvalid = false;
}
}
return change;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public NotificationChain basicSetChange(ModelSetChange newChange, NotificationChain msgs)
{
msgs = eDynamicInverseAdd((InternalEObject)newChange, EvolutionPackage.MODEL_SET__CHANGE, msgs);
return msgs;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void setChange(ModelSetChange newChange)
{
eDynamicSet(EvolutionPackage.MODEL_SET__CHANGE, EvolutionPackage.Literals.MODEL_SET__CHANGE, newChange);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public NotificationChain basicUnsetChange(NotificationChain msgs)
{
return eDynamicInverseRemove((InternalEObject)getChange(), EvolutionPackage.MODEL_SET__CHANGE, msgs);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void unsetChange()
{
eDynamicUnset(EvolutionPackage.MODEL_SET__CHANGE, EvolutionPackage.Literals.MODEL_SET__CHANGE);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public boolean isSetChange()
{
return eDynamicIsSet(EvolutionPackage.MODEL_SET__CHANGE, EvolutionPackage.Literals.MODEL_SET__CHANGE);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@SuppressWarnings("unchecked")
public EList<Migration> getMigrations()
{
return (EList<Migration>)eDynamicGet(EvolutionPackage.MODEL_SET__MIGRATIONS, EvolutionPackage.Literals.MODEL_SET__MIGRATIONS, true, true);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public abstract Evolution getEvolution();
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public abstract int getVersion();
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public abstract Release getPreviousRelease();
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public abstract EList<EPackage> getRootPackages();
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public abstract EList<EPackage> getAllPackages();
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public abstract boolean containsElement(EModelElement modelElement);
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
@SuppressWarnings("unchecked")
public <T extends EModelElement> T getElement(final String id)
{
// TODO Design better approach to early return from ElementHandler.execute().
int xxx;
class ResultException extends RuntimeException
{
private static final long serialVersionUID = 1L;
EModelElement result;
}
try
{
ElementHandler.execute(getRootPackages(), new ElementRunnable()
{
public void run(EModelElement modelElement)
{
if (ObjectUtil.equals(id, getElementID(modelElement)))
{
ResultException ex = new ResultException();
ex.result = modelElement;
throw ex;
}
}
});
}
catch (ResultException ex)
{
return (T)ex.result;
}
return null;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public String getElementID(EModelElement modelElement)
{
return getElementID(modelElement, false);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public String getElementID(EModelElement modelElement, boolean considerOldIDs)
{
if (containsElement(modelElement))
{
return IDAnnotation.getValue(modelElement, considerOldIDs);
}
return null;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public ModelSetChange compare(ModelSet other)
{
ModelSet[] modelSetChain = createModelSetChain(this, other);
ModelSetChangeImpl result = (ModelSetChangeImpl)EvolutionFactory.eINSTANCE.createModelSetChange(modelSetChain);
compareElements(result, null);
return result;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public Migration getMigration(String diagnosticID)
{
if (diagnosticID != null)
{
for (Migration migration : getMigrations())
{
if (diagnosticID.equals(migration.getDiagnosticID()))
{
return migration;
}
}
}
return null;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@SuppressWarnings("unchecked")
@Override
public NotificationChain eInverseAdd(InternalEObject otherEnd, int featureID, NotificationChain msgs)
{
switch (featureID)
{
case EvolutionPackage.MODEL_SET__MIGRATIONS:
return ((InternalEList<InternalEObject>)(InternalEList<?>)getMigrations()).basicAdd(otherEnd, msgs);
}
return super.eInverseAdd(otherEnd, featureID, msgs);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs)
{
switch (featureID)
{
case EvolutionPackage.MODEL_SET__CHANGE:
return basicUnsetChange(msgs);
case EvolutionPackage.MODEL_SET__MIGRATIONS:
return ((InternalEList<?>)getMigrations()).basicRemove(otherEnd, msgs);
}
return super.eInverseRemove(otherEnd, featureID, msgs);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public Object eGet(int featureID, boolean resolve, boolean coreType)
{
switch (featureID)
{
case EvolutionPackage.MODEL_SET__CHANGE:
return getChange();
case EvolutionPackage.MODEL_SET__MIGRATIONS:
return getMigrations();
}
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 EvolutionPackage.MODEL_SET__CHANGE:
setChange((ModelSetChange)newValue);
return;
case EvolutionPackage.MODEL_SET__MIGRATIONS:
getMigrations().clear();
getMigrations().addAll((Collection<? extends Migration>)newValue);
return;
}
super.eSet(featureID, newValue);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public void eUnset(int featureID)
{
switch (featureID)
{
case EvolutionPackage.MODEL_SET__CHANGE:
unsetChange();
return;
case EvolutionPackage.MODEL_SET__MIGRATIONS:
getMigrations().clear();
return;
}
super.eUnset(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public boolean eIsSet(int featureID)
{
switch (featureID)
{
case EvolutionPackage.MODEL_SET__CHANGE:
return isSetChange();
case EvolutionPackage.MODEL_SET__MIGRATIONS:
return !getMigrations().isEmpty();
}
return super.eIsSet(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public Object eInvoke(int operationID, EList<?> arguments) throws InvocationTargetException
{
switch (operationID)
{
case EvolutionPackage.MODEL_SET___GET_EVOLUTION:
return getEvolution();
case EvolutionPackage.MODEL_SET___GET_VERSION:
return getVersion();
case EvolutionPackage.MODEL_SET___GET_PREVIOUS_RELEASE:
return getPreviousRelease();
case EvolutionPackage.MODEL_SET___GET_ROOT_PACKAGES:
return getRootPackages();
case EvolutionPackage.MODEL_SET___GET_ALL_PACKAGES:
return getAllPackages();
case EvolutionPackage.MODEL_SET___CONTAINS_ELEMENT__EMODELELEMENT:
return containsElement((EModelElement)arguments.get(0));
case EvolutionPackage.MODEL_SET___GET_ELEMENT__STRING:
return getElement((String)arguments.get(0));
case EvolutionPackage.MODEL_SET___GET_ELEMENT_ID__EMODELELEMENT:
return getElementID((EModelElement)arguments.get(0));
case EvolutionPackage.MODEL_SET___GET_ELEMENT_ID__EMODELELEMENT_BOOLEAN:
return getElementID((EModelElement)arguments.get(0), (Boolean)arguments.get(1));
case EvolutionPackage.MODEL_SET___COMPARE__MODELSET:
return compare((ModelSet)arguments.get(0));
case EvolutionPackage.MODEL_SET___GET_MIGRATION__STRING:
return getMigration((String)arguments.get(0));
}
return super.eInvoke(operationID, arguments);
}
public void invalidateChange()
{
changeInvalid = true;
}
private ModelSet getOldModelSet()
{
Release previousRelease = getPreviousRelease();
return previousRelease != null ? previousRelease : getEmptyModelSet();
}
private ModelSet getEmptyModelSet()
{
if (emptyModelSet == null)
{
emptyModelSet = new ModelSetImpl()
{
@Override
public int getVersion()
{
return 0;
}
@Override
public EList<EPackage> getRootPackages()
{
return ECollections.emptyEList();
}
@Override
public Release getPreviousRelease()
{
return null;
}
@Override
public Evolution getEvolution()
{
return ModelSetImpl.this.getEvolution();
}
@Override
public EList<EPackage> getAllPackages()
{
return ECollections.emptyEList();
}
@Override
public boolean containsElement(EModelElement modelElement)
{
return false;
}
};
}
return emptyModelSet;
}
private static void compareElements(final ModelSetChangeImpl result, final Map<EModelElement, ElementChange> previousElementChanges)
{
final ModelSet[] modelSets = result.getModelSetChain();
final ModelSet oldModelSet = result.getOldModelSet();
final ModelSet newModelSet = result.getNewModelSet();
ElementHandler.execute(newModelSet.getRootPackages(), new ElementRunnable()
{
public void run(EModelElement newElement)
{
EModelElement oldElement = newElement;
for (int i = modelSets.length - 1; i > 0; --i)
{
String id = modelSets[i].getElementID(oldElement, true);
oldElement = modelSets[i - 1].getElement(id);
if (oldElement == null)
{
break;
}
}
if (oldElement != null)
{
String newID = IDAnnotation.getValue(newElement);
String oldID = IDAnnotation.getValue(oldElement);
ChangeKind kind = ObjectUtil.equals(newID, oldID) ? ChangeKind.NONE : ChangeKind.COPIED;
if (kind == ChangeKind.COPIED)
{
if (newModelSet.getElement(oldID) == null)
{
kind = ChangeKind.MOVED;
}
}
if (DEBUG_MATCH)
{
System.out.println(kind + " " + getLabel(oldElement) + " --> " + getLabel(newElement));
}
ElementChange elementChange = getElementChange(newElement, oldElement, kind, previousElementChanges);
getParentChange(elementChange, result).getChildren().add(elementChange);
result.getElementChanges().put(oldElement, elementChange);
result.getElementChanges().put(newElement, elementChange);
result.getNewToOldElements().put(newElement, oldElement);
CollectionUtil.add(result.getOldToNewElements(), oldElement, newElement);
}
else
{
if (DEBUG_MATCH)
{
System.out.println("ADDED " + getLabel(newElement));
}
ElementChange elementChange = getElementChange(null, newElement, ChangeKind.ADDED, previousElementChanges);
getParentChange(elementChange, result).getChildren().add(elementChange);
result.getElementChanges().put(newElement, elementChange);
result.getAddedElements().add(newElement);
}
}
});
ElementHandler.execute(oldModelSet.getRootPackages(), new ElementRunnable()
{
public void run(EModelElement oldElement)
{
if (isRemoved(oldElement))
{
if (DEBUG_MATCH)
{
System.out.println("REMOVED " + getLabel(oldElement));
}
ElementChange elementChange = getElementChange(oldElement, null, ChangeKind.REMOVED, previousElementChanges);
getParentChange(elementChange, result).getChildren().add(elementChange);
result.getElementChanges().put(oldElement, elementChange);
result.getRemovedElements().add(oldElement);
}
}
private boolean isRemoved(EModelElement oldElement)
{
Set<EModelElement> newElements = result.getOldToNewElements().get(oldElement);
if (newElements == null || newElements.isEmpty())
{
return true;
}
String oldID = oldModelSet.getElementID(oldElement);
for (EModelElement newElement : newElements)
{
String newID = newModelSet.getElementID(newElement);
if (ObjectUtil.equals(newID, oldID))
{
return false;
}
}
return true;
}
});
for (Map.Entry<EModelElement, EModelElement> entry : result.getNewToOldElements().entrySet())
{
EModelElement newElement = entry.getKey();
EModelElement oldElement = entry.getValue();
compareProperties(result, newElement, oldElement);
}
for (EModelElement newElement : result.getAddedElements())
{
compareProperties(result, newElement, null);
}
cleanupChanges(result);
}
private static void compareProperties(ModelSetChange result, EModelElement newElement, EModelElement oldElement)
{
EClass eClass = newElement.eClass();
if (oldElement == null)
{
oldElement = (EModelElement)EcoreUtil.create(eClass);
}
else if (oldElement.eClass() != eClass)
{
int xxx;
throw new IllegalArgumentException();
}
Map<EModelElement, EModelElement> newToOldElements = result.getNewToOldElements();
for (EStructuralFeature feature : eClass.getEAllStructuralFeatures())
{
if (feature.isDerived())
{
continue;
}
if (feature == EcorePackage.Literals.EPACKAGE__EFACTORY_INSTANCE)
{
continue;
}
if (feature == EcorePackage.Literals.EENUM_LITERAL__INSTANCE)
{
continue;
}
EReference eReference = feature instanceof EReference ? (EReference)feature : null;
if (COMPARE_CONTAINMENT)
{
if (feature == EcorePackage.Literals.EMODEL_ELEMENT__EANNOTATIONS)
{
continue;
}
if (feature == EcorePackage.Literals.ETYPED_ELEMENT__EGENERIC_TYPE)
{
continue;
}
if (feature == EcorePackage.Literals.ECLASS__EGENERIC_SUPER_TYPES)
{
continue;
}
}
else
{
if (eReference != null && eReference.isContainment())
{
continue;
}
}
Object newValue = newElement.eGet(feature);
if (eReference != null)
{
if (eReference.isMany())
{
@SuppressWarnings("unchecked")
EList<EObject> list = (EList<EObject>)newValue;
// TODO As an optimization, defer list creation.
EList<EObject> newList = new BasicEList<EObject>(list.size());
for (EObject eObject : list)
{
if (eObject instanceof EModelElement)
{
EModelElement newListElement = (EModelElement)eObject;
EModelElement oldListElement = newToOldElements.get(newListElement);
if (oldListElement != null && oldListElement != newListElement)
{
eObject = oldListElement;
}
}
newList.add(eObject);
}
newValue = newList;
}
else
{
if (newValue instanceof EModelElement)
{
EModelElement oldValue = newToOldElements.get(newValue);
if (oldValue != null && oldValue != newValue)
{
newValue = oldValue;
}
}
}
}
Object oldValue = oldElement.eGet(feature);
if (!ObjectUtil.equals(newValue, oldValue))
{
if (DEBUG_COMPARE)
{
System.out.println("PROPERTY CHANGE " + getLabel(newElement) + " --> " + feature.getName());
}
PropertyChange propertyChange = EvolutionFactory.eINSTANCE.createPropertyChange(feature, oldValue, newValue);
ElementChange elementChange = result.getElementChanges().get(newElement);
elementChange.getChildren().add(0, propertyChange);
}
}
}
private static void cleanupChanges(Change change)
{
for (Iterator<Change> it = change.getChildren().iterator(); it.hasNext();)
{
Change child = it.next();
if (child instanceof ElementChange)
{
ElementChange elementChange = (ElementChange)child;
cleanupChanges(elementChange);
if (elementChange.getKind() == ChangeKind.NONE && elementChange.getChildren().isEmpty())
{
if (DEBUG_CLEANUP)
{
System.out.println("CLEANUP " + getLabel(elementChange.getElement()));
}
it.remove();
}
}
}
}
private static Change getParentChange(ElementChange elementChange, ModelSetChange modelSetChange)
{
EModelElement element = elementChange.getElement();
EObject eContainer = element.eContainer();
if (eContainer instanceof EModelElement)
{
EModelElement parentElement = (EModelElement)eContainer;
ElementChange parentChange = modelSetChange.getElementChanges().get(parentElement);
if (parentChange != null)
{
return parentChange;
}
}
return modelSetChange;
}
private static ElementChange getElementChange(EModelElement newElement, EModelElement oldElement, ChangeKind kind,
Map<EModelElement, ElementChange> previousElementChanges)
{
if (previousElementChanges != null)
{
ElementChangeImpl elementChange = (ElementChangeImpl)previousElementChanges.remove(newElement);
if (elementChange == null)
{
elementChange = (ElementChangeImpl)previousElementChanges.remove(oldElement);
}
if (elementChange != null)
{
// Make sure that the ElementChange can't be used later through any other lookup.
for (Iterator<Map.Entry<EModelElement, ElementChange>> it = previousElementChanges.entrySet().iterator(); it.hasNext();)
{
Map.Entry<EModelElement, ElementChange> entry = it.next();
if (entry.getValue() == elementChange)
{
it.remove();
}
}
elementChange.setOldElement(oldElement);
elementChange.setNewElement(newElement);
elementChange.setKind(kind);
return elementChange;
}
}
return EvolutionFactory.eINSTANCE.createElementChange(oldElement, newElement, kind);
}
private static String getLabel(EModelElement element)
{
String label = ElementHandler.getLabel(element);
if (DEBUG_IDS)
{
String id = IDAnnotation.getValue(element);
if (id != null)
{
label += "[" + id + "]";
}
}
return label;
}
private static ModelSet[] createModelSetChain(ModelSet modelSetA, ModelSet modelSetB)
{
int versionA = modelSetA.getVersion();
int versionB = modelSetB.getVersion();
EList<ModelSet> result = new BasicEList<ModelSet>();
if (versionA < versionB)
{
result.add(modelSetA);
result.add(modelSetB);
}
else
{
result.add(modelSetB);
result.add(modelSetA);
}
ModelSet first = result.get(0);
ModelSet second;
while ((second = result.get(1).getPreviousRelease()) != first)
{
if (second == null)
{
break;
}
result.add(1, second);
}
return result.toArray(new ModelSet[result.size()]);
}
} // ModelSetImpl