| /** |
| ******************************************************************************** |
| * Copyright (c) 2021 Robert Bosch GmbH and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * See4sys - initial API and implementation (Sphinx) |
| * itemis AG - adaptation to Amalthea |
| * Robert Bosch GmbH - implementation without Sphinx |
| ******************************************************************************** |
| */ |
| |
| package org.eclipse.app4mc.amalthea.model.provider; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| |
| import org.eclipse.app4mc.amalthea.model.AmaltheaFactory; |
| import org.eclipse.app4mc.amalthea.model.AmaltheaPackage; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.emf.common.command.Command; |
| import org.eclipse.emf.common.command.CommandWrapper; |
| import org.eclipse.emf.common.command.CompoundCommand; |
| import org.eclipse.emf.common.command.UnexecutableCommand; |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.AdapterFactory; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.common.util.ResourceLocator; |
| import org.eclipse.emf.ecore.EAttribute; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| 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.edit.command.AddCommand; |
| import org.eclipse.emf.edit.command.CommandParameter; |
| import org.eclipse.emf.edit.command.CreateChildCommand; |
| import org.eclipse.emf.edit.command.MoveCommand; |
| import org.eclipse.emf.edit.command.SetCommand; |
| import org.eclipse.emf.edit.domain.EditingDomain; |
| import org.eclipse.emf.edit.provider.AttributeValueWrapperItemProvider; |
| import org.eclipse.emf.edit.provider.DelegatingWrapperItemProvider; |
| import org.eclipse.emf.edit.provider.Disposable; |
| import org.eclipse.emf.edit.provider.FeatureMapEntryWrapperItemProvider; |
| import org.eclipse.emf.edit.provider.IEditingDomainItemProvider; |
| import org.eclipse.emf.edit.provider.IItemLabelProvider; |
| import org.eclipse.emf.edit.provider.IItemPropertySource; |
| import org.eclipse.emf.edit.provider.IStructuredItemContentProvider; |
| import org.eclipse.emf.edit.provider.ITreeItemContentProvider; |
| import org.eclipse.emf.edit.provider.ItemProviderAdapter; |
| |
| /** |
| * A base class for transient item provider {@link Adapter adapter}s that can be |
| * used to insert non-model view objects between an object and its children. |
| * <p> |
| * Transient item providers must be instantiated statefully wrt their target |
| * object, i.e., the same transient item provider instance must not be used for |
| * multiple target objects. |
| * </p> |
| */ |
| public abstract class TransientItemProvider extends ItemProviderAdapter implements IEditingDomainItemProvider, |
| IStructuredItemContentProvider, ITreeItemContentProvider, IItemLabelProvider, IItemPropertySource { |
| |
| public static class AdapterFactoryHelper { |
| |
| // Suppress default constructor |
| private AdapterFactoryHelper() { |
| throw new IllegalStateException("Utility class"); |
| } |
| |
| public static Object adapt(Object target, Object type, AdapterFactory adapterFactory) { |
| if (type instanceof Class<?> && TransientItemProvider.class.isAssignableFrom((Class<?>) type)) { |
| Adapter adapter = EcoreUtil.getExistingAdapter((Notifier) target, type); |
| if (adapter != null) { |
| return adapter; |
| } |
| return adapterFactory.adaptNew((Notifier) target, type); |
| } |
| return null; |
| } |
| } |
| |
| protected TransientItemProvider(AdapterFactory adapterFactory) { |
| super(adapterFactory); |
| } |
| |
| /* |
| * Overridden to ensure statefulness of transient item provider adapters wrt |
| * their target object, i.e., to avoid that the same transient item provider |
| * instance is used for multiple target objects. |
| * |
| * @see |
| * org.eclipse.emf.edit.provider.ItemProviderAdapter#setTarget(org.eclipse.emf. |
| * common.notify.Notifier) |
| */ |
| @Override |
| public void setTarget(Notifier target) { |
| Assert.isLegal(this.target == null || this.target == target); |
| super.setTarget(target); |
| } |
| |
| /* |
| * Overridden to ensure that an already existing instance of some transient item |
| * provider on a given target object to be retrieved by using the class of the |
| * transient item provider in question as type to adapt to (rather than the item |
| * provider adapter factory as intended by ItemProviderAdapter). In combination |
| * with TransientItemProvider.AdapterFactoryHelper#adapt(Object, Object, |
| * AdapterFactory), this behavior enables the implementation of item provider |
| * adapter factories that support the creation and retrieval transient item |
| * provider instances by calling AdapterFactory#adapt(object, |
| * TransientItemProviderSubClass.class) |
| * |
| * @see |
| * org.eclipse.emf.edit.provider.ItemProviderAdapter#isAdapterForType(java.lang. |
| * Object) |
| * |
| * @see org.eclipse.emf.ecore.util.EcoreUtil#getExistingAdapter(Notifier, |
| * Object) |
| * |
| * @see |
| * org.eclipse.sphinx.emf.edit.TransientItemProvider.AdapterFactoryHelper.adapt( |
| * Object, Object, AdapterFactory) |
| */ |
| @Override |
| public boolean isAdapterForType(Object type) { |
| return type == this.getClass(); |
| } |
| |
| /** |
| * Handles model change notifications by calling {@link #updateChildren} to |
| * update any cached children and by creating a viewer notification, which it |
| * passes to {@link #fireNotifyChanged} to trigger a full or partial refresh of |
| * the underlying viewer. |
| */ |
| @Override |
| public void notifyChanged(Notification notification) { |
| updateChildren(notification); |
| super.notifyChanged(notification); |
| } |
| |
| /** |
| * This sets the parent of the transient item provider to <code>target</code> |
| * value. The target instance variable comes from the adapter base class |
| * {@link org.eclipse.emf.common.notify.impl.AdapterImpl}. |
| */ |
| @Override |
| public Object getParent(Object object) { |
| return target; |
| } |
| |
| /** |
| * Returns a folder kind of icon as default image for non-model view objects |
| * between an object and its children. |
| */ |
| @Override |
| public Object getImage(Object object) { |
| if (object != null) { |
| return overlayImage(object, ExtendedAmaltheaEditPlugin.INSTANCE.getPluginResourceLocator() |
| .getImage("full/obj16/folder_closed")); |
| } |
| return null; |
| } |
| |
| /* |
| * Overridden to use the target object behind this transient item provider |
| * rather than given object (which would be the transient item provider itself) |
| * in order to (1) retrieve the store for the children of the given object (and |
| * thereby retrieve it in the same way as in #updateChildren(Notification)) and |
| * (2) obtain the values of the children features. |
| * |
| * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getChildren(java.lang. |
| * Object) |
| */ |
| @Override |
| public Collection<?> getChildren(Object object) { |
| ChildrenStore store = getChildrenStore(target); |
| if (store != null) { |
| return store.getChildren(); |
| } |
| |
| store = createChildrenStore(object); |
| List<Object> result = store != null ? null : new ArrayList<>(); |
| EObject eObject = (EObject) target; |
| |
| for (EStructuralFeature feature : getChildrenFeatures(object)) { |
| if (feature.isMany()) { |
| List<?> children = (List<?>) eObject.eGet(feature); |
| int index = 0; |
| for (Object unwrappedChild : children) { |
| Object child = wrap(object, feature, unwrappedChild, index); |
| if (store != null) { |
| store.getList(feature).add(child); |
| } else { |
| result.add(child); |
| } |
| index++; |
| } |
| } else { |
| Object child = eObject.eGet(feature); |
| if (child != null) { |
| child = wrap(object, feature, child, CommandParameter.NO_INDEX); |
| if (store != null) { |
| store.setValue(feature, child); |
| } else { |
| result.add(child); |
| } |
| } |
| } |
| } |
| return store != null ? store.getChildren() : result; |
| } |
| |
| /* |
| * Overridden to use the target object behind this transient item provider |
| * rather than given object (which would be the transient item provider itself) |
| * as key for the store for the children of the given object (and thereby match |
| * the way it is retrieved in #updateChildren(Notification)). |
| * |
| * @see |
| * org.eclipse.emf.edit.provider.ItemProviderAdapter#createChildrenStore(java. |
| * lang.Object) |
| */ |
| @Override |
| protected ChildrenStore createChildrenStore(Object object) { |
| ChildrenStore store = null; |
| |
| if (isWrappingNeeded(object)) { |
| if (childrenStoreMap == null) { |
| childrenStoreMap = new HashMap<Object, ChildrenStore>(); |
| } |
| store = new ChildrenStore(getChildrenFeatures(object)); |
| childrenStoreMap.put(target, store); |
| } |
| return store; |
| } |
| |
| @Override |
| protected Collection<? extends EStructuralFeature> getChildrenFeatures(final Object object) { |
| if (this.childrenFeatures == null) { |
| super.getChildrenFeatures(object); |
| this.childrenFeatures.addAll(myFeatures()); |
| } |
| return this.childrenFeatures; |
| } |
| |
| /** |
| * This implements delegated command creation for the given transient item |
| * provider and sets its owner to <code>target</code>. |
| */ |
| @Override |
| public Command createCommand(Object object, EditingDomain domain, Class<? extends Command> commandClass, |
| CommandParameter commandParameter) { |
| if (commandClass == CreateChildCommand.class || commandClass == MoveCommand.class) { |
| commandParameter.setOwner(target); |
| } |
| return super.createCommand(object, domain, commandClass, commandParameter); |
| } |
| |
| /** |
| * This implements {@link IEditingDomainItemProvider#getNewChildDescriptors |
| * IEditingDomainItemProvider.getNewChildDescriptors}, returning descriptors for |
| * all the possible children that can be added to the specified |
| * <code>target</code>. The target instance variable comes from the adapter base |
| * class {@link org.eclipse.emf.common.notify.impl.AdapterImpl}. |
| */ |
| @Override |
| public Collection<?> getNewChildDescriptors(Object object, EditingDomain editingDomain, Object sibling) { |
| return super.getNewChildDescriptors(target, editingDomain, sibling); |
| } |
| |
| /** |
| * Creates and returns a wrapper for the given value, at the given index in the |
| * given feature of the given object if such a wrapper is needed; otherwise, |
| * returns the original value. |
| * <p> |
| * This method is very similar to |
| * {@link #createWrapper(EObject, EStructuralFeature, Object, int)} but accepts |
| * and handles {@link Object} rather than just {@link EObject} |
| * <code>object</code> arguments. |
| * </p> |
| * |
| * @see #createWrapper(EObject, EStructuralFeature, Object, int) |
| */ |
| protected Object createWrapper(Object object, EStructuralFeature feature, Object value, int index) { |
| if (!isWrappingNeeded(object)) { |
| return value; |
| } |
| if (object instanceof EObject) { |
| if (FeatureMapUtil.isFeatureMap(feature)) { |
| value = new FeatureMapEntryWrapperItemProvider((FeatureMap.Entry) value, (EObject) object, |
| (EAttribute) feature, index, adapterFactory, getResourceLocator()); |
| } else if (feature instanceof EAttribute) { |
| value = new AttributeValueWrapperItemProvider(value, (EObject) object, (EAttribute) feature, index, |
| adapterFactory, getResourceLocator()); |
| } |
| } else { |
| if (!((EReference) feature).isContainment()) { |
| value = new DelegatingWrapperItemProvider(value, object, feature, index, adapterFactory); |
| } |
| } |
| return value; |
| } |
| |
| /* |
| * Overridden to enable wrapping of cross-referenced model objects by default. |
| * This is required to make sure that the latter get represented by instances of |
| * org.eclipse.emf.edit.provider.DelegatingWrapperItemProvider and may have a |
| * parent element that is different from object containing them. |
| * |
| * @see |
| * org.eclipse.emf.edit.provider.ItemProviderAdapter#isWrappingNeeded(java.lang. |
| * Object) |
| */ |
| @Override |
| protected boolean isWrappingNeeded(Object object) { |
| if (wrappingNeeded == null) { |
| wrappingNeeded = Boolean.FALSE; |
| |
| for (EStructuralFeature feature : getChildrenFeatures(object)) { |
| if (feature instanceof EAttribute |
| || feature instanceof EReference && !((EReference) feature).isContainment()) { |
| wrappingNeeded = Boolean.TRUE; |
| break; |
| } |
| } |
| } |
| return wrappingNeeded; |
| } |
| |
| /** |
| * Wraps a value, if needed, and keeps the wrapper for disposal along with the |
| * item provider. This method actually calls {@link #createWrapper |
| * createWrapper} to determine if the given value, at the given index in the |
| * given feature of the given object, should be wrapped and to obtain the |
| * wrapper. If a wrapper is obtained, it is recorded and returned. Otherwise, |
| * the original value is returned. Subclasses may override {@link #createWrapper |
| * createWrapper} to specify when and with what to wrap values. |
| * <p> |
| * This method is very similar to |
| * {@link #wrap(EObject, EStructuralFeature, Object, int)} but accepts and |
| * handles {@link Object} rather than just {@link EObject} <code>object</code> |
| * arguments. |
| * </p> |
| */ |
| protected Object wrap(Object object, EStructuralFeature feature, Object value, int index) { |
| if (!feature.isMany() && index != CommandParameter.NO_INDEX) { |
| throw new IllegalArgumentException("Bad wrap index."); |
| } |
| |
| Object wrapper = createWrapper(object, feature, value, index); |
| if (wrapper == null) { |
| wrapper = value; |
| } else if (wrapper != value) { |
| if (wrappers == null) { |
| wrappers = new Disposable(); |
| } |
| wrappers.add(wrapper); |
| } |
| return wrapper; |
| } |
| |
| /** |
| * This allows creating a command and overriding the |
| * {@link org.eclipse.emf.common.command.CommandWrapper.getAffectedObjects()#getAffectedObjects()} |
| * method to return the appropriate transient item provider whenever the real |
| * affected object is the owner. |
| * |
| * @param command an command. |
| * @param owner the owner of the object. |
| * @return a {@link CommandWrapper} |
| */ |
| protected Command createWrappedCommand(Command command, final EObject owner) { |
| return new CommandWrapper(command) { |
| @Override |
| public Collection<?> getAffectedObjects() { |
| Collection<?> affected = super.getAffectedObjects(); |
| if (affected.contains(owner)) { |
| affected = Collections.singleton(TransientItemProvider.this); |
| } |
| return affected; |
| } |
| }; |
| } |
| |
| /** |
| * This creates a primitive {@link org.eclipse.emf.edit.command.AddCommand}. |
| */ |
| @Override |
| protected Command createAddCommand(EditingDomain domain, EObject owner, EStructuralFeature feature, |
| Collection<?> collection, int index) { |
| return createWrappedCommand(super.createAddCommand(domain, owner, feature, collection, index), owner); |
| } |
| |
| /** |
| * This creates a primitive {@link org.eclipse.emf.edit.command.RemoveCommand}. |
| */ |
| @Override |
| protected Command createRemoveCommand(EditingDomain domain, EObject owner, EStructuralFeature feature, |
| Collection<?> collection) { |
| return createWrappedCommand(super.createRemoveCommand(domain, owner, feature, collection), owner); |
| } |
| |
| /* |
| * Overridden to support the case where specified owner via CommandParameter |
| * (commandParameter.getEOwner()) is no EObject. In this case, try to refer to |
| * parent or owner of specified owner. |
| * |
| * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#factorAddCommand(org. |
| * eclipse.emf.edit.domain.EditingDomain, |
| * org.eclipse.emf.edit.command.CommandParameter) |
| */ |
| @Override |
| protected Command factorAddCommand(EditingDomain domain, CommandParameter commandParameter) { |
| if (commandParameter.getCollection() == null || commandParameter.getCollection().isEmpty()) { |
| return UnexecutableCommand.INSTANCE; |
| } |
| |
| EObject eOwner = commandParameter.getEOwner(); |
| |
| // Try to refer to parent or owner in case that owner specified via |
| // CommandParameter is no EObject |
| if (eOwner == null) { |
| Object parentOfOwner = domain.getParent(commandParameter.getOwner()); |
| if (parentOfOwner instanceof EObject) { |
| eOwner = (EObject) parentOfOwner; |
| } |
| } |
| |
| final EObject eObject = eOwner; |
| final List<Object> list = new ArrayList<>(commandParameter.getCollection()); |
| int index = commandParameter.getIndex(); |
| |
| CompoundCommand addCommand = new CompoundCommand(CompoundCommand.MERGE_COMMAND_ALL); |
| |
| while (!list.isEmpty()) { |
| Iterator<Object> children = list.listIterator(); |
| final Object firstChild = children.next(); |
| EStructuralFeature childFeature = getChildFeature(eObject, firstChild); |
| |
| if (childFeature == null) { |
| break; |
| } |
| // If it is a list type value... |
| // |
| else if (childFeature.isMany()) { |
| // Correct the index, if necessary. |
| // |
| if (index != CommandParameter.NO_INDEX) { |
| for (EStructuralFeature feature : getChildrenFeatures(commandParameter.getOwner())) { |
| if (feature == childFeature) { |
| break; |
| } |
| |
| if (feature.isMany()) { |
| index -= ((List<?>) eObject.eGet(feature)).size(); |
| } else if (eObject.eGet(feature) != null) { |
| index -= 1; |
| } |
| } |
| if (index < 0) { |
| break; |
| } |
| } |
| |
| // These will be the children belonging to this feature. |
| // |
| Collection<Object> childrenOfThisFeature = new ArrayList<>(); |
| childrenOfThisFeature.add(firstChild); |
| children.remove(); |
| |
| // Consume the rest of the appropriate children. |
| // |
| while (children.hasNext()) { |
| Object child = children.next(); |
| |
| // Is this child in this feature... |
| // |
| if (getChildFeature(eObject, child) == childFeature) { |
| // Add it to the list and remove it from the other list. |
| // |
| childrenOfThisFeature.add(child); |
| children.remove(); |
| } |
| } |
| |
| // Create a command for this feature, |
| // |
| addCommand.append(createAddCommand(domain, eObject, childFeature, childrenOfThisFeature, index)); |
| |
| if (index >= childrenOfThisFeature.size()) { |
| index -= childrenOfThisFeature.size(); |
| } else { |
| index = CommandParameter.NO_INDEX; |
| } |
| } else if (eObject.eGet(childFeature) == null) { |
| Command setCommand = createSetCommand(domain, eObject, childFeature, firstChild); |
| addCommand.append(new CommandWrapper(setCommand) { |
| protected Collection<?> affected; |
| |
| @Override |
| public void execute() { |
| super.execute(); |
| affected = Collections.singleton(firstChild); |
| } |
| |
| @Override |
| public void undo() { |
| super.undo(); |
| affected = Collections.singleton(eObject); |
| } |
| |
| @Override |
| public void redo() { |
| super.redo(); |
| affected = Collections.singleton(firstChild); |
| } |
| |
| @Override |
| public Collection<?> getResult() { |
| return Collections.singleton(firstChild); |
| } |
| |
| @Override |
| public Collection<?> getAffectedObjects() { |
| return affected; |
| } |
| }); |
| children.remove(); |
| } else { |
| break; |
| } |
| } |
| |
| // If all the objects aren't used up by the above, then we can't do the command. |
| // |
| if (list.isEmpty()) { |
| return addCommand.unwrap(); |
| } else { |
| addCommand.dispose(); |
| return UnexecutableCommand.INSTANCE; |
| } |
| } |
| |
| /* |
| * Overridden to add support for the case where: (1) collection objects of given |
| * command parameter have different containers, or the same container but |
| * different features and (2) specified owner via CommandParameter |
| * (commandParameter.getEOwner()) is no EObject. In this case, try to refer to |
| * parent or owner of specified owner. |
| * |
| * @see |
| * org.eclipse.emf.edit.provider.ItemProviderAdapter#factorRemoveCommand(org. |
| * eclipse.emf.edit.domain.EditingDomain, |
| * org.eclipse.emf.edit.command.CommandParameter) |
| */ |
| @Override |
| protected Command factorRemoveCommand(EditingDomain domain, CommandParameter commandParameter) { |
| |
| if (commandParameter.getCollection() == null || commandParameter.getCollection().isEmpty()) { |
| return UnexecutableCommand.INSTANCE; |
| } |
| |
| EObject eOwner = commandParameter.getEOwner(); |
| |
| // Try to refer to parent or owner in case that owner specified via |
| // CommandParameter is no EObject |
| if (eOwner == null) { |
| Object parentOfOwner = domain.getParent(commandParameter.getOwner()); |
| if (parentOfOwner instanceof EObject) { |
| eOwner = (EObject) parentOfOwner; |
| } |
| } |
| |
| final EObject eObject = eOwner; |
| final List<Object> list = new ArrayList<>(commandParameter.getCollection()); |
| |
| CompoundCommand removeCommand = new CompoundCommand(CompoundCommand.MERGE_COMMAND_ALL); |
| |
| // Iterator over all the child references to factor each child to the right |
| // reference. |
| // |
| for (EStructuralFeature feature : getChildrenFeatures(commandParameter.getOwner())) { |
| // If it is a list type value... |
| // |
| if (feature.isMany()) { |
| List<?> value = (List<?>) getFeatureValue(eObject, feature); |
| |
| // These will be the children belonging to this feature. |
| // |
| Collection<Object> childrenOfThisFeature = new ArrayList<>(); |
| for (ListIterator<Object> objects = list.listIterator(); objects.hasNext();) { |
| Object o = objects.next(); |
| |
| // Is this object in this feature... |
| // |
| if (value.contains(o)) { |
| // Add it to the list and remove it from the other list. |
| // |
| childrenOfThisFeature.add(o); |
| objects.remove(); |
| } |
| } |
| |
| // If we have children to remove for this feature, create a command for it. |
| // |
| if (!childrenOfThisFeature.isEmpty()) { |
| removeCommand.append(createRemoveCommand(domain, eObject, feature, childrenOfThisFeature)); |
| } |
| } else { |
| // It's just a single value |
| // |
| final Object value = getFeatureValue(eObject, feature); |
| for (ListIterator<Object> objects = list.listIterator(); objects.hasNext();) { |
| Object o = objects.next(); |
| |
| // Is this object in this feature... |
| // |
| if (o == value) { |
| // Create a command to unset this and remove the object from the other list. |
| // |
| Command setCommand = createSetCommand(domain, eObject, feature, SetCommand.UNSET_VALUE); |
| removeCommand.append(new CommandWrapper(setCommand) { |
| protected Collection<?> affected; |
| |
| @Override |
| public void execute() { |
| super.execute(); |
| affected = Collections.singleton(eObject); |
| } |
| |
| @Override |
| public void undo() { |
| super.undo(); |
| affected = Collections.singleton(value); |
| } |
| |
| @Override |
| public void redo() { |
| super.redo(); |
| affected = Collections.singleton(eObject); |
| } |
| |
| @Override |
| public Collection<?> getResult() { |
| return Collections.singleton(value); |
| } |
| |
| @Override |
| public Collection<?> getAffectedObjects() { |
| return affected; |
| } |
| }); |
| objects.remove(); |
| break; |
| } |
| } |
| } |
| } |
| |
| // If all the objects are used up by the above, then we can't do the command. |
| // |
| if (list.isEmpty()) { |
| return removeCommand.unwrap(); |
| } else { |
| // FIXME File bug to EMF: In the case where objects in the list have different |
| // container, or the same |
| // container but different features we must to iterate over all container |
| // features |
| for (Object object : new ArrayList<Object>(list)) { |
| if (object instanceof EObject) { |
| final EObject fallBackOwner = ((EObject) object).eContainer(); |
| EStructuralFeature containingFeature = ((EObject) object).eContainingFeature(); |
| if (containingFeature != null) { |
| if (containingFeature.isMany()) { |
| List<?> value = (List<?>) getFeatureValue(fallBackOwner, containingFeature); |
| |
| // These will be the children belonging to this feature. |
| // |
| Collection<Object> childrenOfThisFeature = new ArrayList<>(); |
| for (ListIterator<Object> objects = list.listIterator(); objects.hasNext();) { |
| Object o = objects.next(); |
| |
| // Is this object in this feature... |
| // |
| if (value.contains(o)) { |
| // Add it to the list and remove it from the other list. |
| // |
| childrenOfThisFeature.add(o); |
| objects.remove(); |
| } |
| } |
| |
| // If we have children to remove for this feature, create a command for it. |
| // |
| if (!childrenOfThisFeature.isEmpty()) { |
| removeCommand.append(createRemoveCommand(domain, fallBackOwner, containingFeature, |
| childrenOfThisFeature)); |
| } |
| |
| } else { |
| |
| // It's just a single value |
| // |
| final Object value = getFeatureValue(fallBackOwner, containingFeature); |
| for (ListIterator<Object> objects = list.listIterator(); objects.hasNext();) { |
| Object o = objects.next(); |
| |
| // Is this object in this feature... |
| // |
| if (o == value) { |
| // Create a command to set this to null and remove the object from the other |
| // list. |
| // |
| Command setCommand = domain.createCommand(SetCommand.class, |
| new CommandParameter(fallBackOwner, containingFeature, null)); |
| removeCommand.append(new CommandWrapper(setCommand) { |
| protected Collection<?> affected; |
| |
| @Override |
| public void execute() { |
| affected = Collections.singleton( |
| fallBackOwner.eContainer() != null ? fallBackOwner.eContainer() |
| : fallBackOwner); |
| super.execute(); |
| } |
| |
| @Override |
| public void undo() { |
| super.undo(); |
| affected = Collections.singleton(value); |
| } |
| |
| @Override |
| public void redo() { |
| super.redo(); |
| affected = Collections.singleton( |
| fallBackOwner.eContainer() != null ? fallBackOwner.eContainer() |
| : fallBackOwner); |
| } |
| |
| @Override |
| public Collection<?> getResult() { |
| return Collections.singleton(value); |
| } |
| |
| @Override |
| public Collection<?> getAffectedObjects() { |
| return affected; |
| } |
| }); |
| objects.remove(); |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| if (list.isEmpty()) { |
| return removeCommand.unwrap(); |
| } else { |
| removeCommand.dispose(); |
| return UnexecutableCommand.INSTANCE; |
| } |
| } |
| |
| @Override |
| protected Command createDragAndDropCommand(final EditingDomain domain, final Object owner, final float location, |
| final int operations, final int operation, final Collection<?> collection) { |
| |
| EObject dropTarget = null; |
| if (owner == this && target instanceof EObject) { |
| // for this transient item provider -> use (my) target |
| dropTarget = (EObject) target; |
| } else if (owner instanceof EObject) { |
| // use EObject |
| dropTarget = (EObject) owner; |
| } |
| |
| for (EStructuralFeature feature : myFeatures()) { |
| if (dropTarget != null && new AddCommand(domain, dropTarget, feature, collection).canExecute()) { |
| return super.createDragAndDropCommand(domain, dropTarget, location, operations, operation, collection); |
| } |
| } |
| |
| return UnexecutableCommand.INSTANCE; |
| } |
| |
| protected AmaltheaPackage myPackage() { |
| return AmaltheaPackage.eINSTANCE; |
| } |
| |
| protected AmaltheaFactory myFactory() { |
| return AmaltheaFactory.eINSTANCE; |
| } |
| |
| @Override |
| protected ResourceLocator getResourceLocator() { |
| return AmaltheaEditPlugin.INSTANCE.getPluginResourceLocator(); |
| } |
| |
| // Default implementations. |
| // Subclasses should override either myFeature() or myFeatures(). |
| |
| public EStructuralFeature myFeature() { |
| return null; |
| } |
| |
| public List<EStructuralFeature> myFeatures() { |
| return Collections.singletonList(myFeature()); |
| } |
| |
| } |