/*******************************************************************************
* Copyright (c) 2006-2013 The RCP Company 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:
* The RCP Company - initial API and implementation
*******************************************************************************/
package com.rcpcompany.uibindings.model.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.command.IdentityCommand;
import org.eclipse.emf.common.command.UnexecutableCommand;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EPackage.Registry;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EObjectEList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.MoveCommand;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
/**
* Various Ecore oriented utility methods.
*
* @author Tonny Madsen, The RCP Company
*/
public final class EcoreExtendedUtils {
/**
* Synchronizes the information from the <code>source</code> object into the <code>target</code>
* object.
* <p>
* The sync is potentially destructive for the <code>source</code> list.
*
* @param target the object synchronized into
* @param source the object synchronized from
*/
public static <T extends EObject> SyncController sync(EditingDomain domain, EList<T> target, List<T> source) {
final SyncController controller = new SyncController(source);
controller.setEditingDomain(domain);
controller.sync(target, source);
controller.assertFullMappings();
controller.handleDeferredOperations();
controller.commit();
return controller;
}
/**
* Synchronizes the information from the <code>source</code> object into the <code>target</code>
* object.
* <p>
* The sync is potentially destructive for the <code>source</code> object.
*
* @param domain to editing domain used for all changes - created if not specified
* @param target the object synchronized into
* @param source the object synchronized from
* @return the controller used for all the changes
*/
public static <T extends EObject> SyncController sync(EditingDomain domain, T target, T source) {
return sync(domain, target, source, null);
}
/**
* Synchronizes the information from the <code>source</code> object into the <code>target</code>
* object.
* <p>
* The sync is potentially destructive for the <code>source</code> object.
*
* @param domain to editing domain used for all changes - created if not specified
* @param target the object synchronized into
* @param source the object synchronized from
* @param ignoredFeatures a List of features that will be ignored in the synchronization
* @return the controller used for all the changes
*/
public static <T extends EObject> SyncController sync(EditingDomain domain, T target, T source,
List<EStructuralFeature> ignoredFeatures) {
Assert.isTrue(source.eClass() == target.eClass(), "target and source must have exactly the same types");
final SyncController controller = new SyncController(source);
controller.setEditingDomain(domain);
if (ignoredFeatures != null) {
for (final EStructuralFeature sf : ignoredFeatures) {
controller.addIgnoredFeature(sf);
}
}
controller.sync(target, source);
controller.assertFullMappings();
controller.handleDeferredOperations();
controller.commit();
return controller;
}
/**
* Returns a string that describes the specified command is clear human readable text.
*
* @param c the command
* @return the clear text description
*/
public static String toString(Command c) {
if (c == null) return "<null>";
final StringBuilder sb = new StringBuilder(50);
sb.append(c.getClass().getSimpleName()).append('(');
if (c instanceof IdentityCommand) {
} else if (c instanceof UnexecutableCommand) {
} else if (c instanceof AddCommand) {
final AddCommand cc = (AddCommand) c;
sb.append(getEObjectName(cc.getOwner())).append(", ").append(cc.getFeature().getName()).append(", ")
.append(toString(cc.getCollection()));
if (cc.getFeature().isMany() && cc.getIndex() != -1) {
sb.append(", ").append(cc.getIndex());
}
} else if (c instanceof RemoveCommand) {
final RemoveCommand cc = (RemoveCommand) c;
sb.append(getEObjectName(cc.getOwner())).append(", ").append(cc.getFeature().getName()).append(", ")
.append(toString(cc.getCollection()));
} else if (c instanceof MoveCommand) {
final MoveCommand cc = (MoveCommand) c;
sb.append(getEObjectName(cc.getOwner())).append(", ").append(cc.getFeature().getName()).append(", ")
.append(toString(cc.getValue())).append(", ").append(cc.getIndex());
} else if (c instanceof SetCommand) {
final SetCommand cc = (SetCommand) c;
sb.append(getEObjectName(cc.getOwner())).append(", ").append(cc.getFeature().getName()).append(", ")
.append(formatSetCommandArg(cc.getOldValue())).append(", ")
.append(formatSetCommandArg(cc.getValue()));
if (cc.getFeature().isMany()) {
sb.append(", ").append(cc.getIndex());
}
} else if (c instanceof CompoundCommand) {
final CompoundCommand cc = (CompoundCommand) c;
boolean first = true;
for (final Command ic : cc.getCommandList()) {
if (!first) {
sb.append(", ");
}
sb.append(toString(ic));
first = false;
}
// } else if (c instanceof ContainerDragAndDropCommand) {
// final ContainerDragAndDropCommand cc = (ContainerDragAndDropCommand) c;
// sb.append(toString(cc.getDragCommand())).append(", ").append(toString(cc.getDropCommand()));
} else {
sb.append("...");
}
sb.append(')');
return sb.toString();
}
private static String toString(Object o) {
final StringBuilder sb = new StringBuilder(200);
if (o instanceof EObject) {
sb.append(o);
// sb.append(IBindingObjectInformation.Factory.getLongName((EObject) o));
} else {
sb.append(o);
}
return sb.toString();
}
private static String toString(Collection<?> collection) {
final StringBuilder sb = new StringBuilder(200);
sb.append('[');
for (final Object o : collection) {
if (sb.length() > 1) {
sb.append(", ");
}
sb.append(toString(o));
}
sb.append(']');
return sb.toString();
}
private static String formatSetCommandArg(Object oldValue) {
return (oldValue == SetCommand.UNSET_VALUE) ? "<default> " : ("" + oldValue);
}
private static String getEObjectName(EObject owner) {
return "" + owner; // IBindingObjectInformation.Factory.getQualifiedName(owner);
}
/**
* This class is used to control a synchronize operation in
* {@link EcoreExtUtils#sync(EObject, EObject)} and {@link EcoreExtUtils#sync(EList, EList)}.
*/
public static class SyncController {
private EditingDomain myEditingDomain;
/**
* A compound command that collects all the created commands.
*/
private CompoundCommand myCommitCommand;
public <T extends EObject> SyncController(T source) {
addSourceMappingObjects(source);
}
public <T extends EObject> SyncController(List<T> source) {
for (final T o : source) {
addSourceMappingObjects(o);
}
}
/**
* A mapping of all contained objects in the source tree to the corresponding objects in the
* target tree.
* <p>
* Until an object in the source tree is first encountered, the target object for a source
* object in the map is set to <code>null</code>.
* <p>
* Initially the map is seeded from the contained objects in the source tree.
* <p>
* Basically the target of the mapping is filled in when a new containment relation is
* encountered (see {@link #registerMapping(EObject, EObject)}) and used when
* non-containment (pure reference) relations are encountered (see #).
*/
private final Map<EObject, EObject> sourceToTargetMapping = new HashMap<EObject, EObject>(200);
/**
* A list of sync operations that has been deferred until all containment relations have
* been handled.
*/
private final List<DeferedSyncRecord<EObject>> myDeferedSyncRecords = new ArrayList<DeferedSyncRecord<EObject>>(
200);
/**
* Adds all the objects in the source tree based on the specified root to the set of source
* objects.
*
* @param sourceTreeRoot the root
*/
private <T extends EObject> void addSourceMappingObjects(T sourceTreeRoot) {
final TreeIterator<EObject> contents = EcoreUtil.getAllContents(sourceTreeRoot, true);
while (contents.hasNext()) {
sourceToTargetMapping.put(contents.next(), null);
}
}
/**
* Registers a new mapping between source and target objects.
*
* @param target the target object
* @param source the source object
*/
private <T extends EObject> void registerMapping(T target, T source) {
if (source == null) return;
if (!sourceToTargetMapping.containsKey(source)) return;
// LogUtils.DEBUG_STRACK_LEVELS = 8;
// LogUtils.debug(this, "Added mapping\nsource: " + source + "\ntarget: " + target);
/*
* Check that we only register a mapping once
*/
final EObject existingMapping = sourceToTargetMapping.get(source);
Assert.isTrue(existingMapping == null, "Already have a mapping for " + source);
sourceToTargetMapping.put(source, target);
}
/**
* Asserts that all source objects are properly mapped to target objects.
*/
public void assertFullMappings() {
for (final Entry<EObject, EObject> e : sourceToTargetMapping.entrySet()) {
// Assert.isTrue(e.getValue() != null, "No mapping registered for source object " +
// e.getKey());
}
}
/**
* Returns the target (if known) for the specified source.
* <p>
* If no mapping is known, the original value is returned
*
* @param source the source object
* @return the corresponding target object or <code>source</code> if not known.
*/
private <T extends EObject> T getMapping(T source) {
if (sourceToTargetMapping.containsKey(source)) return (T) sourceToTargetMapping.get(source);
return source;
}
private <T extends EObject> void syncNonContainment(EStructuralFeature sf, T target, T source) {
if (sf.getEType() instanceof EDataType) {
if (sf.isMany()) {
syncNonContainmentList(sf, target, source);
} else {
syncNonContainmentValue(sf, target, source);
}
return;
}
/*
* We defer the sync until all containment objects have been handled...
*/
myDeferedSyncRecords.add(new DeferedSyncRecord<EObject>(sf, target, source));
}
public void handleDeferredOperations() {
for (final DeferedSyncRecord<EObject> d : myDeferedSyncRecords) {
d.sync();
}
}
private class DeferedSyncRecord<T extends EObject> {
private final EStructuralFeature mySf;
private final T myTarget;
private final T mySource;
public DeferedSyncRecord(EStructuralFeature sf, T target, T source) {
mySf = sf;
myTarget = target;
mySource = source;
}
public void sync() {
if (mySf.isMany()) {
syncNonContainmentList(mySf, myTarget, mySource);
} else {
syncNonContainmentValue(mySf, myTarget, mySource);
}
}
}
/**
* Returns the compound command that collects all the created commands.
*
* @return the command
*/
public CompoundCommand getCompoundCommand() {
return myCommitCommand;
}
/**
* A list of objects that have been removed in the synchronization from the target.
*/
private List<EObject> myRemovedObjects = null;
/**
* Synchronizes the information from the <code>source</code> object into the
* <code>target</code> object.
* <p>
* The class of <code>target</code> and <code>source</code> must be the same, e.g.
* <code>source.getClass() == target.getClass()</code>.
*
* @param <T> the type of the involved objects
* @param target the object synchronized into
* @param source the object synchronized from
*/
public <T extends EObject> void sync(T target, T source) {
Assert.isNotNull(source);
Assert.isNotNull(target);
registerMapping(target, source);
for (final EStructuralFeature sf : source.eClass().getEAllStructuralFeatures()) {
if (!sf.isChangeable() || sf.isDerived()) {
continue;
}
if (skipStructuralFeature(sf)) {
continue;
}
if (sf instanceof EAttribute) {
syncNonContainment(sf, target, source);
continue;
}
final EReference ref = (EReference) sf;
if (ref.isContainer()) {
continue;
}
if (!ref.isContainment()) {
syncNonContainment(ref, target, source);
continue;
}
if (ref.isMany()) {
syncContainmentList(ref, target, (EList<T>) source.eGet(ref));
} else {
syncContainmentValue(ref, target, source);
}
}
}
private List<EStructuralFeature> myIgnoredFeatures = null;
/**
* Adds a new feature that should be ignored in
* {@link #skipStructuralFeature(EStructuralFeature)}.
*
* @param ignoredFeature the new feature
*/
public void addIgnoredFeature(EStructuralFeature ignoredFeature) {
if (myIgnoredFeatures == null) {
myIgnoredFeatures = new ArrayList<EStructuralFeature>();
}
myIgnoredFeatures.add(ignoredFeature);
}
/**
* Returns whether a specific structural feature should be skipped in the sync process.
*
* @param sf the structural feature to test
*
* @return <code>true</code> if the feature should be skipped
*/
protected boolean skipStructuralFeature(EStructuralFeature sf) {
if (myIgnoredFeatures == null) return false;
return myIgnoredFeatures.contains(sf);
}
/**
* Commits the commands in the compound command if not empty.
*/
public void commit() {
if (myCommitCommand == null) return;
myCommitCommand.execute();
}
/**
* Synchronizes the specified non-containment structural feature from the
* <code>source</code> object into the <code>target</code> object.
*
* @param <T> the type of the involved objects
* @param sf the structural feature to synchronize
* @param target the object synchronized into
* @param source the object synchronized from
*/
protected <T extends EObject> void syncNonContainmentValue(EStructuralFeature sf, T target, T source) {
final Object targetValue = target.eGet(sf);
Object sourceValue = source.eGet(sf);
if (sourceValue instanceof EObject) {
sourceValue = getMapping((EObject) sourceValue);
}
if (BasicUtils.equals(targetValue, sourceValue)) return;
/*
* Simple attribute
*/
addCommand(SetCommand.create(getEditingDomain(), target, sf, sourceValue));
}
/**
* Synchronizes the specified non-containment structural feature from the
* <code>source</code> object into the <code>target</code> object.
*
* @param <T> the type of the involved objects
* @param sf the structural feature to synchronize
* @param target the object synchronized into
* @param source the object synchronized from
*/
protected <T extends EObject> void syncNonContainmentList(EStructuralFeature sf, T target, T source) {
final EList<Object> targetList = new BasicEList<Object>((List<?>) target.eGet(sf));
final EList<?> sourceList = (EList<?>) source.eGet(sf);
for (int index = 0; index < sourceList.size(); index++) {
Object sourceObject = sourceList.get(index);
if (sourceObject instanceof EObject) {
sourceObject = getMapping((EObject) sourceObject);
}
/*
* Is targetList long enough
*/
if (index >= targetList.size()) {
addCommand(AddCommand.create(getEditingDomain(), target, sf, sourceObject));
targetList.add(sourceObject);
continue;
}
boolean done;
do {
done = true;
/*
* Does the right element already exist
*/
final Object targetObject = targetList.get(index);
if (BasicUtils.equals(targetObject, sourceObject)) {
continue;
}
/*
* See if the source already exists somewhere in the target list.
*
* Also see if the target object at the wanted index is found anywhere in the
* source list.
*/
final int existingTargetIndex = ECollections.indexOf(targetList, sourceObject, index);
int targetIndex = ECollections.indexOf(sourceList, targetObject, index);
if (existingTargetIndex == -1) {
/*
* The object does not exist in the target list already, so we have to add
* it or replace the exiting object if it is not needed
*/
if (targetIndex == -1) {
addCommand(SetCommand.create(getEditingDomain(), target, sf, sourceObject, index));
targetList.set(index, sourceObject);
} else {
addCommand(AddCommand.create(getEditingDomain(), target, sf, sourceObject, index));
targetList.add(index, sourceObject);
}
continue;
}
final Object existingTargetObject = targetList.get(existingTargetIndex);
/*
* There are no use for the current target element - just remove it
*/
if (targetIndex == -1) {
addCommand(RemoveCommand.create(getEditingDomain(), target, sf, targetObject));
targetList.remove(index);
done = false;
continue;
}
/*
* There are a later use for the target element...
*/
if (targetIndex > existingTargetIndex) {
if (targetList.size() <= targetIndex) {
targetIndex = targetList.size() - 1;
}
addCommand(MoveCommand
.create(getEditingDomain(), target, sf, existingTargetObject, targetIndex));
targetList.move(targetIndex, index);
done = false;
continue;
}
addCommand(MoveCommand.create(getEditingDomain(), target, sf, existingTargetObject, index));
targetList.move(targetIndex, index);
} while (!done);
}
/*
* Remove any excess elements
*/
for (int i = targetList.size() - 1; i >= sourceList.size(); i--) {
final Object obj = targetList.get(i);
addCommand(RemoveCommand.create(getEditingDomain(), target, sf, obj));
}
}
/**
* Synchronizes the specified containment list from the <code>source</code> object into the
* <code>target</code> object.
*
* @param <T> the type of the involved objects
* @param ref the structural feature to synchronize
* @param target the object synchronized into
* @param sourceList the list synchronized from
*/
private <T extends EObject> void syncContainmentList(EReference ref, T target, List<T> sourceList) {
if (!target.eIsSet(ref) && (sourceList == null || sourceList.isEmpty())) return;
/*
* We create a new basic target list as a copy of the real list and make changes to this
* list - as it is not a "real" EMF list with notifications, and bi-directional
* reference handling, the operations are safe and will not change the original...
*/
final EList<EObject> targetList = new BasicEList<EObject>((EList<EObject>) target.eGet(ref));
int sourceListSize = 0;
if (sourceList != null) {
sourceListSize = sourceList.size();
}
final EAttribute key = findKeyAttribute(ref);
for (int sourceIndex = 0; sourceIndex < sourceListSize; sourceIndex++) {
final EObject sourceObject = sourceList.get(sourceIndex);
/*
* Is targetList long enough
*/
if (sourceIndex >= targetList.size()) {
addCommand(AddCommand.create(getEditingDomain(), target, ref, sourceObject));
targetList.add(sourceObject);
registerMapping(sourceObject, sourceObject);
continue;
}
boolean done;
do {
done = true;
/*
* Does the right element already exist
*/
final EObject targetObject = targetList.get(sourceIndex);
if (BasicUtils.equals(targetObject, sourceObject)) {
registerMapping(targetObject, sourceObject);
continue;
}
/*
* An object with the correct key already exist in the correct place (uses a
* deep equals). Sync these then.
*/
if (BasicUtils.equals(targetObject, sourceObject, key)) {
if (key == null) {
registerMapping(targetObject, sourceObject);
} else {
sync(targetObject, sourceObject);
}
continue;
}
/*
* See if the source object already exists somewhere in the target list.
*
* Also see if the target object at the wanted (source) index is found anywhere
* in the source list.
*/
final int existingTargetIndex = indexOf(targetList, key, sourceObject, sourceIndex);
int targetIndex = indexOf((List<EObject>) sourceList, key, targetObject, sourceIndex);
if (existingTargetIndex == -1) {
/*
* The source object does not exist in the target list yet, so we have to
* either add it or replace the exiting target object if this is not needed
*/
registerMapping(sourceObject, sourceObject);
if (targetIndex == -1) {
addCommand(SetCommand.create(getEditingDomain(), target, ref, sourceObject, sourceIndex));
addRemovedObject(targetObject);
targetList.set(sourceIndex, sourceObject);
} else {
addCommand(AddCommand.create(getEditingDomain(), target, ref, sourceObject, sourceIndex));
targetList.add(sourceIndex, sourceObject);
}
continue;
}
if (targetIndex == -1) {
/*
* There are no use for the current target object - just remove it
*/
addCommand(RemoveCommand.create(getEditingDomain(), target, ref, targetObject));
addRemovedObject(sourceObject);
targetList.remove(sourceIndex);
done = false;
continue;
}
final EObject existingTargetObject = targetList.get(existingTargetIndex);
if (targetIndex > existingTargetIndex) {
/*
* There are a later use for the target element...
*
* So we just move it to the wanted index (though not outside the target
* list).
*/
if (targetList.size() <= targetIndex) {
targetIndex = targetList.size() - 1;
}
addCommand(MoveCommand.create(getEditingDomain(), target, ref, existingTargetObject,
targetIndex));
targetList.move(targetIndex, sourceIndex);
done = false;
continue;
}
/*
* So we found an equivalent for the source object in the target list.
*
* Move this to the correct position and sync
*/
addCommand(MoveCommand.create(getEditingDomain(), target, ref, existingTargetObject, sourceIndex));
targetList.move(targetIndex, sourceIndex);
// registerMapping(sourceObject, sourceObject);
sync(targetList.get(sourceIndex), sourceObject);
} while (!done);
}
/*
* Remove any excess elements in the target list
*/
for (int i = targetList.size() - 1; i >= sourceListSize; i--) {
final EObject obj = targetList.get(i);
addCommand(RemoveCommand.create(getEditingDomain(), target, ref, obj));
addRemovedObject(obj);
}
}
// TODO
private <T extends EObject> void syncContainmentValue(EReference ref, T target, T source) {
final EAttribute key = findKeyAttribute(ref);
final EObject targetValue = (EObject) target.eGet(ref);
final EObject sourceValue = (EObject) source.eGet(ref);
registerMapping(targetValue, sourceValue);
if (targetValue == sourceValue) return;
/*
* Simple reference.
*
* If either the source or the target is null then we just assign the new value,
* otherwise we synchronize
*/
if (targetValue == null || sourceValue == null) {
addCommand(SetCommand.create(getEditingDomain(), target, ref, sourceValue));
if (sourceValue == null) {
addRemovedObject(targetValue);
}
return;
}
if (BasicUtils.equals(targetValue, sourceValue, key)) {
if (key != null) {
sync(targetValue, sourceValue);
}
return;
}
addCommand(SetCommand.create(getEditingDomain(), target, ref, sourceValue));
}
private EAttribute findKeyAttribute(EReference ref) {
final EList<EAttribute> keys = ref.getEKeys();
if (keys.size() > 0) return keys.get(0);
return ref.getEReferenceType().getEIDAttribute();
}
/**
* Returns the index of the specified object in the list using the key starting from the
* specified index
*
* @param list the list to search
* @param key the key for elements(can be <code>null</code>)
* @param lookup the element to lookup
* @param index the starting index
* @return the index of the element or <code>-1</code> if noy found
*/
private int indexOf(List<EObject> list, EAttribute key, EObject lookup, int index) {
for (int i = index; i < list.size(); i++) {
if (BasicUtils.equals(lookup, list.get(i), key)) return i;
}
return -1;
}
/**
* Adds the specified command to the contained compound command of this controller.
*
* @param c the new command to add
*/
public void addCommand(Command c) {
if (myCommitCommand == null) {
myCommitCommand = new CompoundCommand();
}
// LogUtils.debug(this, EcoreExtendedUtils.toString(c));
myCommitCommand.append(c);
}
/**
* Adds an object that is scheduled for removal by this controller if {@link #commit()
* committed}.
*
* @param obj the object that will be removed
*/
public void addRemovedObject(EObject obj) {
if (myRemovedObjects == null) {
myRemovedObjects = new ArrayList<EObject>();
}
myRemovedObjects.add(obj);
}
/**
* Returns a list with the {@link EObject objects} that will be removed from the target
* object if this controller is committed.
*
* @return the list of objects - can be <code>null</code>
*/
public List<EObject> getRemovedObjects() {
return myRemovedObjects;
}
/**
* Synchronizes the information from the <code>source</code> object into the
* <code>target</code> object.
* <p>
* The class of <code>target</code> and <code>source</code> must be the same, e.g.
* <code>source.getClass() == target.getClass()</code>.
*
* @param <T> the type of the involved objects
* @param target the object synchronized into
* @param source the object synchronized from
*/
public <T extends EObject> void sync(EList<T> target, List<T> source) {
if (target == source) return;
Assert.isTrue(target instanceof EObjectEList);
final EObjectEList<T> tEList = (EObjectEList<T>) target;
syncContainmentList((EReference) tEList.getEStructuralFeature(), (T) tEList.getEObject(), source);
}
/**
* Sets the editing domain for this controller.
*
* @param editingDomain the editing domain to use for this controller
*/
public void setEditingDomain(EditingDomain editingDomain) {
myEditingDomain = editingDomain;
}
/**
* Returns the editing domain for this controller.
*
* @return the editing domain
*/
public EditingDomain getEditingDomain() {
if (myEditingDomain == null) {
myEditingDomain = new AdapterFactoryEditingDomain(new ReflectiveItemProviderAdapterFactory(),
new BasicCommandStack());
}
return myEditingDomain;
}
}
/**
* Returns a list of all <em>known</em> sub-classes for the specified class.
*
* @param cls the super-class
* @return list of all sub-classes - possibly <code>null</code>
*/
public static Collection<EClass> getSubClasses(EClass cls) {
if (SUB_CLASSES.containsKey(cls)) return SUB_CLASSES.get(cls);
Collection<EClass> l = null;
final Registry registry = EPackage.Registry.INSTANCE;
for (final Object v : registry.values()) {
if (!(v instanceof EPackage)) {
continue;
}
final EPackage ep = (EPackage) v;
for (final EClassifier c : ep.getEClassifiers()) {
if (!(c instanceof EClass)) {
continue;
}
final EClass cl = (EClass) c;
if (cl.getESuperTypes().contains(cls)) {
if (l == null) {
l = new ArrayList<EClass>();
}
l.add(cl);
}
}
}
SUB_CLASSES.put(cls, l);
return l;
}
static final Map<EClass, Collection<EClass>> SUB_CLASSES = new HashMap<EClass, Collection<EClass>>();
}