package org.eclipse.uml2.diagram.common.sheet.chooser;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.uml2.uml.UMLPackage;
public class MetaclassContainmentFilter {
private static final boolean DEBUG = Boolean.FALSE.booleanValue();
private final EPackage myMetamodel;
private final EPackageRegistry myRegistry;
private final Map<EClass, Set<EClass>> myAncestorsOrSelfMap;
private final List<EReference> myContainments;
public MetaclassContainmentFilter(EPackage metamodel) {
this(metamodel, Collections.<EReference> emptyList());
}
public MetaclassContainmentFilter(EPackage metamodel, List<EReference> excludedContainments) {
myMetamodel = metamodel;
myRegistry = myMetamodel == UMLPackage.eINSTANCE ? EPackageRegistry.getUML() : new EPackageRegistry(myMetamodel);
myAncestorsOrSelfMap = new HashMap<EClass, Set<EClass>>();
myContainments = Collections.unmodifiableList(loadContainments(myMetamodel, excludedContainments));
}
private static List<EReference> loadContainments(EPackage metamodel, List<EReference> excludedContainments) {
List<EReference> result = new LinkedList<EReference>();
for (EClassifier nextClassifier : metamodel.getEClassifiers()) {
if (nextClassifier instanceof EClass) {
for (EStructuralFeature next : ((EClass) nextClassifier).getEStructuralFeatures()) {
if (next instanceof EReference) {
EReference reference = (EReference) next;
if (excludedContainments.contains(reference)) {
continue;
}
if (reference.isContainment() && !reference.isDerived()) {
result.add(reference);
}
}
}
}
}
return result;
}
public Set<EClass> getAncestorsOrSelf(EClass eClass) {
if (eClass.getEPackage() != myMetamodel) {
throw new IllegalArgumentException("Alien class: " + eClass + ", Expected metamodel: " + myMetamodel + ", Actual metamodel: " + eClass.getEPackage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
Set<EClass> result = myAncestorsOrSelfMap.get(eClass);
if (result == null) {
result = loadAncestorsOrSelf(eClass);
myAncestorsOrSelfMap.put(eClass, result);
}
return result;
}
private Set<EClass> loadAncestorsOrSelf(EClass eClass) {
HashSet<EClass> result = new HashSet<EClass>();
result.add(eClass);
debugAdding(eClass, null);
result.addAll(myRegistry.getSubTypes(eClass));
debugAddingSubtypes(eClass);
boolean scopeChanged = true;
while (scopeChanged) {
scopeChanged = false;
for (EReference containment : myContainments) {
EClass owner = containment.getEContainingClass();
if (result.contains(owner)) {
continue;
}
if (isCompatible(containment, result)) {
if (result.add(owner)) {
scopeChanged = true;
debugAdding(owner, containment);
result.addAll(myRegistry.getSubTypes(owner));
debugAddingSubtypes(owner);
}
}
}
}
return result;
}
private boolean isCompatible(EReference containment, Set<EClass> subtypesClosure) {
if (containment.getEType() instanceof EClass) {
EClass type = (EClass) containment.getEType();
if (subtypesClosure.contains(type)) {
return true;
}
Set<EClass> allSubtypesForType = myRegistry.getSubTypes(type);
if (!allSubtypesForType.isEmpty()) {
for (EClass next : subtypesClosure) {
if (allSubtypesForType.contains(next)) {
return true;
}
}
}
}
return false;
}
private void debugAdding(EClass owner, EReference containment) {
if (DEBUG) {
System.out.print("Adding: " + owner.getName()); //$NON-NLS-1$
if (containment != null) {
System.out.println(", due to: " + containment.getEContainingClass().getName() + "#" + containment.getName() + ":" + containment.getEType().getName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else {
System.out.println(", as root"); //$NON-NLS-1$
}
}
}
private void debugAddingSubtypes(EClass owner) {
if (!DEBUG) {
return;
}
Set<EClass> ownerSubtypes = myRegistry.getSubTypes(owner);
if (!ownerSubtypes.isEmpty()) {
System.out.println("\t processing subtypes: "); //$NON-NLS-1$
for (EClass subtype : ownerSubtypes) {
System.out.println("\t Adding: subtype: " + subtype.getName()); //$NON-NLS-1$
}
}
}
private static class EPackageRegistry {
private static EPackageRegistry UML_INSTANCE;
private final Map<EClass, Set<EClass>> mySubTypesMap = new HashMap<EClass, Set<EClass>>();
public EPackageRegistry(EPackage metamodel) {
for (EClassifier next : metamodel.getEClassifiers()) {
if (next instanceof EClass) {
registerSuperTypes((EClass) next);
}
}
}
public Set<EClass> getSubTypes(EClass eClass) {
Set<EClass> result = mySubTypesMap.get(eClass);
return result == null ? Collections.<EClass> emptySet() : result;
}
private void registerSuperTypes(EClass eClass) {
for (EClass nextSuper : eClass.getEAllSuperTypes()) {
if (nextSuper.getEPackage() != eClass.getEPackage()) {
continue;
}
Set<EClass> registry = mySubTypesMap.get(nextSuper);
if (registry == null) {
registry = new HashSet<EClass>();
mySubTypesMap.put(nextSuper, registry);
}
registry.add(eClass);
}
}
public static EPackageRegistry getUML() {
if (UML_INSTANCE == null) {
UML_INSTANCE = new EPackageRegistry(UMLPackage.eINSTANCE);
}
return UML_INSTANCE;
}
}
}