/** * Returns a description for a value's single property. Subclasses may use this in creating a * property descriptor, and user subclasses may override it to provide a specific name. */ protected String getPropertyDescription() { return KommaEditPlugin.INSTANCE.getString("_UI_ValueProperty_description"); }
/** * A basic implementation of <code>IWrapperProvider</code> from which others can extend. This class * provides all the methods required to implement the following item provider interfaces: * * <ul> * <li>{@link IStructuredItemContentProvider} * <li>{@link ITreeItemContentProvider} * <li>{@link IItemLabelProvider} * <li>{@link IItemFontProvider} * <li>{@link IItemColorProvider} * <li>{@link IItemPropertySource} * <li>{@link IEditingDomainItemProvider} * </ul> * * <p>Subclasses should declare which of these interfaces they are meant to implement, and override * methods as needed. In addition, a partial implementation for {@link IUpdateableItemText} is * provided, along with additional methods and classes that are useful in implementing multiple * subclasses. */ public class WrapperItemProvider implements IWrapperItemProvider { /** The wrapped value. */ protected Object value; /** The object that owns the value. */ protected Object owner; /** The structural feature, if applicable, through which the value can be set and retrieved. */ protected IReference property; /** * The index at which the value is located. If {@link #property} is non-null, this index is within * that feature. */ protected int index; /** The adapter factory for the owner's item provider. */ protected IAdapterFactory adapterFactory; /** * Creates an instance. The adapter factory of the owner's item provider may be needed for echoing * notifications and providing property descriptors. */ public WrapperItemProvider( Object value, Object owner, IReference property, int index, IAdapterFactory adapterFactory) { this.value = value; this.owner = owner; this.property = property; this.index = index; this.adapterFactory = adapterFactory; } /** * Disposes the wrapper by deactivating any notification that this wrapper may provide. Since this * implementation does not provide any notification, this method does nothing. */ public void dispose() { // Do nothing. } /** Returns the wrapped value. */ public Object getValue() { return value; } /** Returns the object that owns the value. */ public Object getOwner() { return owner; } /** * Returns the structural feature through which the value can be set and retrieved, or null if the * feature is unknown or not applicable. */ public IReference getProperty() { return property; } /** * The index at which the value is located, or {@link * net.enilink.komma.edit.command.CommandParameter#NO_INDEX} if the index isn't known to the * wrapper. If {@link #property} is non-null, this index is within that feature. */ public int getIndex() { return index; } /** Sets the index. Has no effect if the index isn't known to the wrapper. */ public void setIndex(int index) { this.index = index; } /** * {@link IStructuredItemContentProvider#getElements IStructuredItemContentProvider.getElements} * is implemented by forwarding the call to {@link #getChildren getChildren}. */ public Collection<?> getElements(Object object) { return getChildren(object); } /** * {@link ITreeItemContentProvider#getChildren ITreeItemContentProvider.getChildren} is * implemented to return an empty list. Subclasses may override it to return something else. */ public Collection<?> getChildren(Object object) { return Collections.emptyList(); } /** * {@link ITreeItemContentProvider#hasChildren ITreeItemContentProvider.hasChildren} is * implemented by testing whether the collection returned by {@link #getChildren getChildren} is * non-empty. */ public boolean hasChildren(Object object) { return !getChildren(object).isEmpty(); } /** * {@link ITreeItemContentProvider#getParent ITreeItemContentProvider.getParent} is implemented by * returning the {@link #owner}. */ public Object getParent(Object object) { return owner; } /** * {@link net.enilink.komma.edit.provider.IItemLabelProvider#getText IItemLabelProvider.getText} * is implemented by returning a non-null value, as a string, or "null". */ public String getText(Object object) { return value != null ? value.toString() : "null"; } /** * {@link net.enilink.komma.edit.provider.IItemLabelProvider#getImage IItemLabelProvider.getImage} * is implemented by returning the default icon for an EMF.Edit item. */ public Object getImage(Object object) { return KommaEditPlugin.INSTANCE.getImage("full/obj16/Item"); } /** * {@link net.enilink.komma.edit.provider.IItemFontProvider#getFont IItemFontProvider.getFont} is * implemented by returning null. */ public Object getFont(Object object) { return null; } /** * {@link net.enilink.komma.edit.provider.IItemColorProvider#getForeground * IItemColorProvider.getForeground} is implemented by returning null. */ public Object getForeground(Object object) { return null; } /** * {@link net.enilink.komma.edit.provider.IItemColorProvider#getBackground * IItemColorProvider.getBackground} is implemented by returning null. */ public Object getBackground(Object object) { return null; } /** * {@link IUpdateableItemText#getUpdateableText IUpdateableItemText.getUpdateableText} is * implemented by forwarding the call to {@link #getText getText}. */ public String getUpdateableText(Object object) { return getText(object); } /** * {@link IItemPropertySource#getPropertyDescriptors IItemPropertySource.getPropertyDescriptors} * is implemented to return an empty list. Subclasses may override it to return something else. */ public List<IItemPropertyDescriptor> getPropertyDescriptors(Object object) { return Collections.emptyList(); } /** * {@link IItemPropertySource#getPropertyDescriptor IItemPropertySource.getPropertyDescriptor} is * implemented by iterating over the descriptors returned by {@link #getPropertyDescriptors * getPropertyDescriptors}, and returning the first descriptor whose {@link * IItemPropertyDescriptor#getId(Object) ID} or {@link IItemPropertyDescriptor#getProperty(Object) * feature} matches the specified ID, or <code>null</code> if none match. */ public IItemPropertyDescriptor getPropertyDescriptor(Object object, Object propertyId) { for (IItemPropertyDescriptor descriptor : getPropertyDescriptors(object)) { if (propertyId.equals(descriptor.getId(object)) || propertyId.equals(descriptor.getProperty(object))) { return descriptor; } } return null; } /** * {@link IItemPropertySource#getEditableValue IItemPropertySource.getEditableValue} is * implemented to return the value, itself. */ public Object getEditableValue(Object object) { return value; } /** * Returns a name for a value's single property. Subclasses may use this in creating a property * descriptor, and user subclasses may override it to provide a specific name. */ protected String getPropertyName() { return KommaEditPlugin.INSTANCE.getString("_UI_ValueProperty_name"); } /** * Returns a description for a value's single property. Subclasses may use this in creating a * property descriptor, and user subclasses may override it to provide a specific name. */ protected String getPropertyDescription() { return KommaEditPlugin.INSTANCE.getString("_UI_ValueProperty_description"); } /** * Returns whether a value's single property is settable. By default, this returns whether the * structural feature is {@link org.eclipse.emf.ecore.EStructuralFeature#isChangeable changeable}. * Subclasses may use this in creating a property descriptor, and user subclasses may override it * to restrict or allow setting of the property. */ protected boolean isPropertySettable() { return true; // return property.isChangeable(); } /** * Returns whether value's single property consists of multi-line text. By default, false is * returned. Subclasses may use this in creating a property descriptor, and user subclasses may * override it to enable multi-line text editing. */ protected boolean isPropertyMultiLine() { return false; } /** * Returns whether value's single property should sort its choices for selection. By default, * false is returned. Subclasses may use this in creating a property descriptor, and user * subclasses may override it to enable sorting. */ protected boolean isPropertySortChoices() { return false; } /** * Returns an image for a value's single property. By default, a standard property icon is * selected based on the type of the structural feature. Subclasses may use this in creating a * property descriptor, and user subclasses may override it to select a different icon. */ protected Object getPropertyImage() { IProperty property = (IProperty) ((IResource) getOwner()).getEntityManager().find(this.property); Set<String> ranges = new HashSet<String>(); for (net.enilink.vocab.rdfs.Class rangeClass : property.getRdfsRanges()) { ranges.add(rangeClass.getURI().toString()); } return getPropertyImage(ranges); } /** * Returns the property image for the specified type. Implementations of {@link * #getPropertyImage() getPropertyImage} typically call this method. */ protected Object getPropertyImage(Collection<String> ranges) { if (ranges.contains(XMLSCHEMA.TYPE_BOOLEAN.toString())) { return ItemPropertyDescriptor.BOOLEAN_VALUE_IMAGE; } else if (ranges.contains(XMLSCHEMA.TYPE_BYTE.toString()) || ranges.contains(XMLSCHEMA.TYPE_INTEGER.toString()) || ranges.contains(XMLSCHEMA.TYPE_LONG.toString()) || ranges.contains(XMLSCHEMA.TYPE_SHORT.toString())) { return ItemPropertyDescriptor.INTEGRAL_VALUE_IMAGE; } else if (ranges.contains(XMLSCHEMA.TYPE_STRING.toString())) { return ItemPropertyDescriptor.TEXT_VALUE_IMAGE; } else if (ranges.contains(XMLSCHEMA.TYPE_DOUBLE.toString()) || ranges.contains(XMLSCHEMA.TYPE_FLOAT.toString())) { return ItemPropertyDescriptor.REAL_VALUE_IMAGE; } return ItemPropertyDescriptor.GENERIC_VALUE_IMAGE; } /** * Returns a category for a value's single property. By default, null is returned. Subclasses may * use this in creating a property descriptor, and user subclasses may override it to actually * provide a category. */ protected String getPropertyCategory() { return null; } /** * Returns filter flags for a value's single property. By default, null is returned. Subclasses * may use this in creating a property descriptor, and user subclasses may override it to actually * provide filter flags. */ protected String[] getPropertyFilterFlags() { return null; } /** * {@link IEditingDomainItemProvider#getNewChildDescriptors * IEditingDomainItemProvider.getNewChildDescriptors} is implemented to return an empty list. * Subclasses may override it to return something else. */ public void getNewChildDescriptors( Object object, IEditingDomain editingDomain, Object sibling, ICollector<Object> descriptors) {} /** * {IEditingDomainItemProvider#createCommand IEditingDomainItemProvider.createCommand} is * implemented via {@link #baseCreateCommand baseCreateCommand} to create set, copy, and * drag-and-drop commands, only. */ public ICommand createCommand( Object object, IEditingDomain domain, Class<? extends ICommand> commandClass, CommandParameter commandParameter) { return baseCreateCommand(object, domain, commandClass, commandParameter); } /** * Implements creation of a set, copy, or drag-and-drop command by calling out to {@link * #createSetCommand createSetCommand}, {@link #createCopyCommand createCopyCommand}, or {@link * #createDragAndDropCommand createDragAndDropCommand}. */ public ICommand baseCreateCommand( Object object, IEditingDomain domain, Class<? extends ICommand> commandClass, CommandParameter commandParameter) { if (commandClass == SetCommand.class) { return createSetCommand( domain, commandParameter.getOwner(), commandParameter.getProperty(), commandParameter.getValue(), commandParameter.getIndex()); } else if (commandClass == CopyCommand.class) { return createCopyCommand( domain, commandParameter.getOwner(), (CopyCommand.Helper) commandParameter.getValue()); } else if (commandClass == DragAndDropCommand.class) { DragAndDropCommand.Detail detail = (DragAndDropCommand.Detail) commandParameter.getProperty(); return createDragAndDropCommand( domain, commandParameter.getOwner(), detail.location, detail.operations, detail.operation, commandParameter.getCollection()); } else { return UnexecutableCommand.INSTANCE; } } /** * Return an {@link net.enilink.komma.common.command.UnexecutableCommand}. Subclasses should * override this to map this into a real set on a model object. */ protected ICommand createSetCommand( IEditingDomain domain, Object owner, Object feature, Object value, int index) { return UnexecutableCommand.INSTANCE; } /** * Returns an {@link net.enilink.komma.common.command.UnexecutableCommand}. An ordinary {@link * net.enilink.komma.edit.command.CopyCommand} is only useful for copying model objects, so it * would be inappropriate here. Subclasses should override it to return something more useful, * like a concrete subclass of a {@link SimpleCopyCommand} or {@link WrappingCopyCommand}. */ protected ICommand createCopyCommand( IEditingDomain domain, Object owner, CopyCommand.Helper helper) { return UnexecutableCommand.INSTANCE; } /** Creates a {@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); } /** * A label for copy command inner classes, the same one used by {@link * net.enilink.komma.edit.command.CopyCommand}. */ protected static final String COPY_COMMAND_LABEL = KommaEditPlugin.INSTANCE.getString("_UI_CopyCommand_label"); /** * A description for copy command inner classes, the same as in {@link * net.enilink.komma.edit.command.CopyCommand}. */ protected static final String COPY_COMMAND_DESCRIPTION = KommaEditPlugin.INSTANCE.getString("_UI_CopyCommand_description"); /** * A command base class for copying a simple value and the wrapper. This is useful when the value * isn't able provide an adapter to return a copy command, itself. This class just provides the * scaffolding; concrete subclasses must implement {@link #copy copy} to do the copying. */ protected abstract class SimpleCopyCommand extends AbstractOverrideableCommand { protected Collection<?> affectedObjects; /** Creates an instance for the given domain. */ public SimpleCopyCommand(IEditingDomain domain) { super(domain, COPY_COMMAND_LABEL, COPY_COMMAND_DESCRIPTION); } /** Returns true; this command can requires now preparation and can always be executed. */ @Override protected boolean prepare() { return true; } /** * Calls {@link #copy} to do the copying, {@link IDisposable#dispose disposes} the copy, and * sets it to be the result of the command. Since the copy has not been created within the * viewed model, it should never do any kind of notification, which is why it is immediately * disposed. */ @Override protected CommandResult doExecuteWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { IWrapperItemProvider copy = copy(); copy.dispose(); return CommandResult.newOKCommandResult(Collections.singletonList(copy)); } /** Concrete subclasses must implement this to copy and return the value and wrapper. */ public abstract IWrapperItemProvider copy(); /** Returns a list containing only the original wrapper itself. */ @Override public Collection<?> doGetAffectedObjects() { if (affectedObjects == null) { affectedObjects = Collections.singletonList(WrapperItemProvider.this); } return affectedObjects; } } /** * A command base class for copying the wrapper for a value that is partly copied by another * command. This is useful when the value includes a model object that is able provide an adapter * to return a copy command, but also includes an element that is not adaptable, such as a feature * map entry. This command copies the non-adapter element and the wrapper, which ensures the copy * can be copied again. */ protected abstract class WrappingCopyCommand extends CommandWrapper { protected Collection<?> affectedObjects; /** Creates an instance where some adaptable value is copied by the given command. */ public WrappingCopyCommand(ICommand command) { super(command); } /** * Executes the adaptable-value-copying command, then calls {@link #copy copy} to copy the rest * of the value and the wrapper, {@link IDisposable#dispose disposes} the copy, and sets it to * be the result of the command. Since the copy has not been created within the viewed model, it * should never do any kind of notification, which is why it is immediately disposed. */ @Override protected CommandResult doExecuteWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { super.doExecuteWithResult(progressMonitor, info); IWrapperItemProvider copy = copy(); copy.dispose(); return CommandResult.newOKCommandResult(Collections.singletonList(copy)); } /** * Concrete subclasses must implement this to copy and return the value and wrapper. The result * of the adaptable-value-copying command is available from <code>getCommand().getResult() * </code>. */ public abstract IWrapperItemProvider copy(); /** Returns a list containing only the original wrapper itself. */ @Override public Collection<?> getAffectedObjects() { if (affectedObjects == null) { affectedObjects = Collections.singletonList(WrapperItemProvider.this); } return affectedObjects; } } /** * Returns the {@link #adapterFactory}, if non-composeable, otherwise, returns its root adapter * factory. */ protected IAdapterFactory getRootAdapterFactory() { return adapterFactory instanceof IComposeableAdapterFactory ? ((IComposeableAdapterFactory) adapterFactory).getRootAdapterFactory() : adapterFactory; } /** * An item property descriptor for the single property of a wrapper for a simple value. This * extends the base implementation and substitutes the wrapper's owner for the selected object * (the wrapper itself) in the call to {@link #getPropertyValue getPropertyValue}. Thus, the owner * must be an EObject to use this class. The property's name, description, settable flag, static * image, category, and filter flags are obtained by calling out to various template methods, so * can be easily changed by deriving a subclass. */ protected class WrapperItemPropertyDescriptor extends ItemPropertyDescriptor { public WrapperItemPropertyDescriptor(IResourceLocator resourceLocator, IReference property) { super( WrapperItemProvider.this.adapterFactory, resourceLocator, getPropertyName(), getPropertyDescription(), property, isPropertySettable(), isPropertyMultiLine(), isPropertySortChoices(), getPropertyImage(), getPropertyCategory(), getPropertyFilterFlags()); } /** * Substitutes the wrapper owner for the selected object and invokes the base implementation. * The actual value returned depends on the implementation of {@link #getValue getValue}. */ @Override public Object getPropertyValue(Object object) { return super.getPropertyValue(owner); } /** * Substitutes the wrapper owner for the selected object and invokes the base implementation. */ @Override public boolean canSetProperty(Object object) { return super.canSetProperty(owner); } /** * Returns <code>true</code>, as the property of a value wrapper is always considered to be set. */ @Override public boolean isPropertySet(Object object) { return true; } /** Does nothing, as resetting the property of a value wrapper is not meaningful. */ @Override public void resetPropertyValue(Object object) { // Do nothing } /** * Sets the property value. If an editing domain can be obtained, the command returned by {@link * #createSetCommand createSetcommand} is executed; otherwise, {@link #setValue setValue} is * called to set the value. */ @Override public void setPropertyValue(Object object, Object value) { IEditingDomain editingDomain = getEditingDomain(owner); if (editingDomain == null) { setValue((IResource) object, property, value); } else { try { editingDomain .getCommandStack() .execute( createSetCommand(editingDomain, (IResource) object, property, value), null, null); } catch (ExecutionException e) { Log.error(KommaEditPlugin.getPlugin(), 0, "Error while setting property value", e); } } } /** * Returns a value from a model object. If the feature is multi-valued, only the single value * that the wrapper represents is returned. */ @Override protected Object getValue(IResource object, IReference property) { // When the value is changed, the property sheet page doesn't update // the property sheet viewer input // before refreshing, and this gets called on the obsolete wrapper. // So, we need to read directly from the // model object. // // return value; Object result = object.get(property); if (object.getApplicableCardinality(property).getSecond() != 1) { // If the last object was deleted and the selection was in the // property sheet view, the obsolete wrapper will // reference past the end of the list. // List<?> list = (List<?>) result; result = index >= 0 && index < list.size() ? list.get(index) : value; } return result; } /** * Sets a value on a model object. If the feature is multi-valued, only the single value that * the wrapper represents is set. */ protected void setValue(IResource object, IReference property, Object value) { if (object.getApplicableCardinality(property).getSecond() != 1) { @SuppressWarnings("unchecked") List<Object> list = ((List<Object>) object.get(property)); list.set(index, value); } else { object.set(property, value); } } /** * Returns a command that will set the value on the model object. The wrapper is used as the * owner of the command, unless overridden, so that it can specialize the command that * eventually gets created. */ protected ICommand createSetCommand( IEditingDomain domain, Object owner, Object feature, Object value) { return SetCommand.create(domain, getCommandOwner(WrapperItemProvider.this), null, value); } /** * Returns <code>false</code>, as the property only represents a single value, even if the * feature is multi-valued. */ @Override public boolean isMany(Object object) { return false; } /** * Substitutes the wrapper owner for the selected object and invokes the base implementation. */ @Override public Collection<?> getChoiceOfValues(Object object) { return super.getChoiceOfValues(owner); } } /** * A <code>ReplacementAffectedObjectCommand</code> wraps another command to return as its affected * objects the single wrapper that replaces this wrapper. That is, it obtains the children of the * wrapper's owner, and returns a collection containing the first wrapper whose feature and index * match this one's. */ protected class ReplacementAffectedObjectCommand extends CommandWrapper { public ReplacementAffectedObjectCommand(ICommand command) { super(command); } /** * Obtains the children of the wrapper's owner, and returns a collection containing the first * wrapper whose feature and index match this one's. */ @Override public Collection<?> getAffectedObjects() { Collection<?> children = Collections.EMPTY_LIST; // Either the IEditingDomainItemProvider or ITreeItemContentProvider // item provider interface can give us // the children. // Object adapter = adapterFactory.adapt(owner, IEditingDomainItemProvider.class); if (adapter instanceof IEditingDomainItemProvider) { children = ((IEditingDomainItemProvider) adapter).getChildren(owner); } else { adapter = adapterFactory.adapt(owner, ITreeItemContentProvider.class); if (adapter instanceof ITreeItemContentProvider) { children = ((ITreeItemContentProvider) adapter).getChildren(owner); } } for (Object child : children) { if (child instanceof IWrapperItemProvider) { IWrapperItemProvider wrapper = (IWrapperItemProvider) child; if (wrapper.getProperty() == property && wrapper.getIndex() == index) { return Collections.singletonList(child); } } } return Collections.EMPTY_LIST; } } @Override public boolean isInferred() { return false; } }
/** * Returns a name for a value's single property. Subclasses may use this in creating a property * descriptor, and user subclasses may override it to provide a specific name. */ protected String getPropertyName() { return KommaEditPlugin.INSTANCE.getString("_UI_ValueProperty_name"); }