blob: 868195d6820a44e7e237d4ec87c98926696fe28f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 CEA LIST.
*
* 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:
* CEA LIST - Initial API and implementation
*******************************************************************************/
package org.eclipse.papyrus.requirements.reqif.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
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;
/**
* A mapping building traverser of a collection of {@link EObject#eAllContents content trees};
* the map is from {@link EObject} to <code>EObject</code>, i.e., from original to copy;
* use {@link EcoreUtil#copy EcoreUtil.copy} or {@link EcoreUtil#copyAll EcoreUtil.copyAll} to do routine copies.
* Since this implementation extends a Map implementation, it acts as the result of the over all copy.
* The client can call {@link #copy copy} and {@link #copyAll copyAll} repeatedly.
* When all the objects have been copied,
* the client should call {@link #copyReferences copyReferences}
* to copy the {@link #copyReference appropriate} {@link EObject#eCrossReferences cross references}.
*<pre>
* Copier copier = new Copier();
* EObject result = copier.copy(eObject);
* Collection results = copier.copyAll(eObjects);
* copier.copyReferences();
*</pre>
* The copier delegates to {@link #copyContainment copyContainment}, {@link #copyAttribute copyAttribute} during the copy phase
* and to {@link #copyReference copyReference}, during the cross reference phase.
* This allows tailored handling through derivation.
*/
public class CopierWithoutContainment extends LinkedHashMap<EObject, EObject>
{
private static final long serialVersionUID = 1L;
/**
* Whether proxies should be resolved during copying.
*/
protected boolean resolveProxies = true;
/**
* Whether non-copied references should be used during copying.
*/
protected boolean useOriginalReferences = true;
/**
* Creates an instance.
*/
public CopierWithoutContainment()
{
super();
}
/**
* Creates an instance that resolves proxies or not as specified.
* @param resolveProxies whether proxies should be resolved while copying.
*/
public CopierWithoutContainment(boolean resolveProxies)
{
this.resolveProxies = resolveProxies;
}
/**
* Creates an instance that resolves proxies or not and uses non-copied references or not as specified.
* @param resolveProxies whether proxies should be resolved while copying.
* @param useOriginalReferences whether non-copied references should be used while copying.
*/
public CopierWithoutContainment(boolean resolveProxies, boolean useOriginalReferences)
{
this.resolveProxies = resolveProxies;
this.useOriginalReferences = useOriginalReferences;
}
/**
* Returns a collection containing a copy of each EObject in the given collection.
* @param eObjects the collection of objects to copy.
* @return the collection of copies.
*/
public <T> Collection<T> copyAll(Collection<? extends T> eObjects)
{
Collection<T> result = new ArrayList<T>(eObjects.size());
for (Object object : eObjects)
{
@SuppressWarnings("unchecked") T t = (T)copy((EObject)object);
if (t != null)
{
result.add(t);
}
}
return result;
}
/**
* Returns a copy of the given eObject.
* @param eObject the object to copy.
* @return the copy.
*/
public EObject copy(EObject eObject)
{
if (eObject == null)
{
return null;
}
else
{
EObject copyEObject = createCopy(eObject);
if (copyEObject != null)
{
put(eObject, copyEObject);
EClass eClass = eObject.eClass();
for (int i = 0, size = eClass.getFeatureCount(); i < size; ++i)
{
EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(i);
if (eStructuralFeature.isChangeable() && !eStructuralFeature.isDerived())
{
if (eStructuralFeature instanceof EAttribute)
{
copyAttribute((EAttribute)eStructuralFeature, eObject, copyEObject);
}
else
{
EReference eReference = (EReference)eStructuralFeature;
if (eReference.isContainment())
{
//copyContainment(eReference, eObject, copyEObject);
}
}
}
}
copyProxyURI(eObject, copyEObject);
}
return copyEObject;
}
}
/**
* Copies the proxy URI from the original to the copy, if present.
* @param eObject the object being copied.
* @param copyEObject the copy being initialized.
*/
protected void copyProxyURI(EObject eObject, EObject copyEObject)
{
if (eObject.eIsProxy())
{
((InternalEObject)copyEObject).eSetProxyURI(((InternalEObject)eObject).eProxyURI());
}
}
/**
* Returns a new instance of the object's target class.
* @param eObject the object to copy.
* @return a new instance of the target class.
* @see #getTarget(EObject)
* @see EcoreUtil#create(EClass)
*/
protected EObject createCopy(EObject eObject)
{
EClass eClass = getTarget(eObject);
return eClass == null ? null : EcoreUtil.create(eClass);
}
/**
* Returns the target class used to create a copy instance for the given instance object.
* @param eObject the object to be copied.
* @return the target class used to create a copy instance.
* @since 2.10
*/
protected EClass getTarget(EObject eObject)
{
return getTarget(eObject.eClass());
}
/**
* Returns the target class used to create a copy instance for objects of the given source class.
* @param eClass the source class.
* @return the target class used to create a copy instance.
* @see #getTarget(EStructuralFeature, EObject, EObject)
*/
protected EClass getTarget(EClass eClass)
{
return eClass;
}
/**
* Returns a setting for the feature and copy instance to be populated with the original object's source feature's value.
* @param eStructuralFeature the source feature.
* @return the target feature used to populate a copy instance.
* @see #getTarget(EStructuralFeature)
* @see #getTarget(EObject)
* @since 2.10
*/
protected EStructuralFeature.Setting getTarget(EStructuralFeature eStructuralFeature, EObject eObject, EObject copyEObject)
{
EStructuralFeature targetEStructuralFeature = getTarget(eStructuralFeature);
return targetEStructuralFeature == null ? null : ((InternalEObject)copyEObject).eSetting(targetEStructuralFeature);
}
/**
* Returns the target feature used to populate a copy instance from the given source feature.
* @param eStructuralFeature the source feature.
* @return the target feature used to populate a copy instance.
* @see #getTarget(EClass)
*/
protected EStructuralFeature getTarget(EStructuralFeature eStructuralFeature)
{
return eStructuralFeature;
}
/**
* Called to handle the copying of a containment feature;
* this adds a list of copies or sets a single copy as appropriate for the multiplicity.
* @param eReference the feature to copy.
* @param eObject the object from which to copy.
* @param copyEObject the object to copy to.
*/
protected void copyContainment(EReference eReference, EObject eObject, EObject copyEObject)
{
if (eObject.eIsSet(eReference))
{
EStructuralFeature.Setting setting = getTarget(eReference, eObject, copyEObject);
if (setting != null)
{
Object value = eObject.eGet(eReference);
if (eReference.isMany())
{
@SuppressWarnings("unchecked")
List<EObject> target = (List<EObject>)value;
setting.set(copyAll(target));
}
else
{
setting.set(copy((EObject)value));
}
}
}
}
/**
* Called to handle the copying of an attribute;
* this adds a list of values or sets a single value as appropriate for the multiplicity.
* @param eAttribute the attribute to copy.
* @param eObject the object from which to copy.
* @param copyEObject the object to copy to.
*/
protected void copyAttribute(EAttribute eAttribute, EObject eObject, EObject copyEObject)
{
if (eObject.eIsSet(eAttribute))
{
if (FeatureMapUtil.isFeatureMap(eAttribute))
{
FeatureMap featureMap = (FeatureMap)eObject.eGet(eAttribute);
copyFeatureMap(featureMap);
}
else
{
EStructuralFeature.Setting setting = getTarget(eAttribute, eObject, copyEObject);
if (setting != null)
{
copyAttributeValue(eAttribute, eObject, eObject.eGet(eAttribute), setting);
}
}
}
}
/**
* Call to handle copying the contained objects within a feature map.
* @param featureMap the feature map the copy.
* @since 2.10
*/
protected void copyFeatureMap(FeatureMap featureMap)
{
for (int i = 0, size = featureMap.size(); i < size; ++i)
{
EStructuralFeature feature = featureMap.getEStructuralFeature(i);
if (feature instanceof EReference && ((EReference)feature).isContainment())
{
Object value = featureMap.getValue(i);
if (value != null)
{
// The containment references are hooked up later during copyReferences.
//
copy((EObject)value);
}
}
}
}
/**
* Called to handle copying of an attribute's value to the target setting.
* @param eAttribute the attribute of the source object corresponding to the value.
* @param eObject the object being copied.
* @param value the value to be copied.
* @param setting the feature-value pair that is the target of of the copy.
* @since 2.10
*/
protected void copyAttributeValue(EAttribute eAttribute, EObject eObject, Object value, EStructuralFeature.Setting setting)
{
setting.set(value);
}
/**
* Hooks up cross references; it delegates to {@link #copyReference copyReference}.
*/
public void copyReferences()
{
for (Map.Entry<EObject, EObject> entry : entrySet())
{
EObject eObject = entry.getKey();
EObject copyEObject = entry.getValue();
EClass eClass = eObject.eClass();
for (int j = 0, size = eClass.getFeatureCount(); j < size; ++j)
{
EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(j);
if (eStructuralFeature.isChangeable() && !eStructuralFeature.isDerived())
{
if (eStructuralFeature instanceof EReference)
{
EReference eReference = (EReference)eStructuralFeature;
if (!eReference.isContainment() && !eReference.isContainer())
{
copyReference(eReference, eObject, copyEObject);
}
}
else if (FeatureMapUtil.isFeatureMap(eStructuralFeature))
{
FeatureMap copyFeatureMap = (FeatureMap)getTarget(eStructuralFeature, eObject, copyEObject);
if (copyFeatureMap != null)
{
FeatureMap featureMap = (FeatureMap)eObject.eGet(eStructuralFeature);
int copyFeatureMapSize = copyFeatureMap.size();
for (int k = 0, featureMapSize = featureMap.size(); k < featureMapSize; ++k)
{
EStructuralFeature feature = featureMap.getEStructuralFeature(k);
if (feature instanceof EReference)
{
Object referencedEObject = featureMap.getValue(k);
Object copyReferencedEObject = get(referencedEObject);
if (copyReferencedEObject == null && referencedEObject != null)
{
EReference reference = (EReference)feature;
if (!useOriginalReferences || reference.isContainment() || reference.getEOpposite() != null)
{
continue;
}
copyReferencedEObject = referencedEObject;
}
// If we can't add it, it must already be in the list so find it and move it to the end.
//
if (!copyFeatureMap.add(feature, copyReferencedEObject))
{
for (int l = 0; l < copyFeatureMapSize; ++l)
{
if (copyFeatureMap.getEStructuralFeature(l) == feature && copyFeatureMap.getValue(l) == copyReferencedEObject)
{
copyFeatureMap.move(copyFeatureMap.size() - 1, l);
--copyFeatureMapSize;
break;
}
}
}
}
else
{
copyFeatureMap.add(getTarget(featureMap.getEStructuralFeature(k)), featureMap.getValue(k));
}
}
}
}
}
}
}
}
/**
* Called to handle the copying of a cross reference;
* this adds values or sets a single value as appropriate for the multiplicity
* while omitting any bidirectional reference that isn't in the copy map.
* @param eReference the reference to copy.
* @param eObject the object from which to copy.
* @param copyEObject the object to copy to.
*/
protected void copyReference(EReference eReference, EObject eObject, EObject copyEObject)
{
if (eObject.eIsSet(eReference))
{
EStructuralFeature.Setting setting = getTarget(eReference, eObject, copyEObject);
if (setting != null)
{
Object value = eObject.eGet(eReference, resolveProxies);
if (eReference.isMany())
{
@SuppressWarnings("unchecked") InternalEList<EObject> source = (InternalEList<EObject>)value;
@SuppressWarnings("unchecked") InternalEList<EObject> target = (InternalEList<EObject>)setting;
if (source.isEmpty())
{
target.clear();
}
else
{
boolean isBidirectional = eReference.getEOpposite() != null;
int index = 0;
for (Iterator<EObject> k = resolveProxies ? source.iterator() : source.basicIterator(); k.hasNext();)
{
EObject referencedEObject = k.next();
EObject copyReferencedEObject = get(referencedEObject);
if (copyReferencedEObject == null)
{
if (useOriginalReferences && !isBidirectional)
{
target.addUnique(index, referencedEObject);
++index;
}
}
else
{
if (isBidirectional)
{
int position = target.indexOf(copyReferencedEObject);
if (position == -1)
{
target.addUnique(index, copyReferencedEObject);
}
else if (index != position)
{
target.move(index, copyReferencedEObject);
}
}
else
{
target.addUnique(index, copyReferencedEObject);
}
++index;
}
}
}
}
else
{
if (value == null)
{
setting.set(null);
}
else
{
Object copyReferencedEObject = get(value);
if (copyReferencedEObject == null)
{
if (useOriginalReferences && eReference.getEOpposite() == null)
{
setting.set(value);
}
}
else
{
setting.set(copyReferencedEObject);
}
}
}
}
}
}
}