/**
* <copyright>
*
* Copyright (c) 2002, 2009 IBM Corporation 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:
* IBM - Initial API and implementation
*
* </copyright>
*
* $Id: ItemProviderAdapter.java,v 1.40 2008/06/07 10:53:28 emerks Exp $
*/
package net.enilink.komma.edit.provider;
import java.net.URL;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.WeakHashMap;
import net.enilink.komma.common.adapter.IAdapter;
import net.enilink.komma.common.adapter.IAdapterFactory;
import net.enilink.komma.common.command.AbortExecutionException;
import net.enilink.komma.common.command.CommandResult;
import net.enilink.komma.common.command.CommandWrapper;
import net.enilink.komma.common.command.CompositeCommand;
import net.enilink.komma.common.command.ExtendedCompositeCommand;
import net.enilink.komma.common.command.ICommand;
import net.enilink.komma.common.command.IdentityCommand;
import net.enilink.komma.common.command.UnexecutableCommand;
import net.enilink.komma.common.notify.INotification;
import net.enilink.komma.common.notify.INotificationBroadcaster;
import net.enilink.komma.common.notify.INotificationListener;
import net.enilink.komma.common.notify.IPropertyNotification;
import net.enilink.komma.common.notify.NotificationFilter;
import net.enilink.komma.common.notify.NotificationSupport;
import net.enilink.komma.common.util.ExtensibleList;
import net.enilink.komma.common.util.ICollector;
import net.enilink.komma.common.util.IList;
import net.enilink.komma.common.util.IResourceLocator;
import net.enilink.komma.core.IEntity;
import net.enilink.komma.core.IReference;
import net.enilink.komma.core.URI;
import net.enilink.komma.core.URIs;
import net.enilink.komma.edit.KommaEditPlugin;
import net.enilink.komma.edit.command.AddCommand;
import net.enilink.komma.edit.command.CommandParameter;
import net.enilink.komma.edit.command.CopyCommand;
import net.enilink.komma.edit.command.CreateChildCommand;
import net.enilink.komma.edit.command.CreateCopyCommand;
import net.enilink.komma.edit.command.DragAndDropCommand;
import net.enilink.komma.edit.command.ICommandActionDelegate;
import net.enilink.komma.edit.command.IInputCallback;
import net.enilink.komma.edit.command.InitializeCopyCommand;
import net.enilink.komma.edit.command.MoveCommand;
import net.enilink.komma.edit.command.RemoveCommand;
import net.enilink.komma.edit.command.ReplaceCommand;
import net.enilink.komma.edit.command.SetCommand;
import net.enilink.komma.edit.domain.IEditingDomain;
import net.enilink.komma.em.concepts.IClass;
import net.enilink.komma.em.concepts.IProperty;
import net.enilink.komma.em.concepts.IResource;
import net.enilink.komma.model.IModel;
import net.enilink.komma.model.IModelAware;
import net.enilink.komma.model.IModelSet;
import net.enilink.komma.model.IObject;
import net.enilink.komma.model.ModelUtil;
import net.enilink.komma.model.event.IStatementNotification;
import net.enilink.vocab.owl.DatatypeProperty;
import net.enilink.vocab.owl.OWL;
import net.enilink.vocab.rdfs.RDFS;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
/**
* This adapter implementation provides a convenient reusable base for adapters
* that will be used as item providers. Default implementations for the
* following interfaces are provided: {@link IItemLabelProvider},
* {@link IItemColorProvider}, {@link IItemFontProvider},
* {@link IItemPropertySource}, {@link IStructuredItemContentProvider},
* {@link ITreeItemContentProvider}, and {@link IEditingDomainItemProvider}.
* Also, {@link IUpdateableItemText#getUpdateableText} is implemented to
* delegate to {@link #getText}; often the editable text will be just the text,
* in which case this is a good default implementation.
*/
public class ItemProviderAdapter extends
NotificationSupport<IViewerNotification> implements IDisposable,
CreateChildCommand.IHelper, IResourceLocator, IAdapter,
INotificationListener<INotification> {
public static class ChildDescriptor {
protected final boolean requiresName;
protected final Object value;
public ChildDescriptor(Object value) {
this(value, false);
}
public ChildDescriptor(Object value, boolean requiresName) {
this.value = value;
this.requiresName = requiresName;
}
public Object getValue() {
return value;
}
public boolean requiresName() {
return requiresName;
}
}
/**
* A <code>ChildrenStore</code> stores a number of objects that are to be
* presented as the children of an object. Each of a set of
* {@link org.eclipse.emf.ecore.EStructuralFeature feature}s may have either
* one or any number of children objects associated with it, depending on
* its multiplicity. The objects associated with a multiplicity-many feature
* are accessed and manipulated as an
* {@link net.enilink.komma.rmf.common.util.EList}, typically mirroring the
* actual values of that feature, with some or all in wrappers, as
* necessary. The object associated with a multiplicity-1 feature is
* typically accessed and set directly, although a modifiable, singleton
* list view is available. This class provides a number of convenient
* methods for access by feature, as well as a method, {@link #getChildren
* getChildren} that returns the complete collection of children from all
* features as an unmodifiable list.
*/
protected static class ChildrenStore {
protected Map<IProperty, IList<Object>> map;
protected IResource owner;
protected Collection<? extends IProperty> properties;
ChildrenStore(IResource owner,
Collection<? extends IProperty> properties) {
this.owner = owner;
this.properties = properties;
}
/**
* Clears the children of all features in the store. Multi-valued
* features are cleared, and single-valued features are set to null.
*/
public void clear() {
for (Map.Entry<IProperty, ? extends List<Object>> entry : map
.entrySet()) {
if (entry.getKey().isMany(owner)) {
entry.getValue().clear();
} else {
entry.getValue().set(0, null);
}
}
}
/**
* Returns a list, either a full list implementation or a fixed-sized
* modifiable singleton list, depending on the multiplicity of the
* feature. Before accessing an entry's list, the store should always
* check if it is null and if so, allocate it using this method.
*/
protected IList<Object> createList(IProperty property) {
return property.isMany(owner) ? new ExtensibleList<Object>()
: new ModifiableSingletonEList<Object>();
}
/**
* Returns the list view of the specified feature, or null if the store
* does not handle the feature.
*/
public List<Object> ensureList(IProperty property) {
if (map == null) {
map = new HashMap<IProperty, IList<Object>>();
}
IList<Object> list = map.get(property);
if (list == null) {
list = createList(property);
map.put(property, list);
}
return list;
}
/**
* Returns either a single element of the specified feature or, if index
* is -1, the value view.
*/
public Object get(IProperty property, int index) {
if (index == -1) {
return getValue(property);
}
List<Object> list = getList(property);
return list != null ? list.get(index) : null;
}
/**
* Returns a list containing all children of all features in the store.
* Null, single-valued features are excluded. The list can be freely
* modified without affecting the store.
*/
public List<Object> getChildren() {
List<Object> result = new ArrayList<Object>();
for (IProperty property : properties) {
List<Object> children = getList(property);
if (children != null) {
if (property.isMany(owner)) {
result.addAll(children);
} else if (children.get(0) != null) {
result.add(children.get(0));
}
}
}
return result;
}
/**
* Returns the list view of the specified feature, or null if the store
* does not handle the feature.
*/
public IList<Object> getList(IProperty property) {
return map == null ? null : map.get(property);
}
public IResource getOwner() {
return owner;
}
/**
* Returns the value view of the specified feature. For a single-valued
* feature, this is the value itself, which may be null. For a
* multi-valued feature, it is just the whole list.
*/
public Object getValue(IProperty property) {
List<Object> list = getList(property);
if (property.isMany(owner)) {
return list;
}
return list.get(0);
}
/**
* Sets either a single element of the specified feature or, if the
* index is -1, the value of the feature.
*/
public boolean set(IProperty property, int index, Object object) {
if (index == -1) {
return setValue(property, object);
}
List<Object> list = getList(property);
if (list != null) {
list.set(index, object);
return true;
}
return false;
}
/**
* Sets the value of the specified feature. For a multi-valued feature,
* the specified value is treated as a {@link java.util.Collection}, and
* all of its elements are added to the emptied list.
*/
public boolean setValue(IProperty property, Object value) {
if (value != null) {
List<Object> list = getList(property);
if (property.isMany(owner)) {
if (list != null) {
list.clear();
}
if (value != null) {
ensureList(property).addAll((Collection<?>) value);
}
} else {
list.set(0, value);
}
}
return true;
}
}
/**
* A single-element implementation of
* {@link net.enilink.komma.rmf.common.util.EList}. The element can be
* modified, but the size of the list may not be changed.
*/
protected static class ModifiableSingletonEList<E> extends AbstractList<E>
implements IList<E> {
private E singleElement;
ModifiableSingletonEList() {
singleElement = null;
}
ModifiableSingletonEList(E element) {
singleElement = element;
}
@Override
public boolean contains(Object o) {
return o == null ? singleElement == null : o.equals(singleElement);
}
@Override
public E get(int index) {
if (index != 0) {
throw new IndexOutOfBoundsException("index=" + index
+ ", size=1");
}
return singleElement;
}
public void move(int index, E o) {
if (index != 0 || !contains(o)) {
throw new IndexOutOfBoundsException("index=" + index
+ ", size=1");
}
}
public E move(int targetIndex, int sourceIndex) {
if (targetIndex != 0) {
throw new IndexOutOfBoundsException("targetIndex="
+ targetIndex + ", size=1");
}
if (sourceIndex != 0) {
throw new IndexOutOfBoundsException("sourceIndex="
+ sourceIndex + ", size=1");
}
return singleElement;
}
@Override
public E set(int index, E element) {
if (index != 0) {
throw new IndexOutOfBoundsException("index=" + index
+ ", size=1");
}
E oldElement = singleElement;
singleElement = element;
return oldElement;
}
@Override
public int size() {
return 1;
}
}
/**
* A <code>ResultAndAffectedObjectsWrappingCommand</code> wraps another
* command to substitute {@link IWrapperItemProvider}s for their values in
* the command's result and affected objects. This is needed as the values
* have been unwrapped for the command to operate on properly.
* <p>
* A list of wrappers from which to substitute is formed by calling
* {@link ItemProviderAdapter#getChildren getChildren} on the command's
* owner(s). Additional wrappers to be considered for the result can by
* specified in the two-argument constructor. The first wrapper whose
* {@link IWrapperItemProvider#getValue value} matches a given value in the
* result or affected objects is substituted for it.
*/
public class ResultAndAffectedObjectsWrappingCommand extends CommandWrapper {
protected Collection<? extends IWrapperItemProvider> additionalWrappers;
protected Set<Object> owners;
public ResultAndAffectedObjectsWrappingCommand(ICommand command) {
super(command);
}
public ResultAndAffectedObjectsWrappingCommand(
ICommand command,
Collection<? extends IWrapperItemProvider> additionalResultWrappers) {
super(command);
additionalWrappers = additionalResultWrappers;
}
/**
* Helper method that builds the list of owners, recursively for command
* wrappers and/or compound commands.
*/
protected void addOwners(ICommand command) {
if (command instanceof CommandWrapper) {
addOwners(((CommandWrapper) command).getCommand());
} else if (command instanceof ExtendedCompositeCommand) {
ExtendedCompositeCommand compoundCommand = (ExtendedCompositeCommand) command;
List<? extends ICommand> commandList = compoundCommand
.getCommandList();
int resultIndex = compoundCommand.getResultIndex();
if (resultIndex == ExtendedCompositeCommand.MERGE_COMMAND_ALL) {
for (ICommand childCommand : commandList) {
addOwners(childCommand);
}
} else {
if (resultIndex == ExtendedCompositeCommand.LAST_COMMAND_ALL) {
resultIndex = commandList.size() - 1;
}
if (resultIndex >= 0) {
addOwners(commandList.get(resultIndex));
}
}
} else if (command instanceof AddCommand) {
owners.add(((AddCommand) command).getOwner());
} else if (command instanceof CreateCopyCommand) {
owners.add(((CreateCopyCommand) command).getOwner());
} else if (command instanceof InitializeCopyCommand) {
owners.add(((InitializeCopyCommand) command).getOwner());
} else if (command instanceof MoveCommand) {
owners.add(((MoveCommand) command).getOwner());
} else if (command instanceof RemoveCommand) {
owners.add(((RemoveCommand) command).getOwner());
} else if (command instanceof ReplaceCommand) {
owners.add(((ReplaceCommand) command).getOwner());
} else if (command instanceof SetCommand) {
owners.add(((SetCommand) command).getOwner());
}
}
@Override
public Collection<?> getAffectedObjects() {
return wrapValues(super.getAffectedObjects(), false);
}
@Override
public CommandResult getCommandResult() {
CommandResult result = getCommandResult();
if (result != null) {
return new CommandResult(result.getStatus(), wrapValues(
result.getReturnValue(), true));
}
return null;
}
/**
* Returns any owners from the wrapped command. If it is a compound
* command, or a wrapped compound command, it may have multiple owners.
* This returns and caches a list of them.
*/
public Collection<Object> getOwners() {
if (owners == null) {
owners = new LinkedHashSet<Object>();
addOwners(getCommand());
}
return owners;
}
protected Collection<? extends IWrapperItemProvider> wrapValues(
Object unwrappedValue, boolean useAdditionalWrappers) {
List<Object> result;
if (unwrappedValue instanceof Collection<?>) {
result = new ArrayList<Object>((Collection<?>) unwrappedValue);
} else {
result = new ArrayList<Object>();
result.add(unwrappedValue);
}
List<IWrapperItemProvider> wrappers = new ArrayList<IWrapperItemProvider>();
// If the adapter factory is composeable, we'll adapt using the
// root.
IAdapterFactory af = adapterFactory instanceof IComposeableAdapterFactory ? ((IComposeableAdapterFactory) adapterFactory)
.getRootAdapterFactory() : adapterFactory;
// Build list of wrapped children from the appropriate adapters.
for (Object owner : getOwners()) {
Collection<?> children = Collections.EMPTY_LIST;
// Either the IEditingDomainItemProvider or
// ITreeItemContentProvider item provider interface can give us
// the children.
Object adapter = af.adapt(owner,
IEditingDomainItemProvider.class);
if (adapter instanceof IEditingDomainItemProvider) {
children = ((IEditingDomainItemProvider) adapter)
.getChildren(owner);
} else {
adapter = af.adapt(owner, ITreeItemContentProvider.class);
if (adapter instanceof ITreeItemContentProvider) {
children = ((ITreeItemContentProvider) adapter)
.getChildren(owner);
}
}
for (Object child : children) {
if (child instanceof IWrapperItemProvider) {
wrappers.add((IWrapperItemProvider) child);
}
}
}
// Add in additional wrappers to search.
if (useAdditionalWrappers && additionalWrappers != null) {
wrappers.addAll(additionalWrappers);
}
// Look for each unwrapped object as a value of a wrapper, replacing
// it with the first one found.
for (ListIterator<Object> i = result.listIterator(); i.hasNext();) {
Object resultObject = i.next();
for (IWrapperItemProvider wrapper : wrappers) {
if (isEquivalentValue(unwrap(wrapper), resultObject)) {
i.set(wrapper);
break;
}
}
}
@SuppressWarnings("unchecked")
Collection<IWrapperItemProvider> collection = (Collection<IWrapperItemProvider>) (Collection<?>) result;
return collection;
}
}
/**
* A <code>ResultAndAffectedObjectsWrappingICommandActionDelegate</code>
* wraps another command that also implements
* <code>ICommandActionDelegate</code>, to substitute
* {@link IWrapperItemProvider}s for its values, which have been unwrapped
* for the command to operate on properly. This substitution is performed
* exactly as by a <code>ResultAndAffectedObjectsWrappingComand</code>, and
* action delegate methods are delegated directly to the wrapped command.
*/
public class ResultAndAffectedObjectsWrappingCommandActionDelegate extends
ResultAndAffectedObjectsWrappingCommand implements
ICommandActionDelegate {
ICommandActionDelegate commandActionDelegate;
/**
* Returns a new
* <code>ResultAndAffectedObjectsWrappingICommandActionDelegate</code>
* for the given command.
*
* @exception ClassCastException
* If the specified command does not implement
* {@link net.enilink.komma.common.command.ICommand} .
*/
public ResultAndAffectedObjectsWrappingCommandActionDelegate(
ICommandActionDelegate command) {
super((ICommand) command);
commandActionDelegate = command;
}
/**
* Returns a new
* <code>ResultAndAffectedObjectsWrappingICommandActionDelegate</code>
* for the given command and list of additional wrappers.
*
* @exception ClassCastException
* If the specified command does not implement
* {@link net.enilink.komma.common.command.ICommand} .
*/
public ResultAndAffectedObjectsWrappingCommandActionDelegate(
ICommandActionDelegate command,
Collection<? extends IWrapperItemProvider> additionalWrappers) {
super((ICommand) command, additionalWrappers);
commandActionDelegate = command;
}
@Override
public String getDescription() {
return commandActionDelegate.getDescription();
}
public Object getImage() {
return commandActionDelegate.getImage();
}
public String getText() {
return commandActionDelegate.getText();
}
public String getToolTipText() {
return commandActionDelegate.getToolTipText();
}
}
/**
* This keeps track of the adapter factory that created this adaptor. It is
* also used as the key/type for this adapter.
*/
protected IAdapterFactory adapterFactory;
/**
* This is used to store all the children features. Derived classes should
* add features to this vector.
*/
protected Set<IProperty> childrenProperties;
/**
* When {@link ChildrenStore}s are to be used to cache children (typically
* to hold wrappers for non-EObject children), this maps adapted objects to
* their corresponding stores. Stores should be accessed and created via
* {@link #getChildrenStore getChildrenStore} and
* {@link #createChildrenStore createChildrenStore}.
*/
protected Map<Object, ChildrenStore> childrenStoreMap;
/**
* This is used to store all the property descriptors. Derived classes
* should add descriptors to this vector.
*/
protected List<IItemPropertyDescriptor> itemPropertyDescriptors;
protected volatile Set<IModelSet> trackedModelSets = Collections
.synchronizedSet(new HashSet<IModelSet>());
protected volatile Map<IReference, IEntity> targets;
/**
* This holds children wrappers that are {@link #wrap created} by this item
* provider, so that they can be {@link #dispose disposed} with it.
*/
protected Disposable wrappers;
/**
* This caches the result returned by {@link #isWrappingNeeded
* isWrappingNeeded} so that it need not be recomputed each time.
*/
protected Boolean wrappingNeeded;
/**
* An instance is created from an adapter factory. The factory is used as a
* key so that we always know which factory created this adapter.
*/
public ItemProviderAdapter(IAdapterFactory adapterFactory) {
this.adapterFactory = adapterFactory;
}
public void addTarget(Object target) {
if (!(target instanceof IEntity)) {
return;
}
if (targets == null) {
synchronized (this) {
if (targets == null) {
targets = Collections
.synchronizedMap(new WeakHashMap<IReference, IEntity>());
}
}
}
targets.put(((IEntity) target).getReference(), (IEntity) target);
if (target instanceof IModelAware) {
IModelSet modelSet = ((IModelAware) target).getModel()
.getModelSet();
if (trackedModelSets.add(modelSet)) {
modelSet.addListener(this);
}
}
}
/**
* If the given object implements {@link IWrapperItemProvider} and specifies
* an index, that index is adjusted by the given increment.
*/
protected void adjustWrapperIndex(Object object, int increment) {
if (object instanceof IWrapperItemProvider) {
IWrapperItemProvider wrapper = (IWrapperItemProvider) object;
int index = wrapper.getIndex();
if (index != CommandParameter.NO_INDEX) {
wrapper.setIndex(index + increment);
}
}
}
/**
* For each element of the given list, starting at <code>from</code>, that
* implements {@link IWrapperItemProvider} and specifies an index, that
* index is adjusted by the given increment.
*/
protected void adjustWrapperIndices(List<Object> objects, int from,
int increment) {
for (Iterator<Object> i = objects.listIterator(from); i.hasNext();) {
adjustWrapperIndex(i.next(), increment);
}
}
/**
* For each element of the given list, between <code>from</code> and
* <code>to</code>, that implements {@link IWrapperItemProvider} and
* specifies an index, that index is adjusted by the given increment.
*/
protected void adjustWrapperIndices(List<Object> objects, int from, int to,
int increment) {
for (Iterator<Object> i = objects.listIterator(from); from < to
&& i.hasNext(); from++) {
adjustWrapperIndex(i.next(), increment);
}
}
protected void collectChildrenProperties(Object object,
Collection<IProperty> childrenProperties) {
if (object instanceof IResource) {
childrenProperties.addAll(((IResource) object)
.getApplicableChildProperties().toSet());
}
}
/**
* This adds to <code>newChildDescriptors</code>, a collection of new child
* descriptors. Typically,
* {@link net.enilink.komma.edit.command.CommandParameter}s will be used as
* descriptors.
*
* @param newChildDescriptors
* The collector for child descriptors
* @param object
* The target object for which new children should be created
*/
protected void collectNewChildDescriptors(ICollector<Object> newChildDescriptors, Object object) {
collectNewChildDescriptors(newChildDescriptors, object, false);
}
/**
* This adds to <code>newChildDescriptors</code>, a collection of new child
* descriptors. Typically,
* {@link net.enilink.komma.edit.command.CommandParameter}s will be used as
* descriptors.
*
* @param newChildDescriptors
* The collector for child descriptors
* @param object
* The target object for which new children should be created
* @param includeOnlyDirectClasses
* Controls if only the direct range classes of the computed
* properties should be returned or all of their sub-classes
*/
protected void collectNewChildDescriptors(ICollector<Object> newChildDescriptors, Object object,
boolean includeOnlyDirectClasses) {
// Subclasses may override to add descriptors.
if (object instanceof IResource) {
for (IProperty property : ((IResource) object).getApplicableChildProperties()) {
if (newChildDescriptors.cancelled()) {
return;
}
if (((IResource) object).getApplicableCardinality(property).getSecond() > 0) {
Set<IClass> ranges = new HashSet<IClass>(
property.getNamedRanges((IResource) object, includeOnlyDirectClasses).toList());
IClass[] rangeArray = ranges.toArray(new IClass[ranges.size()]);
Arrays.sort(rangeArray, new Comparator<IClass>() {
@Override
public int compare(IClass c1, IClass c2) {
String label1 = ModelUtil.getLabel(c1);
String label2 = ModelUtil.getLabel(c2);
if (label1 == null) {
if (label2 == null) {
return 0;
}
return 1;
}
if (label2 == null) {
return -1;
}
return label1.compareTo(label2);
}
});
for (net.enilink.vocab.rdfs.Class rangeClass : rangeArray) {
newChildDescriptors
.add(createChildParameter(property, new ChildDescriptor(Arrays.asList(rangeClass),
childRequiresName((IResource) object, property, rangeClass))));
}
}
}
}
}
/**
* Overwrite to return true for {@link net.enilink.vocab.rdfs.Class}es that
* should be created as named nodes.
*/
protected boolean childRequiresName(IResource subject, IReference property,
net.enilink.vocab.rdfs.Class rangeClass) {
return false;
}
/**
* This creates a primitive
* {@link net.enilink.komma.edit.command.AddCommand}.
*/
protected ICommand createAddCommand(IEditingDomain domain, IResource owner,
IReference property, Collection<?> collection, int index) {
return new AddCommand(domain, owner, property, collection, index);
}
@SuppressWarnings("unchecked")
@Override
public Object createChild(Object owner, Object property,
Object childDescription, IAdaptable info) {
ChildDescriptor childDescriptor = (ChildDescriptor) childDescription;
Collection<? extends IReference> childTypes = (Collection<? extends IReference>) childDescriptor
.getValue();
IModel model;
if (owner instanceof IModel) {
model = (IModel) owner;
} else {
model = ((IObject) owner).getModel();
}
URI parentType = childTypes.isEmpty() ? OWL.TYPE_THING : null;
if (parentType == null && childTypes.size() == 1) {
IClass firstType = model.getManager().find(
childTypes.iterator().next(), IClass.class);
if (firstType.isAbstract()) {
parentType = firstType.getURI();
}
}
URI name = null;
boolean requiresName = childDescriptor.requiresName();
if ((requiresName || parentType != null)) {
IInputCallback input = (IInputCallback) info
.getAdapter(IInputCallback.class);
if (input != null) {
URI nameInput = URIs.createURI("input:name");
URI typeInput = URIs.createURI("input:type");
if (requiresName) {
input.require(nameInput);
}
if (parentType != null) {
input.require(typeInput, parentType);
}
if (input.ask(model)) {
if (requiresName) {
name = (URI) input.get(nameInput);
}
if (parentType != null) {
childTypes = (Collection<? extends IReference>) input
.get(typeInput);
}
} else {
throw new AbortExecutionException();
}
}
}
if (name == null) {
// generate a default URI
name = model.getURI().appendLocalPart(
"entity_" + UUID.randomUUID().toString());
}
return model.getManager().createNamed(name,
childTypes.toArray(new IReference[childTypes.size()]));
}
/**
* This is a convenience method that creates a <code>CommandParameter</code>
* for a given parent property and child object.
*/
protected CommandParameter createChildParameter(Object property,
Object childDescription) {
return new CommandParameter(null, property, childDescription);
}
/**
* Consults {@link #isWrappingNeeded isWrappingNeeded} to decide whether to
* create a store for the children of the given object. If so, the new store
* is created, added to the collection being maintained, and returned. If
* not, null is returned.
*/
protected ChildrenStore createChildrenStore(Object object) {
ChildrenStore store = null;
if (isWrappingNeeded(object)) {
if (childrenStoreMap == null) {
childrenStoreMap = new HashMap<Object, ChildrenStore>();
}
store = new ChildrenStore((IResource) object,
getChildrenProperties(object));
childrenStoreMap.put(object, store);
}
return store;
}
/**
* This implements delegated command creation for the given object.
*/
public ICommand createCommand(Object object, IEditingDomain domain,
Class<? extends ICommand> commandClass,
CommandParameter commandParameter) {
// Commands should operate on the values, not their wrappers. If the
// command's values needed to be unwrapped,
// we'll back get a new CommandParameter.
CommandParameter oldCommandParameter = commandParameter;
commandParameter = unwrapCommandValues(commandParameter, commandClass);
ICommand result = UnexecutableCommand.INSTANCE;
if (commandClass == SetCommand.class) {
result = createSetCommand(
domain,
commandParameter.getOwnerResource(),
commandParameter.getProperty() != null ? (IReference) commandParameter
.getProperty() : getSetProperty(
commandParameter.getOwner(),
commandParameter.getValue()),
commandParameter.getValue(), commandParameter.getIndex());
} else if (commandClass == CopyCommand.class) {
result = createCopyCommand(domain,
commandParameter.getOwnerResource(),
(CopyCommand.Helper) commandParameter.getValue());
} else if (commandClass == CreateCopyCommand.class) {
result = createCreateCopyCommand(domain,
commandParameter.getOwnerResource(),
(CopyCommand.Helper) commandParameter.getValue());
} else if (commandClass == InitializeCopyCommand.class) {
result = createInitializeCopyCommand(domain,
commandParameter.getOwnerResource(),
(CopyCommand.Helper) commandParameter.getValue());
} else if (commandClass == RemoveCommand.class) {
if (commandParameter.getProperty() != null) {
result = createRemoveCommand(domain,
commandParameter.getOwnerResource(),
(IProperty) commandParameter.getProperty(),
commandParameter.getCollection());
} else {
result = factorRemoveCommand(domain, commandParameter);
}
} else if (commandClass == AddCommand.class) {
if (commandParameter.getProperty() != null) {
result = createAddCommand(domain,
commandParameter.getOwnerResource(),
(IProperty) commandParameter.getProperty(),
commandParameter.getCollection(),
commandParameter.getIndex());
} else {
result = factorAddCommand(domain, commandParameter);
}
} else if (commandClass == MoveCommand.class) {
if (commandParameter.getProperty() != null) {
result = createMoveCommand(domain,
commandParameter.getOwnerResource(),
(IProperty) commandParameter.getProperty(),
commandParameter.getValue(),
commandParameter.getIndex());
} else {
result = factorMoveCommand(domain, commandParameter);
}
} else if (commandClass == ReplaceCommand.class) {
result = createReplaceCommand(domain,
commandParameter.getOwnerResource(),
(IProperty) commandParameter.getProperty(),
(IResource) commandParameter.getValue(),
commandParameter.getCollection());
} else if (commandClass == DragAndDropCommand.class) {
DragAndDropCommand.Detail detail = (DragAndDropCommand.Detail) commandParameter
.getProperty();
result = createDragAndDropCommand(domain,
commandParameter.getOwner(), detail.location,
detail.operations, detail.operation,
commandParameter.getCollection());
} else if (commandClass == CreateChildCommand.class) {
CommandParameter newChildParameter = (CommandParameter) commandParameter
.getValue();
result = createCreateChildCommand(domain,
commandParameter.getOwnerResource(),
(IReference) newChildParameter.getProperty(),
newChildParameter.getValue(), newChildParameter.getIndex(),
commandParameter.getCollection());
}
// If necessary, get a command that replaces unwrapped values by their
// wrappers in the result and affected objects.
return wrapCommand(result, object, commandClass, commandParameter,
oldCommandParameter);
}
/**
* This creates a primitive
* {@link net.enilink.komma.edit.command.CopyCommand} .
*/
protected ICommand createCopyCommand(IEditingDomain domain,
IResource owner, CopyCommand.Helper helper) {
return new CopyCommand(domain, owner, helper);
}
/**
* This creates a primitive
* {@link net.enilink.komma.edit.command.CreateChildCommand}.
*/
protected ICommand createCreateChildCommand(IEditingDomain domain,
IResource owner, IReference property, Object value, int index,
Collection<?> collection) {
return new CreateChildCommand(domain, owner, property, value, index,
collection, this);
}
/**
* This creates a primitive
* {@link net.enilink.komma.edit.command.CreateCopyCommand}.
*/
protected ICommand createCreateCopyCommand(IEditingDomain domain,
IResource owner, CopyCommand.Helper helper) {
return new CreateCopyCommand(domain, owner, helper);
}
/**
* This creates a primitive
* {@link net.enilink.komma.edit.command.DragAndDropCommand}.
*/
protected ICommand createDragAndDropCommand(IEditingDomain domain,
Object owner, float location, int operations, int operation,
Collection<?> collection) {
return new DragAndDropCommand(domain, owner, location, operations,
operation, collection);
}
/**
* This creates a primitive
* {@link net.enilink.komma.edit.command.InitializeCopyCommand}.
*/
protected ICommand createInitializeCopyCommand(IEditingDomain domain,
IResource owner, CopyCommand.Helper helper) {
return new InitializeCopyCommand(domain, owner, helper);
}
/**
* Creates an instance that uses a resource locator; indicates whether to be
* multi-line and to sort choices; specifies a static image, a category, and
* filter flags; and determines the cell editor from the type of the
* structural feature.
*/
protected ItemPropertyDescriptor createItemPropertyDescriptor(
IAdapterFactory adapterFactory, IResourceLocator resourceLocator,
String displayName, String description, IReference property,
boolean isSettable, boolean multiLine, boolean sortChoices,
Object staticImage, String category, String[] filterFlags) {
return new ItemPropertyDescriptor(adapterFactory, resourceLocator,
displayName, description, property, isSettable, multiLine,
sortChoices, staticImage, category, filterFlags);
}
/**
* This creates a primitive
* {@link net.enilink.komma.edit.command.MoveCommand} .
*/
protected ICommand createMoveCommand(IEditingDomain domain,
IResource owner, IReference feature, Object value, int index) {
return new MoveCommand(domain, owner, feature, value, index);
}
/**
* This creates a primitive
* {@link net.enilink.komma.edit.command.RemoveCommand}.
*/
protected ICommand createRemoveCommand(IEditingDomain domain,
IResource owner, IReference feature, Collection<?> collection) {
return new RemoveCommand(domain, owner, feature, collection);
}
/**
* This creates a primitive
* {@link net.enilink.komma.edit.command.ReplaceCommand}.
*/
protected ICommand createReplaceCommand(IEditingDomain domain,
IResource owner, IReference feature, IResource value,
Collection<?> collection) {
return new ReplaceCommand(domain, owner, feature, value, collection);
}
/**
* This returned a primitive
* {@link net.enilink.komma.edit.command.SetCommand} , but it has been
* replaced, since this command can now take an index. The replacement
* method still calls this method when invoked with
* {@link CommandParameter#NO_INDEX no index}, to provide backwards
* compatibility.
*
* <p>
* This method will soon be deprecated. New code should use or override the
* {@link #createSetCommand(IEditingDomain, EObject, EStructuralFeature, Object, int)
* new form}, instead.
*/
protected ICommand createSetCommand(IEditingDomain domain, IResource owner,
IReference feature, Object value) {
return new SetCommand(domain, owner, feature, value);
}
/**
* This creates a primitive
* {@link net.enilink.komma.edit.command.SetCommand}.
*/
protected ICommand createSetCommand(IEditingDomain domain, IResource owner,
IReference feature, Object value, int index) {
if (index == CommandParameter.NO_INDEX) {
return createSetCommand(domain, owner, feature, value);
}
return new SetCommand(domain, owner, feature, value, index);
}
/**
* 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. This implementation consults
* {@link #isWrappingNeeded isWrappingNeeded} and, if it is
* <code>true</code>, creates different wrappers that implement
* {@link IWrapperItemProvider} for feature maps, simple attributes, and
* cross references.
*
* By default, {@link #isWrappingNeeded isWrappingNeeded} does not return
* <code>true</code> unless there is at least one feature map or simple
* attribute that contributes children, in order to maintain backwards
* compatibility. As a result, it may be necessary to override that method
* in order to wrap cross-referenced model objects here. Subclasses may also
* override this method, in order to create their own specialized wrappers.
*/
protected Object createWrapper(IResource object, IProperty property,
Object value, int index) {
if (!isWrappingNeeded(object)) {
return value;
}
if (property instanceof DatatypeProperty) {
value = new LiteralValueWrapperItemProvider(value, object,
property, index, getRootAdapterFactory(),
getResourceLocator());
} else if (!property.isContainment()) {
value = new DelegatingWrapperItemProvider(value, object, property,
index, getRootAdapterFactory());
}
return value;
}
/**
* This crops the given text to exclude any control characters. The first
* such character and all following it are replaced by "..."
*/
public String crop(String text) {
if (text != null) {
char[] chars = text.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (Character.isISOControl(chars[i])) {
return text.substring(0, i) + "...";
}
}
}
return text;
}
/**
* This will remove this adapter from all its the targets and dispose any
* remaining children wrappers in the children store.
*/
public void dispose() {
Map<IReference, IEntity> oldTargets = targets;
targets = null;
if (oldTargets != null) {
for (IEntity otherTarget : oldTargets.values()) {
if (otherTarget instanceof IModelAware) {
for (IModelSet modelSet : new ArrayList<>(trackedModelSets)) {
modelSet.removeListener(this);
}
}
}
}
// Dispose the child wrappers.
if (wrappers != null) {
wrappers.dispose();
}
}
/**
* If the given object implements {@link IWrapperItemProvider}, it is
* disposed by calling {@link IDisposable#dispose dispose}. It is also
* removed from {@link #wrappers}, as it will longer need to be disposed
* along with this item provider.
*/
protected void disposeWrapper(Object object) {
if (object instanceof IWrapperItemProvider) {
((IWrapperItemProvider) object).dispose();
if (wrappers != null) {
wrappers.remove(object);
}
}
}
/**
* Each element of the given list that implements
* {@link IWrapperItemProvider} is disposed by calling
* IWrapperItemProvider#dispose dispose} and is removed from
* {@link #wrappers}.
*/
protected void disposeWrappers(List<?> objects) {
for (Object object : objects) {
disposeWrapper(object);
}
}
/**
* This method factors an {@link net.enilink.komma.edit.command.AddCommand}
* for a collection of objects into one or more primitive add command, i.e.,
* one per unique feature.
*/
protected ICommand factorAddCommand(IEditingDomain domain,
CommandParameter commandParameter) {
if (commandParameter.getCollection() == null
|| commandParameter.getCollection().isEmpty()) {
return UnexecutableCommand.INSTANCE;
}
final IResource object = commandParameter.getOwnerResource();
final List<Object> list = new ArrayList<Object>(
commandParameter.getCollection());
int index = commandParameter.getIndex();
CompositeCommand addCommand = new CompositeCommand();
while (!list.isEmpty()) {
Iterator<Object> children = list.listIterator();
final Object firstChild = children.next();
IProperty childProperty = getChildProperty(object, firstChild);
if (childProperty == null) {
break;
}
// If it is a list type value...
if (childProperty.isMany(object)) {
// Correct the index, if necessary.
if (index != CommandParameter.NO_INDEX) {
for (IProperty property : getChildrenProperties(object)) {
if (property.equals(childProperty)) {
break;
}
if (property.isMany(object)) {
Collection<?> values = (Collection<?>) object
.get(property);
if (values != null) {
index -= values.size();
}
} else if (object.get(property) != null) {
index -= 1;
}
}
if (index < 0) {
break;
}
}
// These will be the children belonging to this feature.
//
Collection<Object> childrenOfThisProperty = new ArrayList<Object>();
childrenOfThisProperty.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 (getChildProperty(object, child) == childProperty) {
// Add it to the list and remove it from the other list.
childrenOfThisProperty.add(child);
children.remove();
}
}
// Create a command for this feature,
addCommand.add(createAddCommand(domain, object, childProperty,
childrenOfThisProperty, index));
if (index >= childrenOfThisProperty.size()) {
index -= childrenOfThisProperty.size();
} else {
index = CommandParameter.NO_INDEX;
}
} else if (object.get(childProperty) == null) {
ICommand setCommand = createSetCommand(domain, object,
childProperty, firstChild);
addCommand.add(new CommandWrapper(setCommand) {
protected Collection<?> affected;
@Override
protected CommandResult doExecuteWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
CommandResult result = super.doExecuteWithResult(
progressMonitor, info);
affected = Collections.singleton(firstChild);
return result;
}
@Override
protected CommandResult doRedoWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
CommandResult result = super.doRedoWithResult(
progressMonitor, info);
affected = Collections.singleton(firstChild);
return result;
}
@Override
protected CommandResult doUndoWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
CommandResult result = super.doUndoWithResult(
progressMonitor, info);
affected = Collections.singleton(object);
return result;
}
@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.reduce();
} else {
addCommand.dispose();
return UnexecutableCommand.INSTANCE;
}
}
/**
* This method factors a {@link net.enilink.komma.edit.command.MoveCommand}
* to determine the feature.
*/
protected ICommand factorMoveCommand(IEditingDomain domain,
CommandParameter commandParameter) {
final IResource object = commandParameter.getOwnerResource();
final Object value = commandParameter.getValue();
int index = commandParameter.getIndex();
IProperty childProperty = getChildProperty(object, value);
if (childProperty != null && childProperty.isMany(object)) {
// Compute the relative index as best as possible.
for (IProperty property : getChildrenProperties(object)) {
if (property.equals(childProperty)) {
break;
}
if (property.isMany(object)) {
Collection<?> values = (Collection<?>) object.get(property);
if (values != null) {
index -= values.size();
}
} else if (object.get(property) != null) {
index -= 1;
}
}
// Create a command for this property,
return createMoveCommand(domain, object, childProperty, value,
index);
} else {
return UnexecutableCommand.INSTANCE;
}
}
/**
* This method factors a
* {@link net.enilink.komma.edit.command.RemoveCommand} for a collection of
* objects into one or more primitive remove commands, i.e., one per unique
* feature.
*/
protected ICommand factorRemoveCommand(IEditingDomain domain,
CommandParameter commandParameter) {
if (commandParameter.getCollection() == null
|| commandParameter.getCollection().isEmpty()) {
return UnexecutableCommand.INSTANCE;
}
final IResource object = commandParameter.getOwnerResource();
final List<Object> list = new ArrayList<Object>(
commandParameter.getCollection());
// do nothing if owner is null
if (object == null) {
return IdentityCommand.INSTANCE;
}
// do nothing if self-removal is requested
if (list.size() == 1 && object != null && object.equals(list.get(0))) {
return IdentityCommand.INSTANCE;
}
CompositeCommand removeCommand = new CompositeCommand();
// Iterator over all the child references to factor each child to the
// right reference.
for (IReference property : getChildrenProperties(object)) {
IProperty resolvedProperty = (IProperty) object.getEntityManager()
.find(property);
// If it is a list type value...
if (resolvedProperty.isMany(object)) {
Collection<?> value = (Collection<?>) getPropertyValue(object,
resolvedProperty);
// These will be the children belonging to this feature.
Collection<Object> childrenOfThisProperty = new ArrayList<Object>();
for (ListIterator<Object> objects = list.listIterator(); objects
.hasNext();) {
Object o = objects.next();
// Is this object in this property ...
if (value.contains(o)) {
// Add it to the list and remove it from the other list.
childrenOfThisProperty.add(o);
objects.remove();
}
}
// If we have children to remove for this feature, create a
// command for it.
if (!childrenOfThisProperty.isEmpty()) {
removeCommand.add(createRemoveCommand(domain, object,
property, childrenOfThisProperty));
}
} else {
// It's just a single value
final Object value = getPropertyValue(object, property);
for (ListIterator<Object> objects = list.listIterator(); objects
.hasNext();) {
Object o = objects.next();
// Is this object in this feature...
if (o.equals(value)) {
// Create a command to set this to null and remove the
// object from the other list.
//
ICommand setCommand = createSetCommand(domain, object,
property, null);
removeCommand.add(new CommandWrapper(setCommand) {
protected Collection<?> affected;
@Override
protected CommandResult doExecuteWithResult(
IProgressMonitor progressMonitor,
IAdaptable info) throws ExecutionException {
CommandResult result = super
.doExecuteWithResult(progressMonitor,
info);
affected = Collections.singleton(object);
return result;
}
@Override
protected CommandResult doRedoWithResult(
IProgressMonitor progressMonitor,
IAdaptable info) throws ExecutionException {
CommandResult result = super.doRedoWithResult(
progressMonitor, info);
affected = Collections.singleton(object);
return result;
}
@Override
protected CommandResult doUndoWithResult(
IProgressMonitor progressMonitor,
IAdaptable info) throws ExecutionException {
CommandResult result = super.doUndoWithResult(
progressMonitor, info);
affected = Collections.singleton(value);
return result;
}
@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.reduce();
} else {
removeCommand.dispose();
return UnexecutableCommand.INSTANCE;
}
}
/**
* This convenience method converts the arguments into an appropriate update
* call on the viewer. The event type is a value from the static constants
* in {@link net.enilink.komma.rmf.common.notify.Notifier}.
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public void fireNotifications(
Collection<? extends IViewerNotification> notifications) {
/*
* System.out.println("ItemProviderAdapterFactory.fireNotifyChanged");
* System.out.println(" object = " + object);
* System.out.println(" eventType = " + eventType); if (oldValue
* instanceof Collection) { System.out.println(" oldValue = " +
* CommandParameter.collectionToString((Collection)oldValue)); } else {
* System.out.println(" oldValue = " + oldValue); } if (newValue
* instanceof Collection) { System.out.println(" newValue = " +
* CommandParameter.collectionToString((Collection)newValue)); } else {
* System.out.println(" newValue = " + newValue); }
*/
super.fireNotifications(notifications);
if (adapterFactory instanceof INotificationBroadcaster) {
((INotificationBroadcaster) adapterFactory)
.fireNotifications(notifications);
}
}
/**
* This provides access to the factory.
*/
public IAdapterFactory getAdapterFactory() {
return adapterFactory;
}
/**
* This implements {@link IItemColorProvider#getBackground
* IItemColorProvider.getBackground} by returning null;
*/
public Object getBackground(Object object) {
return null;
}
/**
* This implements {@link ITableItemColorProvider#getBackground
* ITableItemColorProvider.getBackground} by returning null;
*/
public Object getBackground(Object object, int columnIndex) {
return null;
}
/**
* Get the base URL from the resource locator.
*/
public URL getBaseURL() {
return getResourceLocator().getBaseURL();
}
/**
* This returns the most appropriate feature of the object into which the
* given child could be added. This default implementation returns the first
* property returned by {@link #getChildrenProperties(Object)} that has a
* type compatible with the child. You can override this to return a better
* result or to compute it more efficiently.
*/
protected IProperty getChildProperty(Object object, Object child) {
for (IProperty property : getChildrenProperties(object)) {
int maxCard = ((IResource) object).getApplicableCardinality(
property).getSecond();
if (maxCard > 0) {
if (isValidValue(object, child, property)) {
return property;
}
}
}
return null;
}
/**
* This implements {@link ITreeItemContentProvider#getChildren
* ITreeItemContentProvider.getChildren}. If children are already cached in
* a {@link ChildrenStore}, they are returned. Otherwise, children are
* collected from the features returned by {@link #getChildrenFeatures
* getChildrenFeatures}. The collected children may or may not be cached,
* depending on the result of {@link #createChildrenStore
* createChildrenStore}; by default, no store is returned if
* {@link #getChildrenFeatures getChildrenFeatures} returns only containment
* references. All children are optionally {@link #wrap wrapped} before
* being cached and returned. Subclasses may override {@link #createWrapper
* createWrapper} to specify when and with what to wrap children.
*/
public Collection<?> getChildren(Object object) {
ChildrenStore store = getChildrenStore(object);
if (store != null) {
return store.getChildren();
}
store = createChildrenStore(object);
Collection<Object> result = store != null ? null
: new LinkedHashSet<Object>();
IResource resource = (IResource) object;
for (IProperty property : getChildrenProperties(object)) {
Object oneOrMultipleChildren = resource.get(property);
if (oneOrMultipleChildren instanceof Collection<?>) {
Collection<?> children = (Collection<?>) oneOrMultipleChildren;
int index = 0;
for (Object unwrappedChild : children) {
Object child = wrap(resource, property, unwrappedChild,
index);
if (store != null) {
store.getList(property).add(child);
} else {
result.add(child);
}
index++;
}
} else {
Object child = oneOrMultipleChildren;
if (child != null) {
child = wrap(resource, property, child,
CommandParameter.NO_INDEX);
if (store != null) {
store.setValue(property, child);
} else {
result.add(child);
}
}
}
}
return store != null ? store.getChildren() : result;
}
/**
* If this is defined to be something other than an empty list, it is use>d
* to implement {@link #getChildren getChildren}, including in determining
* whether to cache children and, if so, in setting up the store. It is also
* used to deduce the appropriate feature for an <code>AddCommand</code>,
* <code>RemoveCommand</code> or <code>MoveCommand</code> in
* {@link #createCommand createCommand}. If you override those methods, then
* you don't need to implement this.
*/
protected Collection<? extends IProperty> getChildrenProperties(
Object object) {
if (childrenProperties == null) {
childrenProperties = new LinkedHashSet<IProperty>();
collectChildrenProperties(object, childrenProperties);
}
return childrenProperties;
}
/**
* Returns the store for the children of the given object, or null if no
* such store is being maintained.
*/
protected ChildrenStore getChildrenStore(Object object) {
return childrenStoreMap == null ? null
: (ChildrenStore) childrenStoreMap.get(object);
}
/**
* This implements {@link ITableItemLabelProvider#getColumnImage
* ITableItemLabelProvider.getColumnImage} by returning null.
*/
public Object getColumnImage(Object object, int columnIndex) {
return null;
}
/**
* This implements {@link ITableItemLabelProvider#getColumnText
* ITableItemLabelProvider.getColumnText} by returning <code>""</code>.
*/
public String getColumnText(Object object, int columnIndex) {
return "";
}
/**
* This returns the description for {@link CreateChildCommand}.
*/
public String getCreateChildDescription(Object owner, Object property,
Object child, Collection<?> selection) {
if (child instanceof ChildDescriptor) {
child = ((ChildDescriptor) child).getValue();
}
IProperty resolvedProperty = (IProperty) ((IResource) owner)
.getEntityManager().find((IReference) property);
String childType = resolvedProperty instanceof DatatypeProperty ? getTypeText((IProperty) resolvedProperty)
: getTypeText(child);
Object selectionObject = selection == null || selection.isEmpty() ? null
: selection.iterator().next();
if (owner.equals(selectionObject)) {
return getResourceLocator().getString(
"_UI_CreateChild_description",
new Object[] { childType,
getPropertyText(resolvedProperty),
getTypeText(owner) });
}
Object sibling = selectionObject;
IProperty siblingProperty = getChildProperty(owner, sibling);
String siblingType = siblingProperty instanceof DatatypeProperty ? getTypeText((IProperty) siblingProperty)
: getTypeText(sibling);
return getResourceLocator().getString(
"_UI_CreateSibling_description",
new Object[] { childType, getPropertyText(resolvedProperty),
siblingType });
}
/**
* This returns the icon image for
* {@link net.enilink.komma.edit.command.CreateChildCommand}.
*/
public Object getCreateChildImage(Object owner, Object feature,
Object child, Collection<?> selection) {
return KommaEditPlugin.INSTANCE.getImage("full/ctool16/CreateChild");
}
/**
* This returns the result collection for {@link CreateChildCommand}.
*/
public Collection<?> getCreateChildResult(Object child) {
return Collections.singletonList(child);
}
/**
* This returns the label for {@link CreateChildCommand}.
*/
public String getCreateChildText(Object owner, Object property,
Object childDescription, Collection<?> selection) {
if (childDescription instanceof ChildDescriptor) {
childDescription = ((ChildDescriptor) childDescription).getValue();
}
IProperty resolvedProperty = (IProperty) ((IResource) owner)
.getEntityManager().find((IReference) property);
String childType = resolvedProperty instanceof DatatypeProperty ? getTypeText((IProperty) resolvedProperty)
: getTypeText(childDescription);
return getResourceLocator().getString(
property instanceof DatatypeProperty ? "_UI_CreateChild_text3"
: "_UI_CreateChild_text",
new Object[] { childType, getPropertyText(resolvedProperty),
getTypeText(owner) });
}
/**
* This returns the tool tip text for {@link CreateChildCommand}.
*/
public String getCreateChildToolTipText(Object owner, Object feature,
Object childDescription, Collection<?> selection) {
if (childDescription instanceof ChildDescriptor) {
childDescription = ((ChildDescriptor) childDescription).getValue();
}
IProperty resolvedProperty = (IProperty) ((IResource) owner)
.getEntityManager().find((IReference) feature);
String childType = resolvedProperty instanceof DatatypeProperty ? getTypeText((IProperty) feature)
: getTypeText(childDescription);
return getResourceLocator().getString(
"_UI_CreateChild_tooltip",
new Object[] { childType, getPropertyText(resolvedProperty),
getTypeText(owner) });
}
/**
* This implements {@link IItemPropertySource#getEditableValue
* IItemPropertySource.getEditableValue} by simply returning the object
* itself.
*/
public Object getEditableValue(Object object) {
return object;
}
/**
* This implements {@link IStructuredItemContentProvider#getElements
* IStructuredItemContentProvider.getElements} by forwarding the call to
* {@link #getChildren getChildren}. It seems that you almost always want
* getElements and getChildren to return the same thing, so this makes that
* easy.
*/
public Collection<?> getElements(Object object) {
return getChildren(object);
}
@Override
public NotificationFilter<INotification> getFilter() {
return null;
}
/**
* This implements {@link IItemFontProvider#getFont
* IItemFontProvider.getFont} by returning null;
*/
public Object getFont(Object object) {
return null;
}
/**
* This implements {@link ITableItemFontProvider#getFont
* ITableItemFontProvider.getFont} by returning null;
*/
public Object getFont(Object object, int columnIndex) {
return null;
}
/**
* This implements {@link IItemColorProvider#getForeground
* IItemColorProvider.getForeground} by returning null;
*/
public Object getForeground(Object object) {
return null;
}
/**
* This implements {@link ITableItemColorProvider#getForeground
* ITableItemColorProvider.getForeground} by returning null;
*/
public Object getForeground(Object object, int columnIndex) {
return null;
}
/**
* This implements {@link IItemLabelProvider#getImage
* IItemLabelProvider.getImage} by returning null. Most things really should
* have an icon, but not having one is technically correct too.
*/
public Object getImage(Object object) {
return null;
}
/**
* Get an image from the resource locator.
*/
public Object getImage(String key) {
return getResourceLocator().getImage(key);
}
/**
* This implements {@link IEditingDomainItemProvider#getNewChildDescriptors
* IEditingDomainItemProvider.getNewChildDescriptors}, returning descriptors
* for all the possible children that can be added to the specified
* <code>object</code>. Usually, these descriptors will be instances of
* {@link net.enilink.komma.edit.command.CommandParameter}s, containing at
* least the child object and the feature under which it should be added.
*
* <p>
* This implementation invokes {@link #collectNewChildDescriptors
* collectNewChildDescriptors}, which should be overridden by derived
* classes, to build this collection.
*
* <p>
* If <code>sibling</code> is non-null, an index is added to each
* <code>CommandParameter</code> with a multi-valued feature, to ensure that
* the new child object gets added in the right position.
*/
public void getNewChildDescriptors(Object object,
IEditingDomain editingDomain, Object sibling,
final ICollector<Object> descriptors) {
if (object instanceof IResource) {
IResource resource = (IResource) object;
// Build the collection of new child descriptors.
final Collection<Object> newChildDescriptors = new ArrayList<Object>();
collectNewChildDescriptors(new ICollector<Object>() {
@Override
public void add(Iterable<Object> elements) {
for (Object element : elements) {
add(element);
}
}
@Override
public void add(Object element) {
newChildDescriptors.add(element);
}
@Override
public boolean cancelled() {
return descriptors.cancelled();
}
}, object);
// Add child descriptors contributed by extenders.
if (adapterFactory instanceof IChildCreationExtender) {
newChildDescriptors
.addAll(((IChildCreationExtender) adapterFactory)
.getNewChildDescriptors(object, editingDomain));
}
// If a sibling has been specified, add the best index possible to
// each
// CommandParameter.
if (sibling != null) {
sibling = unwrap(sibling);
// Find the index of a feature containing the sibling, or an
// equivalent value, in the collection of children
// features.
Collection<? extends IProperty> childrenProperties = getChildrenProperties(object);
int siblingPropertyIndex = -1;
int i = 0;
PROPERTIES_LOOP: for (IProperty property : childrenProperties) {
Object propertyValue = resource.get(property);
if (property.isMany(resource)) {
for (Object value : (Collection<?>) propertyValue) {
if (isEquivalentValue(sibling, value)) {
siblingPropertyIndex = i;
break PROPERTIES_LOOP;
}
}
} else if (isEquivalentValue(sibling, propertyValue)) {
siblingPropertyIndex = i;
break PROPERTIES_LOOP;
}
++i;
}
// For each CommandParameter with a non-null, multi-valued
// property...
DESCRIPTORS_LOOP: for (Object descriptor : newChildDescriptors) {
if (descriptor instanceof CommandParameter) {
CommandParameter parameter = (CommandParameter) descriptor;
IProperty childProperty = (IProperty) parameter
.getObjectProperty();
if (childProperty == null
|| !childProperty.isMany(resource)) {
continue DESCRIPTORS_LOOP;
}
// Look for the sibling value or an equivalent in the
// new
// child's feature. If it is found, the child should
// immediately follow it.
i = 0;
for (Object v : (Collection<?>) resource
.get(childProperty)) {
if (isEquivalentValue(sibling, v)) {
parameter.index = i + 1;
continue DESCRIPTORS_LOOP;
}
++i;
}
// Otherwise, if a sibling property was found, iterate
// through the children property to find the index of
// the child property...
if (siblingPropertyIndex != -1) {
i = 0;
for (IProperty property : childrenProperties) {
if (property.equals(childProperty)) {
// If the child property follows the sibling
// property, the child should be first in
// its
// property.
if (i > siblingPropertyIndex) {
parameter.index = 0;
}
continue DESCRIPTORS_LOOP;
}
++i;
}
}
}
}
}
descriptors.add(newChildDescriptors);
}
}
/**
* This implements {@link ITreeItemContentProvider#getParent
* ITreeItemContentProvider.getParent} by returning the EMF object's
* container. This is used by certain commands to find an owner, where none
* is specified, and by the viewers, when trying to locate an arbitrary
* object within the view (i.e. during select and reveal operation).
*/
public Object getParent(Object object) {
if (!(object instanceof IResource)) {
return null;
}
return ((IResource) object).getContainer();
}
/**
* This convenience method finds a particular descriptor given its
* {@link IItemPropertyDescriptor#getId(Object) ID} or
* {@link IItemPropertyDescriptor#getFeature(Object) feature}.
*/
public IItemPropertyDescriptor getPropertyDescriptor(Object object,
Object propertyId) {
for (IItemPropertyDescriptor itemPropertyDescriptor : getPropertyDescriptors(object)) {
if (propertyId.equals(itemPropertyDescriptor.getId(object))
|| propertyId.equals(itemPropertyDescriptor
.getProperty(object))) {
return itemPropertyDescriptor;
}
}
return null;
}
/**
* This implements {@link IItemPropertySource#getPropertyDescriptors
* IItemPropertySource.getPropertyDescriptors} by returning the locally
* stored vector of descriptors. This vector could be populated in the
* constructor of a derived class but it's probably more efficient to create
* them only on demand by overriding this method. You'll probably want to
* call super.getPropertyDescriptors if you do this, since you may have one
* adapter derive from another.
*/
public List<IItemPropertyDescriptor> getPropertyDescriptors(Object object) {
if (itemPropertyDescriptors == null) {
itemPropertyDescriptors = new ArrayList<IItemPropertyDescriptor>();
if (object instanceof IResource) {
for (IProperty property : ((IResource) object)
.getRelevantProperties()) {
itemPropertyDescriptors.add(createItemPropertyDescriptor(
getRootAdapterFactory(), this,
ModelUtil.getLabel(property), "", property, true,
false, true, null, null, null));
}
}
}
return itemPropertyDescriptors;
}
/**
* This looks up the name of the specified feature.
*/
protected String getPropertyText(IProperty property) {
return ModelUtil.getLabel(property);
}
/**
* This method is called by {@link #factorRemoveCommand factorRemoveCommand}
* to retrieve the children objects of the features returned from
* {@link #getChildrenFeatures getChildrenFeatures}.
*/
protected Object getPropertyValue(IResource object, IReference feature) {
return object.get(feature);
}
/**
* This implements a PropertySource by delegating to the descriptor, which
* is assumed to support the IItemPropertyDescriptor interface
*/
public Object getPropertyValue(Object object, String property) {
return getPropertyDescriptor(object, property).getPropertyValue(object);
}
/**
* Get the resource locator for this adapter's resources.
*/
protected IResourceLocator getResourceLocator() {
return KommaEditPlugin.INSTANCE;
}
/**
* Get the resource locator from the adapter of the object, if possible. it
* can be any object, i.e., it may not the type object for which this
* adapter is applicable.
*/
protected IResourceLocator getResourceLocator(Object anyObject) {
if (adapterFactory instanceof IComposeableAdapterFactory) {
Object adapter = ((IComposeableAdapterFactory) adapterFactory)
.getRootAdapterFactory().adapt(anyObject,
IItemLabelProvider.class);
if (adapter instanceof IResourceLocator) {
return (IResourceLocator) adapter;
}
}
return getResourceLocator();
}
/**
* Gets the root factory if this local adapter factory is composed,
* otherwise just the local one.
*/
protected IAdapterFactory getRootAdapterFactory() {
if (adapterFactory instanceof IComposeableAdapterFactory) {
return ((IComposeableAdapterFactory) adapterFactory)
.getRootAdapterFactory();
}
return adapterFactory;
}
/**
* If this is defined to be something other than an empty list, it is used
* to implement {@link #getSetFeature getSetFeature} and to deduce the EMF
* feature in the SetCommand {@link #createCommand createCommand}. If you
* override those, then you don't need to implement this.
*/
protected Collection<? extends IReference> getSetProperties(Object object) {
return Collections.emptyList();
}
/**
* This returns the most appropriate feature of the object into which the
* value be set. This default implementation returns the first feature
* returned by {@link #getSetFeatures getSetFeatures} that has a type
* compatible with the value. You can override this to return a better
* result or to compute it more efficiently.
*/
protected IReference getSetProperty(Object object, Object value) {
// Iterate over all the set feature to factor each child to the right
// reference.
for (IReference property : getSetProperties(object)) {
if (isValidValue(object, value, property)) {
return property;
}
}
return null;
}
/**
* Get a translated string from the resource locator.
*/
public String getString(String key) {
return getString(key, shouldTranslate());
}
/**
* Get a translated string from the resource locator.
*/
public String getString(String key, boolean translate) {
return getResourceLocator().getString(key, translate);
}
/**
* Get a translated string from the resource locator, with substitutions.
*/
public String getString(String key, Object... substitutions) {
return getString(key, substitutions, shouldTranslate());
}
/**
* Get a translated string from the resource locator, with substitutions.
*/
public String getString(String key, Object[] substitutions,
boolean translate) {
return getResourceLocator().getString(key, substitutions, translate);
}
/**
* Get a translated string from the resource locator, substituting another
* such translated string.
*/
protected String getString(String key, String s0) {
return getString(key, s0, shouldTranslate());
}
/**
* Get a translated string from the resource locator, substituting another
* such translated string.
*/
protected String getString(String key, String s0, boolean translate) {
IResourceLocator resourceLocator = getResourceLocator();
return resourceLocator.getString(key,
new Object[] { resourceLocator.getString(s0, translate) },
translate);
}
/**
* Get a translated string from the resource locator, substituting two other
* such translated strings.
*/
protected String getString(String key, String s0, String s1) {
return getString(key, s0, s1, shouldTranslate());
}
/**
* Get a translated string from the resource locator, substituting two other
* such translated strings.
*/
protected String getString(String key, String s0, String s1,
boolean translate) {
IResourceLocator resourceLocator = getResourceLocator();
return resourceLocator.getString(key,
new Object[] { resourceLocator.getString(s0, translate),
resourceLocator.getString(s1, translate) }, translate);
}
/**
* This implements {@link IItemLabelProvider#getText
* IItemLabelProvider.getText} by simply calling toString on the argument.
* This will often be correct as is.
*/
public String getText(Object object) {
return object.toString();
}
/**
* This looks up the name of the type of the specified attribute.
*/
protected String getTypeText(IProperty property) {
StringBuilder rangeSb = new StringBuilder();
for (net.enilink.vocab.rdfs.Class rangeClass : property.getRdfsRanges()) {
if (rangeSb.length() > 0) {
rangeSb.append(", ");
}
rangeSb.append(ModelUtil.getLabel(rangeClass));
}
return getString("_UI_Unknown_datatype");
}
/**
* This looks up the name of the type of the specified object.
*/
protected String getTypeText(Object object) {
if (object instanceof Collection<?>) {
StringBuilder typesSb = new StringBuilder();
for (Object type : (Collection<?>) object) {
if (typesSb.length() > 0) {
typesSb.append(", ");
}
typesSb.append(ModelUtil.getLabel(type));
}
return typesSb.toString();
} else if (object instanceof IResource) {
StringBuilder typesSb = new StringBuilder();
for (IClass typeClass : ((IResource) object)
.getDirectNamedClasses()) {
if (typesSb.length() > 0) {
typesSb.append(", ");
}
typesSb.append(ModelUtil.getLabel(typeClass));
}
return typesSb.toString();
}
return ModelUtil.getLabel(object);
}
/**
* This implements {@link IUpdateableItemText#getUpdateableText
* IUpdateableItemText.getUpdateableText} by simply calling {@link #getText}
* . This will often be correct as is.
*/
public String getUpdateableText(Object object) {
return getText(object);
}
/**
* Returns a collection of any objects in the given command parameter's
* {@link net.enilink.komma.edit.command.CommandParameter#getCollection
* collection} and
* {@link net.enilink.komma.edit.command.CommandParameter#getValue value},
* that implement {@link IWrapperItemProvider}.
*/
protected Collection<? extends IWrapperItemProvider> getWrappedValues(
CommandParameter commandParameter) {
Collection<?> collection = commandParameter.getCollection();
Object value = commandParameter.getValue();
if (collection != null) {
List<IWrapperItemProvider> result = new ArrayList<IWrapperItemProvider>(
collection.size() + 1);
for (Object o : collection) {
if (o instanceof IWrapperItemProvider) {
result.add((IWrapperItemProvider) o);
}
}
if (value instanceof IWrapperItemProvider) {
result.add((IWrapperItemProvider) value);
}
return result;
} else if (value instanceof IWrapperItemProvider) {
return Collections.singletonList((IWrapperItemProvider) value);
}
return Collections.emptyList();
}
/**
* This implements {@link ITreeItemContentProvider#hasChildren
* ITreeItemContentProvider.hasChildren} by simply testing whether
* {@link #getChildren getChildren} returns any children. This
* implementation will always be right, however, for efficiency you may want
* to override it to return false or use the optimized approach offered by
* {@link #hasChildren(Object, boolean)} (i.e. by passing <code>true</code>
* as the second argument).
*
* @see #hasChildren(Object, boolean)
*/
public boolean hasChildren(Object object) {
return hasChildren(object, false);
}
/**
* This implements {@link ITreeItemContentProvider#hasChildren
* ITreeItemContentProvider.hasChildren}. The approach taken depends on the
* value of <code>optimized</code>. The traditional, non-optimized approach
* simply tests whether whether {@link #getChildren getChildren} returns any
* children. The new, optimized approach actually iterates through and tests
* the {@link #getChildrenFeatures children features} directly, avoiding
* accessing the children objects themselves, wherever possible.
*/
protected boolean hasChildren(Object object, boolean optimized) {
if (!optimized) {
return !getChildren(object).isEmpty();
}
if (object instanceof IResource) {
IResource resource = (IResource) object;
for (IProperty property : getChildrenProperties(object)) {
if (property.isMany(resource)) {
Collection<?> children = (Collection<?>) resource
.get(property);
if (children != null && !children.isEmpty()) {
return true;
}
} else if (resource.get(property) != null) {
return true;
}
}
}
return false;
}
/**
* The adapter factory is used as the type key. This returns true, only if
* this adapter was created by the given factory.
*/
@Override
public boolean isAdapterForType(Object type) {
return type == adapterFactory;
}
/**
* Returns whether the given value is to be considered equivalent to the
* given reference value. This is true if it is the reference value or if it
* is a feature map entry whose value is the reference value.
*/
protected boolean isEquivalentValue(Object value, Object referenceValue) {
return value.equals(referenceValue);
}
/**
* This implements PropertySource by delegating to the descriptor, which is
* assumed to support the IItemPropertyDescriptor interface
*/
public boolean isPropertySet(Object object, String property) {
return getPropertyDescriptor(object, property).isPropertySet(object);
}
/**
* This returns whether the given value is an appropriate instance for the
* given feature of the given object.
*/
protected boolean isValidValue(Object object, Object value,
IReference property) {
IProperty resolvedProperty = (IProperty) ((IResource) object)
.getEntityManager().find(property);
return resolvedProperty.isRangeCompatible((IResource) object, value);
}
/**
* Returns whether this item provider may need to use wrappers for some or
* all of the values it returns as {@link #getChildren children}. This is
* used to determine whether to use a store to keep track of children and
* whether to use command wrappers that re-wrap results and affected
* objects. The default implementation of {@link #createWrapper
* createWrapper} also tests this method and will not create any wrappers if
* it returns <code>false</code>.
*
* <p>
* This implementation consults {@link #getChildrenProperties
* getChildrenProperties}, returning true if any feature map or simple
* attributes contribute children. This provides backwards compatibility
* with pre-2.0 subclasses and enables the more useful new default behaviour
* for attributes, which were previously not allowed. Subclasses may
* override this to enable wrapping of cross-referenced model objects, or to
* immediately return <code>true</code> or <code>false</code>, as desired.
* This is a convenient way to disable all of the new wrapping features in
* 2.0.
*/
protected boolean isWrappingNeeded(Object object) {
if (wrappingNeeded == null) {
wrappingNeeded = Boolean.FALSE;
for (IProperty f : getChildrenProperties(object)) {
if (f instanceof DatatypeProperty) {
wrappingNeeded = Boolean.TRUE;
}
}
}
return wrappingNeeded.booleanValue();
}
@Override
public void notifyChanged(Collection<? extends INotification> notifications) {
updateChildren(notifications);
}
/**
* This adds an overlay to the given image if the object is controlled.
*/
protected Object overlayImage(Object object, Object image) {
// if (AdapterFactoryEditingDomain.isControlled(object)) {
// List<Object> images = new ArrayList<Object>(2);
// images.add(image);
// images.add(KommaEditPlugin.INSTANCE
// .getImage("full/ovr16/ControlledObject"));
// image = new ComposedImage(images);
// }
return image;
}
public void removeTarget(Object target) {
// TODO remove listener from model set if there is
// no other target from the same one
if (targets != null) {
targets.remove(target);
}
if (childrenStoreMap != null) {
ChildrenStore store = childrenStoreMap.remove(target);
if (store != null && wrappers != null) {
for (Object child : store.getChildren()) {
if (wrappers.remove(child)) {
((IDisposable) child).dispose();
}
}
}
}
}
/**
* This implements PropertySource.resetPropertyValue by delegating to the
* descriptor, which is assumed to support the IItemPropertyDescriptor
* interface
*/
public void resetPropertyValue(Object object, String property) {
getPropertyDescriptor(object, property).resetPropertyValue(object);
}
protected IEntity resolveReference(Object reference) {
if (targets != null) {
IEntity result = targets.get(reference);
if (result != null) {
return result;
}
}
return (IEntity) ((reference instanceof IEntity) ? reference : null);
}
/**
* This implements PropertySource by delegating to the descriptor, which is
* assumed to support the IItemPropertyDescriptor interface
*/
public void setPropertyValue(Object object, String property, Object value) {
getPropertyDescriptor(object, property).setPropertyValue(object, value);
}
/**
* Indicates whether strings should be translated by default.
*
* @return <code>true</code> if strings should be translated by default;
* <code>false</code> otherwise.
*/
protected boolean shouldTranslate() {
return true;
}
/**
* If the given object implements {@link IWrapperItemProvider}, it is
* unwrapped by obtaining a value from {@link IWrapperItemProvider#getValue
* getValue}. The unwrapping continues until a non-wrapper value is
* returned. This iterative unwrapping is required because values may be
* repeatedly wrapped, as children of a delegating wrapper.
*/
protected Object unwrap(Object object) {
while (object instanceof IWrapperItemProvider) {
object = ((IWrapperItemProvider) object).getValue();
}
return object;
}
/**
* If the given command parameter contains wrapped values that need to be
* unwrapped for the given command class to operate on, a new command
* parameter will be returned, with those values unwrapped; otherwise, the
* original one is returned. For most commands, any objects in the
* {@link net.enilink.komma.edit.command.CommandParameter#getCollection
* collection} or in the
* {@link net.enilink.komma.edit.command.CommandParameter#getValue value}
* that implement {@link IWrapperItemProvider} will be {@link #unwrap
* unwrapped}. {@link net.enilink.komma.edit.command.DragAndDropCommand} is
* never unwrapped.
*/
protected CommandParameter unwrapCommandValues(
CommandParameter commandParameter,
Class<? extends ICommand> commandClass) {
// We need the wrapper, not the item provider, to handle a
// DragAndDropCommand; the move, add, remove, etc. commands
// that implement it will have their values unwrapped as usual.
//
if (commandClass == DragAndDropCommand.class) {
return commandParameter;
}
ArrayList<Object> newCollection = null;
Collection<?> oldCollection = commandParameter.getCollection();
// Unwrap collection.
//
if (oldCollection != null) {
for (Object oldValue : oldCollection) {
Object newValue = unwrap(oldValue);
// If the first wrapped value is found...
//
if (newValue != oldValue && newCollection == null) {
// Allocate the new collection, and populate it up to this
// point.
//
newCollection = new ArrayList<Object>(oldCollection.size());
for (Object o : oldCollection) {
if (o == oldValue)
break;
newCollection.add(o);
}
}
// If a new collection was allocated, continue to populate it.
//
if (newCollection != null) {
newCollection.add(newValue);
}
}
}
// Unwrap value.
//
Object oldValue = commandParameter.getValue();
Object newValue = unwrap(oldValue);
if (newCollection != null || newValue != oldValue) {
commandParameter = new CommandParameter(commandParameter.owner,
commandParameter.property, newValue, newCollection,
commandParameter.index);
}
return commandParameter;
}
/**
* Updates any cached children based on the given notification. If a
* {@link ChildrenStore} exists for its notifier, then the children of the
* specified feature are updated.
*
* <p>
* Also clears the cache of childrenProperties upon changes to statements
* with the subPropertyOf predicate so that applicable child properties are
* properly refreshed.
*
* <p>
* Existing children in the store that correspond to any set, removed or
* unset values are {@link #disposeWrapper disposed} before being removed
* from the store. When children are added to, removed from, or moved within
* a property, the indices of any others affected are
* {@link #adjustWrapperIndex adjusted}. Since this method is typically
* called from {@link #notifyChanged(INotification) notifyChanged}, which,
* in subclasses, is often invoked repeatedly up the inheritance chain, it
* can be safely called repeatedly for a single notification, and only the
* first such call will have an effect. Such repeated calls may not,
* however, safely be interleaved with calls for another notification.
*/
protected void updateChildren(
Collection<? extends INotification> notifications) {
for (INotification notification : notifications) {
// for changes to subPropertyOf, delete the childrenProperties cache
if (notification instanceof IStatementNotification) {
if (((IStatementNotification) notification).getPredicate()
.equals(RDFS.PROPERTY_SUBPROPERTYOF)) {
childrenProperties = null;
}
}
ChildrenStore childrenStore = getChildrenStore(notification
.getSubject());
if (childrenStore == null) {
continue;
}
if (notification instanceof IStatementNotification) {
IStatementNotification stmtNotification = (IStatementNotification) notification;
IProperty property = (IProperty) childrenStore.getOwner()
.getEntityManager()
.find((IReference) stmtNotification.getPredicate());
// ensure that cached data is discarded
childrenStore.getOwner().refresh(property);
} else if (notification instanceof IPropertyNotification) {
IPropertyNotification propertyNotification = (IPropertyNotification) notification;
IProperty property = (IProperty) childrenStore.getOwner()
.getEntityManager()
.find((IReference) propertyNotification.getProperty());
// ensure that cached data is discarded
childrenStore.getOwner().refresh(property);
IList<Object> children = childrenStore.getList(property);
if (children != null) {
int index = propertyNotification.getPosition();
switch (propertyNotification.getEventType()) {
case IPropertyNotification.UNSET: {
// Ignore the unset notification for an isMany
// feature; the value is boolean in this case.
if (property.isMany(childrenStore.getOwner())) {
break;
}
// continue to next case
}
case IPropertyNotification.SET: {
Object oldChild = childrenStore.get(property, index);
Object newValue = propertyNotification.getNewValue();
if (unwrap(oldChild).equals(newValue)) {
if (property.isMany(childrenStore.getOwner())
&& index == IPropertyNotification.NO_INDEX) {
disposeWrappers((List<?>) oldChild);
} else {
disposeWrapper(oldChild);
}
Object newChild = newValue == null
&& index == IPropertyNotification.NO_INDEX ? null
: wrap(childrenStore.getOwner(), property,
newValue, index);
childrenStore.set(property, index, newChild);
}
break;
}
case IPropertyNotification.ADD: {
Collection<?> values = (Collection<?>) childrenStore
.getOwner().get(property);
if (children.size() != values.size()) {
Object newValue = propertyNotification
.getNewValue();
adjustWrapperIndices(children, index, 1);
children.add(
index,
wrap(childrenStore.getOwner(), property,
newValue, index));
}
break;
}
case IPropertyNotification.REMOVE: {
Collection<?> values = (Collection<?>) childrenStore
.getOwner().get(property);
if (children.size() != values.size()) {
disposeWrapper(children.remove(index));
adjustWrapperIndices(children, index, -1);
}
break;
}
case IPropertyNotification.ADD_MANY: {
Collection<?> values = (Collection<?>) childrenStore
.getOwner().get(property);
if (children.size() != values.size()) {
if (propertyNotification.getOldValue() != null) {
throw new IllegalArgumentException(
"No old value expected");
}
List<?> newValues = (List<?>) propertyNotification
.getNewValue();
List<Object> newChildren = new ArrayList<Object>(
newValues.size());
int offset = 0;
for (Object newValue : newValues) {
newChildren.add(wrap(childrenStore.getOwner(),
property, newValue, index + offset++));
}
adjustWrapperIndices(children, index, offset);
children.addAll(index, newChildren);
}
break;
}
case IPropertyNotification.REMOVE_MANY: {
// No index specified when removing all
// elements.
//
if (index == IPropertyNotification.NO_INDEX)
index = 0;
Collection<?> values = (Collection<?>) childrenStore
.getOwner().get(property);
if (children.size() != values.size()) {
if (propertyNotification.getNewValue() instanceof int[]) {
int[] indices = (int[]) propertyNotification
.getNewValue();
for (int i = indices.length - 1; i >= 0; i--) {
disposeWrapper(children.remove(indices[i]));
adjustWrapperIndices(children, indices[i],
-1);
}
} else {
int len = ((List<?>) propertyNotification
.getOldValue()).size();
List<?> sl = children.subList(index, index
+ len);
disposeWrappers(sl);
sl.clear();
adjustWrapperIndices(children, index, -len);
}
}
break;
}
case IPropertyNotification.MOVE: {
int oldIndex = ((Integer) propertyNotification
.getOldValue()).intValue();
List<?> values = (List<?>) childrenStore.getOwner()
.get(property);
boolean didMove = true;
for (int i = Math.min(oldIndex, index), end = Math.max(
oldIndex, index); didMove && i <= end; i++) {
didMove = unwrap(children.get(i)).equals(
values.get(i));
}
if (!didMove) {
int delta = index - oldIndex;
if (delta < 0) {
adjustWrapperIndices(children, index, oldIndex,
1);
}
children.move(index, oldIndex);
adjustWrapperIndex(children.get(index), delta);
if (delta > 0) {
adjustWrapperIndices(children, oldIndex, index,
-1);
}
}
break;
}
}
}
}
}
}
/**
* 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.
*/
protected Object wrap(IResource object, IProperty property, Object value,
int index) {
if (!property.isMany(object) && index != CommandParameter.NO_INDEX) {
System.out.println("Bad wrap index.");
System.out.println(" object: " + object);
System.out.println(" property: " + property);
System.out.println(" value: " + value);
System.out.println(" index: " + index);
(new IllegalArgumentException("Bad wrap index.")).printStackTrace();
}
Object wrapper = createWrapper(object, property, value, index);
if (wrapper == null) {
wrapper = value;
} else if (wrapper != value) {
if (wrappers == null) {
wrappers = new Disposable();
}
wrappers.add(wrapper);
}
return wrapper;
}
/**
* Returns a version of the given command that automatically re-wraps values
* that have been unwrapped when returning them as the command's result or
* affected objects. This is only done if {@link #isWrappingNeeded
* isWrappingNeeded} returns <code>true</code>, and never for a
* {@link net.enilink.komma.edit.command.DragAndDropCommand}.
*/
protected ICommand wrapCommand(ICommand command, Object object,
Class<? extends ICommand> commandClass,
CommandParameter commandParameter,
CommandParameter oldCommandParameter) {
if (isWrappingNeeded(object)
&& commandClass != DragAndDropCommand.class) {
// Wrappers from the old command parameter must be considered in
// order for cut and paste to work.
Collection<? extends IWrapperItemProvider> oldWrappers;
if (commandParameter != oldCommandParameter) {
oldWrappers = getWrappedValues(oldCommandParameter);
} else {
oldWrappers = Collections.emptyList();
}
command = command instanceof ICommandActionDelegate ? new ResultAndAffectedObjectsWrappingCommandActionDelegate(
(ICommandActionDelegate) command, oldWrappers)
: new ResultAndAffectedObjectsWrappingCommand(command,
oldWrappers);
}
return command;
}
}