DevDocProperties - JUCMNAV/projetseg-update GitHub Wiki
Properties
Overview
- Each editpart in our editor can have its properties modified in the properties view.
- The mechanism for properties is very elegant, but our code is a bit overwhelming at first.
- It would be an interesting project to change our property source for a more generic use that would allow us to modify simple text files to change the appearance of the properties view.
- Our current solution uses EMF reflection to build drop down lists.
General mechanism
- The framework asks the edit part, using getAdapter(), for an instance of a class implementing IPropertySource
- Basically, the property source is built using the model object. This model object is later obtainable by using getEditableValue() in the property source.
- The framework queries the property source for a list of all lines to put in the property view. These are called property descriptors. (getPropertyDescriptors()).
- A property descriptor is basically a mapping between a certain model property or reference, a textual name to be used in the property view and a visual control to display this value and edit it. For example, strings are associated with text fields while colors are associated with color pickers (we later learned that the color picker changes depending on the platform) getPropertyDescriptors() basically returns a list of such descriptors.
- Descriptors are associated with categories. The framework groups the property descriptors by categories and sorts them by name.
- If a property descriptor is another IPropertySource, the framework will build nested properties.
- When building our property descriptor, we give it an Object ID. This ID can be anything we want (we use EMF structural attributs as unique IDs for our property descriptors), but we learned that if you don't want your properties to be rebuilt for no reason, make sure these IDs are comparable with equals() and hashCode(). Don't use Object arrays, as the equals()/hashCode() won't return true for different arrays, even though all the indices might be equal.
- getPropertyValue(Object) must return the value of the property for which the ID is passed as a parameter. This will feed the control with its value (String into a textbox, for example).
- setPropertyValue(Object, Object) sets the property value. The first parameter identifies the property and the second parameter is obtained from the control (String entered by the user in a textbox).
- We're using an undoable property sheet. The framework manages undo/redo mechanisms for us. We're not sure what would happen if we executed commands in setPropertyValue() instead of simply modifying the EMF object directly.
jUCMNav property class hierarchy
* EObjectPropertySource is the superclass for all our property sources. * It is a general purpose property source for EMF objects. Using EMF reflection, it gets all of an object's properties and tries to build descriptors for them. * It can build descriptors for basic types like booleans, strings, integers, colors. * Basically, it has no knowledge of UCM elements. * UCMElementPropertySource is the property source for most of our model elements such as pathnodes * It contains the property source for EMF enumerations (question: why didn't we put this in its superclass; oh well) * Contains the property descriptor for ucm elements that are children to componentrefs. * Contains nested properties for performance attributes such as workload * Contains nested properties for scenario traversal such as conditions * ComponentPropertySource and ResponsibilityPropertySource add the definition's properties to the references. badly named :) * LabelPropertySource builds properties for labels by building another property source for the labeled element. * This class is a good example of extensions to UCMElementPropertySource. It redefines the three important methods; addSpecificProperties() is a method that is called by getPropertyDescriptors() in the parent class to add any properties that the subclass wants to add to the regular properties inferred by EMF reflection. References add their definition's properties here. * PropertyID is used to identify each property descriptor.
Use of reflection
- We use reflection to build dropdowns automatically for enumerations generated by EMF.
- See UCMElementPropertySource
public void addPropertyToDescriptor(Collection descriptors, EStructuralFeature attr, EClass c) {
// Get type for the structural feature
EClassifier type = getFeatureType(attr);
PropertyID propertyid = new PropertyID(c, attr);
...
} else if (type.getInstanceClass().getSuperclass() == AbstractEnumerator.class) {
// these are enums created by EMF
enumerationDescriptor(descriptors, propertyid);
}
...
}
private void enumerationDescriptor(Collection descriptors, PropertyID propertyid) {
EClassifier type = getFeatureType(propertyid.getFeature());
Class enumer = type.getInstanceClass();
String[] values = getEnumerationValues(enumer);
String name = enumer.getName().substring(enumer.getName().lastIndexOf('.') + 1);
descriptors.add(new ComboBoxPropertyDescriptor(propertyid, name, values));
}
protected Object returnPropertyValue(EStructuralFeature feature, Object result) {
...
} else if (result instanceof AbstractEnumerator) {
// if this is an EMF enumeration
int i = getEnumerationIndex((AbstractEnumerator) result);
result = new Integer(i);
}
}
public void setPropertyValue(Object id, Object value) {
PropertyID propertyid = (PropertyID) id;
EStructuralFeature feature = propertyid.getFeature();
Object result = getPropertyValue(id);
...
} else if (getFeatureType(feature).getInstanceClass().getSuperclass() == AbstractEnumerator.class) {
// if this is an EMF enumeration
Class enumer = getFeatureType(feature).getInstanceClass();
try {
int selectedIndex = ((Integer) value).intValue();
String selectedString = getEnumerationValues(enumer)[selectedIndex];
result = enumer.getMethod("get", new Class[] { String.class }).invoke(getEditableValue(), new Object[] { selectedString }); //$NON-NLS-1$
} catch (Exception e) {
e.printStackTrace();
}
setReferencedObject(propertyid, feature, result);
}
...
}
Proposed work
- Extract our descriptors from our current classes. Make them standalone. Encapsulate their get/set values.
- Find a nice & clean way to define mappings between:
- EMF structural attribute
- property name
- property descriptor used
- property category
- whether should build nested property or merge the properties (perf attributes versus definitions for references)
- Could be XML file based or maybe we could have a specific metamodel package that takes care of these relationships
- Goal: Given an EMF metamodel, generate the properties automatically, using a set of defined property descriptors.
- EMF can generate the property view automatically, but we haven't looked at what it does.
- Something clean&generic might be distributable on its own.
-- Main.JasonKealey - 07 Jul 2005