/** * <copyright> * * Copyright (c) 2009, 2010 Springsite BV (The Netherlands) 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: * Martin Taal - Initial API and implementation * * </copyright> * * $Id: EMFModelConverter.java,v 1.23 2011/08/29 05:16:04 mtaal Exp $ */ package org.eclipse.emf.texo.converter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; 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.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.util.FeatureMapUtil; import org.eclipse.emf.texo.component.ComponentProvider; import org.eclipse.emf.texo.component.TexoComponent; import org.eclipse.emf.texo.model.DynamicModelObject; import org.eclipse.emf.texo.model.ModelFeatureMapEntry; import org.eclipse.emf.texo.model.ModelObject; import org.eclipse.emf.texo.model.ModelPackage; import org.eclipse.emf.texo.model.ModelResolver; import org.eclipse.emf.texo.utils.ModelUtils; /** * Capable of copying objects and their children and references. * * This object can be used for subsequent copy actions, internal data structures can/should be cleaned using the clear * method in between copy actions. * * As a default referenced objects are not copied, use the {@link #setCopyChildren(boolean)} or * {@link #setCopyReferences(boolean)} methods to enable copying of contained and not contained objects. * * If referenced objects are also copied then these referenced objects are at most copied once, so if multiple objects * refer to another object (and that other object is also copied) then that other object is only copied once. * * The complete set of copied objects (including copied referenced objects) can be obtained through the * {@link #getAllCopiedObjects()} method. * * @author <a href="mtaal@elver.org">Martin Taal</a> * @see ModelObject */ public class ObjectCopier implements TexoComponent { private Map<Object, Object> sourceTargetMap = new HashMap<Object, Object>(); private boolean copyChildren = false; private boolean copyReferences = false; /** * Copy all objects in the sourceList, * * @param sourceList * @return */ public List<Object> copyAll(List<Object> sourceList) { final List<Object> result = new ArrayList<Object>(); for (Object source : sourceList) { result.add(copy(source)); } return result; } public Object copy(Object source) { if (source == null) { return null; } if (sourceTargetMap.containsKey(source)) { return sourceTargetMap.get(source); } if (source instanceof DynamicModelObject) { final DynamicModelObject target = ComponentProvider.getInstance().newInstance(DynamicModelObject.class); target.setData((DynamicModelObject) source); sourceTargetMap.put(source, target); return target; } @SuppressWarnings("unchecked") final ModelObject<Object> sourceModelObject = (ModelObject<Object>) ModelResolver.getInstance().getModelObject( source); final ModelPackage modelPackage = ModelResolver.getInstance().getModelPackage( sourceModelObject.eClass().getEPackage().getNsURI()); final Object target = modelPackage.getModelFactory().create(sourceModelObject.eClass()); @SuppressWarnings("unchecked") final ModelObject<Object> targetModelObject = (ModelObject<Object>) ModelResolver.getInstance().getModelObject( target); sourceTargetMap.put(source, target); for (EStructuralFeature eFeature : sourceModelObject.eClass().getEAllStructuralFeatures()) { if (eFeature.isVolatile()) { continue; } if (FeatureMapUtil.isFeatureMap(eFeature)) { copyFeatureMap(sourceModelObject, targetModelObject, eFeature); } else if (ModelUtils.isEMap(eFeature)) { copyMap(sourceModelObject, targetModelObject, (EReference) eFeature); } else if (eFeature.isMany()) { if (eFeature instanceof EAttribute) { copyManyEAttribute(sourceModelObject, targetModelObject, (EAttribute) eFeature); } else { copyManyEReference(sourceModelObject, targetModelObject, (EReference) eFeature); } } else if (eFeature instanceof EAttribute) { copySingleEAttribute(sourceModelObject, targetModelObject, (EAttribute) eFeature); } else { copySingleEReference(sourceModelObject, targetModelObject, (EReference) eFeature); } } return target; } protected void copyManyEAttribute(ModelObject<Object> source, ModelObject<Object> target, EAttribute eAttribute) { for (Object value : (Collection<?>) source.eGet(eAttribute)) { target.eAddTo(eAttribute, value); } } protected void copySingleEAttribute(ModelObject<Object> source, ModelObject<Object> target, EAttribute eAttribute) { target.eSet(eAttribute, source.eGet(eAttribute)); } protected void copyManyEReference(ModelObject<Object> source, ModelObject<Object> target, EReference eReference) { for (Object value : (Collection<?>) source.eGet(eReference)) { if (eReference.isContainment() && copyChildren) { target.eAddTo(eReference, copy(value)); } else if (!eReference.isContainment() && copyReferences) { target.eAddTo(eReference, copy(value)); } else { target.eAddTo(eReference, value); } } } protected void copySingleEReference(ModelObject<Object> source, ModelObject<Object> target, EReference eReference) { final Object value = source.eGet(eReference); if (eReference.isContainment() && copyChildren) { target.eSet(eReference, copy(value)); } else if (!eReference.isContainment() && copyReferences) { target.eSet(eReference, copy(value)); } else { target.eSet(eReference, value); } } protected void copyMap(ModelObject<Object> source, ModelObject<Object> target, EReference eReference) { final Map<?, ?> sourceMap = (Map<?, ?>) source.eGet(eReference); @SuppressWarnings("unchecked") final Map<Object, Object> targetMap = (Map<Object, Object>) target.eGet(eReference); final EClass mapEClass = eReference.getEReferenceType(); final EStructuralFeature valueFeature = mapEClass.getEStructuralFeature("value"); //$NON-NLS-1$ final EStructuralFeature keyFeature = mapEClass.getEStructuralFeature("key"); //$NON-NLS-1$ final boolean copyKey = keyFeature instanceof EReference && copyReference((EReference) keyFeature); final boolean copyValue = valueFeature instanceof EReference && copyReference((EReference) valueFeature); for (Object key : sourceMap.keySet()) { Object value = sourceMap.get(key); if (copyKey) { key = copy(key); } if (copyValue) { value = copy(value); } targetMap.put(key, value); } } private boolean copyReference(EReference eReference) { return copyChildren && eReference.isContainment() || copyReferences && !eReference.isContainment(); } protected void copyFeatureMap(ModelObject<Object> source, ModelObject<Object> target, EStructuralFeature eFeature) { @SuppressWarnings("unchecked") final Collection<Object> targetCollection = (Collection<Object>) target.eGet(eFeature); for (Object sourceEntry : (Collection<?>) source.eGet(eFeature)) { final ModelFeatureMapEntry<?> sourceModelEntry = ModelResolver.getInstance().getModelFeatureMapEntry(eFeature, sourceEntry); final Object targetEntry = ModelResolver.getInstance().createFeatureMapEntry(eFeature); final ModelFeatureMapEntry<?> targetModelEntry = ModelResolver.getInstance().getModelFeatureMapEntry(eFeature, targetEntry); final EStructuralFeature eFMFeature = sourceModelEntry.getEStructuralFeature(); Object value = sourceModelEntry.getValue(); if (eFMFeature instanceof EReference && copyReference((EReference) eFMFeature)) { value = copy(value); } targetModelEntry.setEStructuralFeature(eFMFeature); targetModelEntry.setValue(value); targetCollection.add(targetEntry); } } public void clear() { sourceTargetMap.clear(); } public Collection<Object> getAllCopiedObjects() { return sourceTargetMap.values(); } public Map<Object, Object> getSourceTargetMap() { return sourceTargetMap; } public void setSourceTargetMap(Map<Object, Object> sourceTargetMap) { this.sourceTargetMap = sourceTargetMap; } /** * If true then also children (referenced using an {@link EReference} with {@link EReference#isContainment()} true) * are also copied. */ public boolean isCopyChildren() { return copyChildren; } public void setCopyChildren(boolean copyChildren) { this.copyChildren = copyChildren; } /** * If true then also non-containment references (referenced using an {@link EReference} with * {@link EReference#isContainment()} false) are also copied. */ public boolean isCopyReferences() { return copyReferences; } public void setCopyReferences(boolean copyReferences) { this.copyReferences = copyReferences; } }