/** * <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: ItemPropertyDescriptor.java,v 1.32 2008/08/13 15:11:42 emerks Exp $ */ package net.enilink.komma.edit.provider; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import org.eclipse.core.commands.ExecutionException; import net.enilink.vocab.owl.DatatypeProperty; import net.enilink.komma.common.adapter.IAdapterFactory; import net.enilink.komma.common.command.ExtendedCompositeCommand; import net.enilink.komma.common.command.ICommand; import net.enilink.komma.common.util.IResourceLocator; import net.enilink.komma.common.util.Log; import net.enilink.komma.edit.KommaEditPlugin; import net.enilink.komma.edit.command.SetCommand; import net.enilink.komma.edit.domain.AdapterFactoryEditingDomain; import net.enilink.komma.edit.domain.IEditingDomain; import net.enilink.komma.edit.domain.IEditingDomainProvider; import net.enilink.komma.edit.provider.IItemPropertyDescriptor.OverrideableCommandOwner; import net.enilink.komma.em.concepts.IProperty; import net.enilink.komma.em.concepts.IResource; import net.enilink.komma.em.util.KommaUtil; import net.enilink.komma.model.IModel; import net.enilink.komma.model.IObject; import net.enilink.komma.model.ModelUtil; import net.enilink.komma.core.IEntity; import net.enilink.komma.core.IReference; /** * This implementation of an item property descriptor supports delegating of the * {@link IItemPropertySource} interface to the {@link IItemPropertyDescriptor} * interface. */ public class ItemPropertyDescriptor implements IItemPropertyDescriptor, OverrideableCommandOwner { /** * Returns the feature's default {@link #getId(Object) identifier}. * * @param eStructuralFeature * the feature to lookup. * @return the feature's default identifier. */ public static String getDefaultId(IReference property) { return property.getURI().toString(); } public static final Object BOOLEAN_VALUE_IMAGE = KommaEditPlugin.INSTANCE .getImage("full/obj16/BooleanValue"); public static final Object GENERIC_VALUE_IMAGE = KommaEditPlugin.INSTANCE .getImage("full/obj16/GenericValue"); public static final Object INTEGRAL_VALUE_IMAGE = KommaEditPlugin.INSTANCE .getImage("full/obj16/IntegralValue"); public static final Object REAL_VALUE_IMAGE = KommaEditPlugin.INSTANCE .getImage("full/obj16/RealValue"); public static final Object TEXT_VALUE_IMAGE = KommaEditPlugin.INSTANCE .getImage("full/obj16/TextValue"); /** * For now we need to keep track of the adapter factory, because we need it * to provide a correct label provider. */ protected IAdapterFactory adapterFactory; /** * This is used to locate resources for translated values like enumeration * literals. */ protected IResourceLocator resourceLocator; /** * This is a convenient wrapper of the {@link #adapterFactory}. */ protected AdapterFactoryItemDelegator itemDelegator; /** * This is returned by {@link #canSetProperty}. */ protected boolean isSettable; /** * This is the name that is displayed in the property sheet. */ protected String displayName; /** * This is the description shown in the property sheet when this property is * selected. */ protected String description; /** * This is the structural feature that provides the values for this * property. This is mutually exclusive with {@link #parentReferences}. */ protected IReference property; /** * This is the set of single-valued references that act as a parent, only * one can have a non null value at a time. This is mutually exclusive with * {@link #property}. */ protected IReference[] parentReferences; /** * Whether the value of this property consists of multi-line text. */ protected boolean multiLine; /** * Whether the choices for this property should be sorted for display. */ protected boolean sortChoices; /** * This represents the group of properties into which this one should be * placed. */ protected String category; /** * These are the flags used as filters in the property sheet. */ protected String[] filterFlags; /** * This is the label provider used to render property values. */ // protected Object labelProvider; /** * This is the image that will be used with the value no matter what type of * object it is. */ protected Object staticImage; /** * If non-null, this object will be the owner of commands created to set the * property's value. */ protected Object commandOwner; /** * This class uses a static image */ protected class ItemDelegator extends AdapterFactoryItemDelegator { protected IResourceLocator resourceLocator; public ItemDelegator(IAdapterFactory adapterFactory) { super(adapterFactory); } public ItemDelegator(IAdapterFactory adapterFactory, IResourceLocator resourceLocator) { super(adapterFactory); this.resourceLocator = resourceLocator; } @Override public String getText(Object object) { if (property instanceof DatatypeProperty) { if (isMany(object)) { if (object instanceof Collection<?>) { StringBuffer result = new StringBuffer(); for (Iterator<?> i = ((Collection<?>) object) .iterator(); i.hasNext();) { Object value = i.next(); result.append(ModelUtil.getLabel(value)); if (i.hasNext()) { result.append(", "); } } return result.toString(); } } else { return ModelUtil.getLabel(object); } } return super.getText(object); } // This is copied from ItemProviderAdapterFactory. // protected 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; } @Override public Object getImage(Object object) { return staticImage == null ? super.getImage(object) : staticImage; } } /** * This creates an instance that uses a resource locator and determines the * cell editor from the type of the structural feature. */ public ItemPropertyDescriptor(IAdapterFactory adapterFactory, IResourceLocator resourceLocator, String displayName, String description, IReference property, boolean isSettable) { this(adapterFactory, resourceLocator, displayName, description, property, isSettable, false, false, null, null, null); } /** * This creates an instance that uses a resource locator, specifies a static * image, and determines the cell editor from the type of the structural * feature. */ public ItemPropertyDescriptor(IAdapterFactory adapterFactory, IResourceLocator resourceLocator, String displayName, String description, IReference property, boolean isSettable, Object staticImage) { this(adapterFactory, resourceLocator, displayName, description, property, isSettable, false, false, staticImage, null, null); } /** * This creates an instance that uses a resource locator, specifies a * category and filter flags, and determines the cell editor from the type * of the structural feature. */ public ItemPropertyDescriptor(IAdapterFactory adapterFactory, IResourceLocator resourceLocator, String displayName, String description, IReference property, boolean isSettable, String category, String[] filterFlags) { this(adapterFactory, resourceLocator, displayName, description, property, isSettable, false, false, null, category, filterFlags); } /** * This creates an instance that uses a resource locator; specifies a static * image, a category, and filter flags; and determines the cell editor from * the type of the structural feature. */ public ItemPropertyDescriptor(IAdapterFactory adapterFactory, IResourceLocator resourceLocator, String displayName, String description, IReference property, boolean isSettable, Object staticImage, String category, String[] filterFlags) { this(adapterFactory, resourceLocator, displayName, description, property, isSettable, false, false, staticImage, category, filterFlags); } /** * This 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. */ public ItemPropertyDescriptor(IAdapterFactory adapterFactory, IResourceLocator resourceLocator, String displayName, String description, IReference property, boolean isSettable, boolean multiLine, boolean sortChoices, Object staticImage, String category, String[] filterFlags) { this.adapterFactory = adapterFactory; this.resourceLocator = resourceLocator; this.itemDelegator = new ItemDelegator(adapterFactory, resourceLocator); this.displayName = displayName; this.description = description; this.property = property; this.isSettable = isSettable; this.multiLine = multiLine; this.sortChoices = sortChoices; this.staticImage = staticImage; this.category = category; this.filterFlags = filterFlags; } /** * This creates an instance that uses a resource locator and determines the * cell editor from the parent references. */ public ItemPropertyDescriptor(IAdapterFactory adapterFactory, IResourceLocator resourceLocator, String displayName, String description, IReference[] parentReferences, boolean isSettable) { this(adapterFactory, resourceLocator, displayName, description, parentReferences, isSettable, null, null); } /** * This creates an instance that uses a resource locator, specifies a * category and filter flags, and determines the cell editor from the parent * references. */ public ItemPropertyDescriptor(IAdapterFactory adapterFactory, IResourceLocator resourceLocator, String displayName, String description, IReference[] parentReferences, boolean isSettable, String category, String[] filterFlags) { this.adapterFactory = adapterFactory; this.resourceLocator = resourceLocator; this.itemDelegator = new ItemDelegator(adapterFactory, resourceLocator); this.displayName = displayName; this.description = description; this.parentReferences = parentReferences; this.isSettable = isSettable; this.category = category; this.filterFlags = filterFlags; } /** * This returns the group of properties into which this one should be * placed. */ public String getCategory(Object object) { return category; } /** * This returns the description to be displayed in the property sheet when * this property is selected. */ public String getDescription(Object object) { return description; } /** * This returns the name of the property to be displayed in the property * sheet. */ public String getDisplayName(Object object) { return displayName; } /** * This returns the flags used as filters in the property sheet. */ public String[] getFilterFlags(Object object) { return filterFlags; } /** * This returns the {@link #getDefaultId(EStructuralFeature) default * identifier} of the {@link #property feature} if it's present, or * dash-separated concatenation of the default identifier of each * {@link #parentReferences parent reference}. This key that must uniquely * identify this descriptor among the other descriptors from the same * {@link IItemPropertySource#getPropertyDescriptor(Object, Object) property * source}. */ public String getId(Object object) { if (property != null) { return getDefaultId(property); } else if (parentReferences != null && parentReferences.length != 0) { StringBuffer result = new StringBuffer( getDefaultId(parentReferences[0])); for (int i = 1; i < parentReferences.length; ++i) { result.append('-'); result.append(getDefaultId(parentReferences[i])); } return result.toString(); } else { return displayName; } } public Object getHelpContextIds(Object object) { return null; } /** * This will be called to populate a list of choices. The label provider * will be used to determine the labels for the objects this returns. This * default implementation uses {@link #getReachableObjectsOfType * getReachableObjectsOfType}. */ protected Collection<?> getComboBoxObjects(Object object) { if (object instanceof IObject) { IModel model = ((IObject) object).getModel(); if (parentReferences != null) { Collection<Object> result = new LinkedHashSet<Object>(); for (int i = 0; i < parentReferences.length; ++i) { parentReferences[i] = (IProperty) model .resolve(parentReferences[i]); result.addAll(KommaUtil.getInstances( model.getManager(), ((IProperty) parentReferences[i]).getNamedRanges( (IObject) object, true).toSet())); } return result; } else if (property != null) { Collection<IResource> instances = KommaUtil.getInstances( model.getManager(), ((IProperty) property).getNamedRanges( (IResource) object, true).toSet()); if (((IObject) object).getApplicableCardinality(property) .getSecond() == 1 && !instances.contains(null)) { instances.add(null); } return instances; } } return null; } /** * This returns the label provider that will be used to render the value of * this property. The implementation here just creates an * {@link AdapterFactoryItemDelegator}. */ public IItemLabelProvider getLabelProvider(Object object) { return itemDelegator; } // /** // * This indicates whether these two property descriptors are equal. It's // not // * really clear to me how this is meant to be used, but it's a little bit // * like an equals test. // */ // public boolean isCompatibleWith(Object object, Object anotherObject, // IItemPropertyDescriptor anotherItemPropertyDescriptor) { // /* // * if (propertyDescriptor == this) { return true; } else if // * (propertyDescriptor instanceof ItemPropertyDescriptor) { // * ItemPropertyDescriptor itemPropertyDescriptor = // * (ItemPropertyDescriptor)propertyDescriptor; if (adapterFactory == // * itemPropertyDescriptor.adapterFactory && // * displayName.equals(itemPropertyDescriptor.displayName) && (category // * == null && itemPropertyDescriptor.category == null || // * category.equals(itemPropertyDescriptor.category))) { return true; } } // */ // // return false; // } static public class PropertyValueWrapper implements IItemLabelProvider, IItemPropertySource { protected Object object; protected Object propertyValue; protected Object nestedPropertySource; protected AdapterFactoryItemDelegator itemDelegator; public PropertyValueWrapper(IAdapterFactory adapterFactory, Object object, Object propertyValue, Object nestedPropertySource) { this.object = object; this.propertyValue = propertyValue; this.nestedPropertySource = nestedPropertySource; this.itemDelegator = new AdapterFactoryItemDelegator(adapterFactory); } public String getText(Object thisObject) { return itemDelegator.getText(propertyValue); } public Object getImage(Object thisObject) { return itemDelegator.getImage(propertyValue); } public List<IItemPropertyDescriptor> getPropertyDescriptors( Object thisObject) { if (nestedPropertySource != null) { List<IItemPropertyDescriptor> list = itemDelegator .getPropertyDescriptors(nestedPropertySource); if (list != null) { List<IItemPropertyDescriptor> result = new ArrayList<IItemPropertyDescriptor>( list.size()); for (IItemPropertyDescriptor itemPropertyDescriptor : list) { result.add(createPropertyDescriptorDecorator( nestedPropertySource, itemPropertyDescriptor)); } return result; } } return Collections.emptyList(); } public IItemPropertyDescriptor getPropertyDescriptor(Object thisObject, Object propertyId) { return createPropertyDescriptorDecorator(nestedPropertySource, itemDelegator.getPropertyDescriptor(nestedPropertySource, propertyId)); } public Object getEditableValue(Object thisObject) { return propertyValue; } protected IItemPropertyDescriptor createPropertyDescriptorDecorator( Object object, IItemPropertyDescriptor itemPropertyDescriptor) { return new ItemPropertyDescriptorDecorator(object, itemPropertyDescriptor); } } protected Object createPropertyValueWrapper(Object object, Object propertyValue) { return new PropertyValueWrapper(adapterFactory, object, propertyValue, null); } /** * This is called by {@link #getPropertyValue getPropertyValue} to * reflectively obtain the value of a feature from an object. It can be * overridden by a subclass to provide additional processing of the value. */ protected Object getValue(IResource object, IReference property) { try { return object.get(property); } catch (Throwable exception) { return null; } } /** * This does the delegated job of getting the property value from the given * object; and it sets object, which is necessary if * {@link #getComboBoxObjects getComboBoxObjects} is called. It is * implemented in a generic way using the structural feature or parent * references. */ public Object getPropertyValue(Object object) { if (property instanceof DatatypeProperty) { Object result = getValue((IResource) object, (DatatypeProperty) property); if (result == null) { return null; } else { return createPropertyValueWrapper(object, result); } } else if (parentReferences != null) { for (int i = 0; i < parentReferences.length; ++i) { Object result = getValue((IResource) object, parentReferences[i]); if (result != null) { return createPropertyValueWrapper(object, result); } } return ""; } else { return createPropertyValueWrapper(object, getValue((IResource) object, property)); } } /** * This does the delegated job of determine whether the property value from * the given object is set. It is implemented in a generic way using the * structural feature. */ public boolean isPropertySet(Object object) { // System.out.println("isPropertySet " + object); if (parentReferences != null) { for (int i = 0; i < parentReferences.length; ++i) { Object value = ((IResource) object).get(parentReferences[i]); if (value != null && !(value instanceof Collection<?> && ((Collection<?>) value) .isEmpty())) { return true; } } return false; } else { Object value = ((IResource) object).get(property); return value != null && !(value instanceof Collection<?> && ((Collection<?>) value) .isEmpty()); } } /** * This determines whether this descriptor's property for the object * supports set (and reset). */ public boolean canSetProperty(Object object) { if (isSettable) { IEditingDomain editingDomain = getEditingDomain(object); if (editingDomain != null) { return !editingDomain.isReadOnly((IEntity) object); } else { return true; } } else { return false; } } /** * Sets the object to use as the owner of commands created to set the * property's value. */ public void setCommandOwner(Object commandOwner) { this.commandOwner = commandOwner; } /** * Returns the override command owner set via {@link #setCommandOwner * setCommandOwner}. */ public Object getCommandOwner() { return commandOwner; } /** * Returns either the override command owner set via * {@link #setCommandOwner setCommandOwner} or, if that is null, the * fall-back object provided. */ protected Object getCommandOwner(Object fallback) { return commandOwner != null ? commandOwner : fallback; } /** * This does the delegated job of resetting property value back to it's * default value. */ public void resetPropertyValue(Object object) { IObject iObject = (IObject) object; IEditingDomain editingDomain = getEditingDomain(object); try { if (parentReferences != null) { for (int i = 0; i < parentReferences.length; ++i) { final IReference parentReference = parentReferences[i]; if (iObject.isPropertySet(parentReference, true)) { if (editingDomain == null) { iObject.set(parentReferences[i], null); } else { editingDomain.getCommandStack() .execute( SetCommand.create(editingDomain, getCommandOwner(iObject), parentReference, SetCommand.UNSET_VALUE), null, null); } break; } } } else { if (editingDomain == null) { iObject.set(property, null); } else { editingDomain.getCommandStack().execute( SetCommand.create(editingDomain, getCommandOwner(iObject), property, SetCommand.UNSET_VALUE), null, null); } } } catch (ExecutionException e) { Log.error(KommaEditPlugin.getPlugin(), 0, "Error while resetting property value", e); } } public IEditingDomain getEditingDomain(Object object) { IEditingDomain result = AdapterFactoryEditingDomain .getEditingDomainFor(object); if (result == null) { if (adapterFactory instanceof IEditingDomainProvider) { result = ((IEditingDomainProvider) adapterFactory) .getEditingDomain(); } if (result == null && adapterFactory instanceof IComposeableAdapterFactory) { IAdapterFactory rootAdapterFactory = ((IComposeableAdapterFactory) adapterFactory) .getRootAdapterFactory(); if (rootAdapterFactory instanceof IEditingDomainProvider) { result = ((IEditingDomainProvider) rootAdapterFactory) .getEditingDomain(); } } } return result; } /** * This does the delegated job of setting the property to the given value. * It is implemented in a generic way using the structural feature. */ public void setPropertyValue(Object object, Object value) { IResource resource = (IResource) object; IEditingDomain editingDomain = getEditingDomain(object); try { if (parentReferences != null) { ICommand removeCommand = null; for (int i = 0; i < parentReferences.length; ++i) { Object oldValue = resource.get(parentReferences[i]); if (oldValue != null) { final IProperty parentReference = (IProperty) resource .getEntityManager().find(parentReferences[i]); if (oldValue == value) { return; } else if (parentReference.isRangeCompatible(value)) { if (editingDomain == null) { resource.set(parentReference, value); } else { editingDomain.getCommandStack().execute( SetCommand.create(editingDomain, getCommandOwner(resource), parentReference, value), null, null); } return; } else { if (editingDomain == null) { resource.set(parentReference, null); } else { removeCommand = SetCommand.create( editingDomain, getCommandOwner(resource), parentReference, null); } break; } } } for (int i = 0; i < parentReferences.length; ++i) { final IProperty parentReference = (IProperty) resource .getEntityManager().find(parentReferences[i]); if (parentReference.isRangeCompatible(value)) { if (editingDomain == null) { resource.set(parentReferences[i], value); } else { if (removeCommand != null) { final ExtendedCompositeCommand compoundCommand = new ExtendedCompositeCommand( ExtendedCompositeCommand.LAST_COMMAND_ALL); compoundCommand.add(removeCommand); compoundCommand.add(SetCommand.create( editingDomain, getCommandOwner(resource), parentReference, value)); editingDomain.getCommandStack().execute( compoundCommand, null, null); } else { editingDomain.getCommandStack().execute( SetCommand.create(editingDomain, getCommandOwner(resource), parentReference, value), null, null); } } break; } } } else { if (editingDomain == null) { resource.set(property, value); } else { editingDomain.getCommandStack() .execute( SetCommand.create(editingDomain, getCommandOwner(resource), property, value), null, null); } } } catch (ExecutionException e) { Log.error(KommaEditPlugin.getPlugin(), 0, "Error while setting property value", e); } } public Object getProperty(Object object) { if (property != null) { return property; } else if (parentReferences != null) { return parentReferences; } else { return null; } } /** * Returns whether this property represents multiple values. This is true * only if we're using a {@link #property structural feature} to provide the * values for this property, and if that feature is multi-valued. */ public boolean isMany(Object object) { return parentReferences == null && property != null && object instanceof IResource && ((IResource) object).getApplicableCardinality(property) .getSecond() > 1; } public Collection<?> getChoiceOfValues(Object object) { return getComboBoxObjects(object); } public boolean isMultiLine(Object object) { return multiLine; } public boolean isSortChoices(Object object) { return sortChoices; } }