/**
* Copyright (c) 2007-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.util;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.change.ChangeDescription;
import org.eclipse.emf.ecore.change.ChangeFactory;
import org.eclipse.emf.ecore.change.FeatureChange;
import org.eclipse.emf.ecore.change.ListChange;
import org.eclipse.emf.ecore.change.ResourceChange;
import org.eclipse.emf.ecore.resource.Resource;
/**
* This class provides the basic methods required to implement a change recorder.
* @since 2.3
*/
public class BasicChangeRecorder extends ListDifferenceAnalyzer
{
protected boolean recording;
protected boolean recordingTransientFeatures = true;
protected ChangeDescription changeDescription;
/**
* @return true if this change recorder is recording or false otherwise.
*/
public boolean isRecording()
{
return recording;
}
protected void setRecording(boolean recording)
{
this.recording = recording;
}
/**
* @return true if this change recorder is recording transient features or false otherwise.
* @since 2.7
*/
public boolean isRecordingTransientFeatures()
{
return recordingTransientFeatures;
}
/**
* @since 2.7
*/
public void setRecordingTransientFeatures(boolean recordingTransientFeatures)
{
this.recordingTransientFeatures = recordingTransientFeatures;
}
protected ChangeDescription getChangeDescription()
{
return changeDescription;
}
protected void setChangeDescription(ChangeDescription changeDescription)
{
this.changeDescription = changeDescription;
}
/**
* Disposes this change recorder. This method ends a recording without
* consolidating the changes.
*/
public void dispose()
{
changeDescription = null;
}
/**
* <p>Summarizes the changes made to the analyzed objects on the {@link ChangeDescription change description}
* returned by the {@link #endRecording()} without ending the recording.</p>
*
* <p>This method doesn't do anything if this ChangeRecorder is not recording.</p>
*
* @return the {@link ChangeDescription} or <tt class="code">null</tt> if there is nothing being recorded.
*/
public ChangeDescription summarize()
{
if (isRecording())
{
consolidateChanges();
return getChangeDescription();
}
return null;
}
/**
* Ends the recording and consolidates the changes on the {@link ChangeDescription change description}.
* @return the {@link ChangeDescription} or <tt class="code">null</tt> if there is nothing being recorded.
*/
public ChangeDescription endRecording()
{
if (isRecording())
{
setRecording(false);
consolidateChanges();
return getChangeDescription();
}
return null;
}
/**
* Consolidates the changes that have happen since the last consolidation.
*/
protected void consolidateChanges()
{
ChangeDescription changeDescription = getChangeDescription();
for (Map.Entry<EObject, EList<FeatureChange>> entry : changeDescription.getObjectChanges())
{
EObject eObject = entry.getKey();
for (FeatureChange featureChange : entry.getValue())
{
finalizeChange(featureChange, eObject);
}
}
for (ResourceChange resourceChange : changeDescription.getResourceChanges())
{
finalizeChange(resourceChange);
}
eliminateEmptyChanges();
}
/**
* Eliminates changes that result in a state that's equal to the current state.
*/
protected void eliminateEmptyChanges()
{
ChangeDescription changeDescription = getChangeDescription();
for (Iterator<Map.Entry<EObject, EList<FeatureChange>>> i = changeDescription.getObjectChanges().iterator(); i.hasNext();)
{
Map.Entry<EObject, EList<FeatureChange>> entry = i.next();
EObject eObject = entry.getKey();
EList<FeatureChange> featureChanges = entry.getValue();
for (Iterator<FeatureChange> j = featureChanges.iterator(); j.hasNext(); )
{
FeatureChange featureChange = j.next();
EStructuralFeature feature = featureChange.getFeature();
if (featureChange.isSet() == eObject.eIsSet(feature))
{
Object value = featureChange.getValue();
Object eObjectValue = eObject.eGet(feature);
if (value == null ? eObject.eGet(feature) == null : value.equals(eObjectValue))
{
j.remove();
}
}
}
if (featureChanges.isEmpty())
{
i.remove();
}
}
for (Iterator<ResourceChange> i = changeDescription.getResourceChanges().iterator(); i.hasNext(); )
{
ResourceChange resourceChange = i.next();
if (resourceChange.getResource().getContents().equals(resourceChange.getValue()))
{
i.remove();
}
}
}
protected boolean shouldRecord(EStructuralFeature feature, EObject eObject)
{
return isRecording() &&
!feature.isDerived() &&
(isRecordingTransientFeatures() || !feature.isTransient()) &&
feature != EcorePackage.Literals.ECLASS__ESUPER_TYPES &&
feature != EcorePackage.Literals.ETYPED_ELEMENT__ETYPE &&
feature != EcorePackage.Literals.EOPERATION__EEXCEPTIONS &&
feature != EcorePackage.Literals.ECLASSIFIER__INSTANCE_CLASS_NAME;
}
protected void finalizeChange(ResourceChange change)
{
EList<Object> oldList = new BasicEList.FastCompare<Object>(change.getResource().getContents());
EList<?> newList = change.getValue();
change.getListChanges().clear();
createListChanges(oldList, newList, change.getListChanges());
}
protected void finalizeChange(FeatureChange change, EObject eObject)
{
if (change.isSet())
{
EStructuralFeature feature = change.getFeature();
if (feature.isMany())
{
EList<Object> oldList = new BasicEList<Object>((EList<?>)eObject.eGet(feature));
EList<?> newList = (EList<?>)change.getValue();
EList<ListChange> listChanges = change.getListChanges();
listChanges.clear();
createListChanges(oldList, newList, listChanges);
}
}
}
protected EList<ResourceChange> getResourceChanges()
{
return getChangeDescription().getResourceChanges();
}
protected ResourceChange getResourceChange(Resource resource)
{
List<ResourceChange> resourceChanges = getResourceChanges();
for (int i = 0, size = resourceChanges.size(); i < size;)
{
ResourceChange resourceChange = resourceChanges.get(i++);
if (resourceChange.getResource() == resource)
{
return resourceChange;
}
}
return null;
}
protected List<FeatureChange> getFeatureChanges(EObject eObject)
{
ChangeDescription changeDescription = getChangeDescription();
List<FeatureChange> featureChanges = changeDescription.getObjectChanges().get(eObject);
if (featureChanges == null)
{
Map.Entry<EObject, EList<FeatureChange>> entry = ChangeFactory.eINSTANCE.createEObjectToChangesMapEntry(eObject);
changeDescription.getObjectChanges().add(entry);
featureChanges = entry.getValue();
}
return featureChanges;
}
protected FeatureChange getFeatureChange(List<FeatureChange> featureChanges, EStructuralFeature eStructuralFeature)
{
for (int i = 0, size = featureChanges.size(); i < size;)
{
FeatureChange featureChange = featureChanges.get(i++);
if (featureChange.getFeature() == eStructuralFeature)
{
return featureChange;
}
}
return null;
}
protected FeatureChange createFeatureChange(EObject eObject, EStructuralFeature eStructuralFeature, Object value, boolean isSet)
{
return ChangeFactory.eINSTANCE.createFeatureChange(eStructuralFeature, value, isSet);
}
protected ResourceChange createResourceChange(Resource resource, EList<Object> value)
{
return ChangeFactory.eINSTANCE.createResourceChange(resource, value);
}
protected ChangeDescription createChangeDescription()
{
return ChangeFactory.eINSTANCE.createChangeDescription();
}
}