package de.hub.emffrag.fragmentation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EEnum; import org.eclipse.emf.ecore.EFactory; 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.impl.EClassImpl; import org.eclipse.emf.ecore.impl.EFactoryImpl; import org.eclipse.emf.ecore.util.EcoreUtil; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import de.hub.emffrag.util.EMFFragUtil; import de.hub.emffrag.util.EMFFragUtil.FragmentationType; /** * Since EMF does not allow to use the same model in a regular fashion * (generated classes) and purely reflective fashion, we need to maintain two * versions of the same meta-model. One is used by clients/users as the regular * meta-model. The other is used internally by EMF-Fragments as a purely * reflective version. * * This class allows to manage meta-models and their two versions and access the * corresponding opposite elements of the other meta-model. * */ public class ReflectiveMetaModelRegistry extends HashMap<String, Object> implements EPackage.Registry { private static final long serialVersionUID = 1L; public static final ReflectiveMetaModelRegistry instance = new ReflectiveMetaModelRegistry(); private ReflectiveMetaModelRegistry() { } private final Map<String, EPackage> userMetaModelRegistry = new HashMap<String, EPackage>(); private final Multimap<EClass, EReference> incomingIndexedReferences = HashMultimap.create(); private final Map<EReference, Integer> inverseReferenceIds = new HashMap<EReference, Integer>(); public EPackage registerUserMetaModel(EPackage source) { String nsURI = source.getNsURI(); EPackage target = (EPackage)get(nsURI); if (target == null) { target = generateReflectiveMetaModel(source); } return target; } @Override public EPackage getEPackage(String nsURI) { EPackage target = (EPackage)get(nsURI); if (target == null) { EPackage source = EPackage.Registry.INSTANCE.getEPackage(nsURI); if (source != null) { target = registerUserMetaModel(source); } } return target; } @Override public EFactory getEFactory(String nsURI) { return getEPackage(nsURI).getEFactoryInstance(); } public EClass getInternalClass(EClass theClass) { return getOtherClass(this, theClass); } public EStructuralFeature getInternalFeature(EStructuralFeature feature) { return getOtherFeature(this, feature); } public EPackage getInternalMetaModel(EPackage thePackage) { return getOtherMetaModel(this, thePackage); } private EClass getOtherClass(Map<String, ? extends Object> mapping, EClass theClass) { EPackage metaModel = getOtherMetaModel(mapping, theClass.getEPackage()); return metaModel == null ? null : (EClass)metaModel.getEClassifier(theClass.getName()); // TODO performance } private EStructuralFeature getOtherFeature(Map<String, ? extends Object> mapping, EStructuralFeature feature) { EClass theClass = getOtherClass(mapping, feature.getEContainingClass()); return theClass == null ? null : theClass.getEStructuralFeature(feature.getName()); // TODO performance } private EPackage getOtherMetaModel(Map<String, ? extends Object> mapping, EPackage thePackage) { return (EPackage)mapping.get(thePackage.getNsURI()); } public EStructuralFeature getRegularFeature(EStructuralFeature feature) { return getOtherFeature(userMetaModelRegistry, feature); } public EPackage getUserMetaModel(EPackage thePackage) { return getOtherMetaModel(userMetaModelRegistry, thePackage); } public EClass getUserClass(EClass theClass) { return getOtherClass(userMetaModelRegistry, theClass); } private EPackage generateReflectiveMetaModel(final EPackage source) { EPackage target = EcoreUtil.copy(source); put(source.getNsURI(), target); userMetaModelRegistry.put(source.getNsURI(), source); // check and modify target properties { TreeIterator<EObject> iterator = target.eAllContents(); while (iterator.hasNext()) { EObject next = iterator.next(); if (next instanceof EClass) { ((EClassImpl) next).setGeneratedInstanceClass(false); ((EClass) next).setInstanceClass(null); EList<EClass> superTypes = ((EClass)next).getESuperTypes(); List<Integer> superTypesToChange = new ArrayList<Integer>(superTypes.size()); int index = 0; for (EClass superType: superTypes) { if (superType.getEPackage() != target) { superTypesToChange.add(index); } index++; } for (Integer superTypeIndex: superTypesToChange) { EClass superType = superTypes.get(superTypeIndex); registerUserMetaModel(superType.getEPackage()); superTypes.set(superTypeIndex, getInternalClass(superType)); } superTypes.add(InternalPackage.eINSTANCE.getFInternalObject()); } else if (next instanceof EDataType) { EDataType sourceDataType = (EDataType)source.getEClassifier(((EDataType) next).getName()); ((EDataType) next).setInstanceTypeName(sourceDataType.getInstanceTypeName()); } else if (next instanceof EReference) { FragmentationType fragmentationType = EMFFragUtil.getFragmentationType((EReference)next); EReference reference = (EReference)next; if (reference.isContainment() && fragmentationType == FragmentationType.None) { reference.setResolveProxies(false); } else if (fragmentationType == FragmentationType.FragmentsIndexedContainment || fragmentationType == FragmentationType.IndexedReferences) { reference.setUnique(false); reference.setTransient(true); incomingIndexedReferences.put((EClass)reference.getEType(), reference); } EClassifier type = reference.getEType(); if (type instanceof EClass) { registerUserMetaModel(((EClass)type).getEPackage()); reference.setEType(getInternalClass((EClass)type)); } } } } // check and modify source properties { TreeIterator<EObject> iterator = source.eAllContents(); while (iterator.hasNext()) { EObject next = iterator.next(); if (next instanceof EReference) { FragmentationType fragmentationType = EMFFragUtil.getFragmentationType((EReference)next); EReference reference = (EReference)next; if (fragmentationType == FragmentationType.FragmentsIndexedContainment || fragmentationType == FragmentationType.IndexedReferences) { reference.setUnique(false); } } } } EFactory targetFactory = new InternalEFactoryImpl(source.getEFactoryInstance()); targetFactory.setEPackage(target); target.setEFactoryInstance(targetFactory); return target; } private static class InternalEFactoryImpl extends EFactoryImpl { private final EFactory sourceFactory; public InternalEFactoryImpl(EFactory sourceFactory) { super(); this.sourceFactory = sourceFactory; } @Override public EObject create(EClass eClass) { return new FInternalObjectImpl(eClass); } @Override public Object createFromString(EDataType eDataType, String stringValue) { if (eDataType instanceof EEnum) { return sourceFactory.createFromString(eDataType, stringValue); } else { return super.createFromString(eDataType, stringValue); } } } public int getInverseReferenceIndex(EReference reference) { Integer index = inverseReferenceIds.get(reference); if (index == null) { Collection<EReference> collection = incomingIndexedReferences.get((EClass)reference.getEType()); ArrayList<EReference> sortedIncomingReferences = new ArrayList<EReference>(collection); Collections.sort(sortedIncomingReferences, new Comparator<EReference>() { private String getQualifier(EReference reference) { return reference.getEContainingClass().getName() + "." + reference.getName(); } @Override public int compare(EReference o1, EReference o2) { return getQualifier(o1).compareTo(getQualifier(o2)); } }); index = sortedIncomingReferences.indexOf(reference); inverseReferenceIds.put(reference, index); } return index; } @Override public void clear() { super.clear(); userMetaModelRegistry.clear(); } }