1 Properties

1.4 Exporting Properties from a Class

As indicated above, a class can export properties by implementing the interface HasProperties, along with the supporting interfaces Property, PropertyInfo, and PropertyInfoList. The class developer can do this in any way desired, but support is provided to make this fairly easy.

The standard approach is to create a static instance of PropertyList for the exporting class, and then populate it with PropertyInfo structures for the various exported properties. This PropertyList (which implements PropertyInfoList) can then be used to implement the getProperty() and getAllPropertyInfo() methods required by HasProperties:

   protected static PropertyList myProps;
   ... initialize myProps in a static code block ...
   // returns an information list for all properties
   public PropertyInfoList getAllPropertyInfo () {
      return myProps;
   }
   // returns a handle for a specific property
   public Property getProperty (String name) {
      getAllPropertyInfo().getProperty (name, this);
   }

Information about specific properties should be added to PropertyList within a static code block (second line in the above fragment). This can be done directly using the method

   void add (PropertyInfo info)

but this requires creating and initializing a PropertyInfo object. An easier way is to use a different version of the add method, which creates the required PropertyInfo structure based on information supplied through its arguments. In the example below, we have a class called ThisHost which exports three properties called visible, lineWidth, and color:

   // default values for the properties
   protected static int defaultLineWidth = 1;
   protected static boolean defaultVisibleP = true;
   protected static Color defaultColor =
      new Color (0.5f, 0.5f, 0.5f);
   // fields containing the property values
   protected int myLineWidth = defaultLineWidth;
   protected boolean myVisibleP = defaultVisibleP;
   protected Color myColor = defaultColor;
   // create a PropertyList ...
   protected static PropertyList myProps =
      new PropertyList(ThisHost.class);
   // ... and add information for each property:
   static {
      myProps.add (
         "visible isVisible *", "object is visible",
          defaultVisibleP);
      myProps.add (
         "lineWidth", "line width (pixels)",
         defaultLineWidth);
      myProps.add (
         "color", "color ", defaultColor);
   }
   public PropertyInfoList getAllPropertyInfo () {
      return myProps;
   }
   public Property getProperty (String name) {
      getAllPropertyInfo().get (name, this);
   }

The values for the three properties are stored in the fields myLineWidth, myVisibleP, and myColor. Default values for these are defined by static fields.

A static instance of a PropertyList is created, using a constructor which takes the exporting class as an argument (in Java 1.5, the class object for a class can be referenced as ClassName.class). Information for each property is then added within a static block, using the convenience method

   void add (String nameAndMethods, String description,
             Object defaultValue)

The first argument, nameAndMethods, is a string which gives the name of the property, optionally followed by whitespace-separated names of the accessor methods for the property’s value:

   "<propertyName> [<getMethodName>] [<setMethodName>] [<getRangeMethodName>]"

These accessor methods should have the signatures

   Object getMethod();
   void setMethod (Object value);
   Range getRangeMethod ();

If any of the methods are not specified, or are specified by a ’*’ character, then the system with look for accessor methods with the names getXxx, setXxx, and getXxxRange, where xxx is the name of the property. If no getRangeMethod is defined (and no numeric range is specfied in the options argument string, as described below), then the property will be assumed to have no range limitations and its getRange() method will return null.

The second argument, description, gives a textual description of the property, and is used for generating help messages or tool-tip text.

The third argument, defaultValue, is a default property value, which is used for automatic initialization and for deciding whether the property’s value needs to be written explicitly to persistent storage.

An extended version of the add method takes an additional argument options:

   void add (String nameAndMethods, String description,
             Object defaultValue, String options)

The options argument is a sequence of option tokens specifing various property attributes, each of which can be queried using an associated PropertyInfo method. Token are separated by white space and may appear in any order. Some have have both long and abbreviated forms. They include:

NW, NoAutoWrite

Disables this property from being automatically written using the PropertyList methods write and writeNonDefaults (Section 1.5). Causes the PropertyInfo method getAutoWrite() to return false.

AW, AutoWrite (Default setting)

Enables this property to be automatically written using the PropertyList methods write and writeNonDefaults (Section 1.5). Causes the PropertyInfo method getAutoWrite() to return true.

NE, NeverEdit

Disables this property from being interactively edited. Causes the PropertyInfo method getEditing() to return Edit.Never.

AE, AlwaysEdit (Default setting)

Enables this property to be interactively edited. Causes the PropertyInfo method getEditing() to return Edit.Always.

1E, SingleEdit

Enables this property to be interactively edited for one property host at a time. Causes the PropertyInfo method getEditing() to return Edit.Single.

XE, ExpandedEdit

Indicates, where appropriate, that the widget for editing this property can be expanded or contracted to conserve GUI space, and that it is initially expanded. Causes the PropertyInfo method getWidgetExpandState() to return ExpandState.Expanded. This is generally relevant only for properties such as CompositeProperties (Section 1.4.2) whose editing widgets have several sub-widgets.

CE, ContractedEdit

Indicates, where appropriate, that the widget for editing this property can be expanded or contracted to conserve GUI space, and that it is initially contracted. Causes the PropertyInfo method getWidgetExpandState() to return ExpandState.Contracted. This is generally relevant only for properties such as CompositeProperties (Section 1.4.2) whose editing widgets have several sub-widgets.

NS, NoSlider

Indicates that a slider should not be allowed in the widget for editing this property. Causes the PropertyInfo method isSliderAllowed() to return false. In order for the editing widget to contain a slider, the property must also have both a numeric value and a defined range.

DX, DimensionX

Sets the numeric dimension of this property to X. The dimension can be queried using the PropertyInfo method getDimension(). For properties which are non-numeric or do not have a fixed dimension, the dimension will be returned as -1. Note the for some numeric properties, the dimension can be determined automatically and there is no need to explicitly specify this attribute.

SH, Sharable

Indicates that the property value is not copied internally by the host and can therefore be shared among several hosts. This may improve memory efficiency but means that changes to the value itself may be reflected among several hosts. This attribute can be queried by the PropertyInfo method isSharable().

NV, NullOK

Indicates that the property value may be null. By default, this is false, unless the default value has been specified as null. Whether or not a property may be set to null is particularly relevant in the case of CompositeProperties (Section 1.4.2), where one may choose between setting individual sub-properties or setting the entrie structure to null altogether.

%fmt

A printf-style format string, beginning with %, used to format numeric information for this property’s value, either in a GUI or when writing to persistent storage. A good general purpose format string to use is often "%.6g", which specifies a free format with six significant characters.

[l,u]

A numeric range interval with a lower bound of l and an upper bound of u. If specified, this defines the value returned by PropertyInfo.getDefaultNumericRange(); otherwise, that method returns null. If a getRangeMethod is not defined for the property, and the property has a numeric type, then the default numeric range is returned by the property’s Property.getRange() method. The default numeric range is also used to determine bounds on slider widgets for manipulating the property’s value, in case the upper or lower limits returned by the Property.getRange() method are unbounded. The symbol inf can be used in an interval range, so that [0,inf] represents the set of non-negative numbers.

The following code fragment shows an example of using the option argument:

   myProps.add (
      "radius", "radius of the sphere (mm)", defaultRadius,
      "%8.3f [0,100] NE");
   );

The property named radius is given a numeric format string of "%8.3f", a numeric range in the interval [0,100], and set so that it will not be displayed in an automatically created GUI panel.

1.4.1 Read-only properties

A property can be read-only, which means that it can be read but not set. In particular, the set() for a read-only Property handle is inoperative.

Read-only properties can be specified using the following PropertyList methods:

   void addReadOnly (String nameAndMethod, String description);
   void addReadOnly (String nameAndMethod, String description,
       String options);

These are identical to the add methods described above, except that the nameAndMethod argument includes at most a get accessor, and there is no argument for specifying a default value.

The method getAutoWrite() also returns false for read-only properties (since it does not make sense to store them in persistent storage).

1.4.2 Inheriting Properties from a superclass

By default, a subclass of a HasProperties class inherits all the properties exported by the class exports all the properties exported by it’s immediate superclass.

Alternatively, a subclass can create its own properties by creating it’s own PropertyList, as in the code example of Section 1.3:

   // create a PropertyList ...
   protected static PropertyList myProps =
      new PropertyList(ThisHost.class);
   public PropertyInfoList getAllPropertyInfo () {
      return myProps;
   }

and none of the properties from the superclass will be exported. Note that it is necessary to redefine getAllPropertyInfo() so that the instance of myProps specific to ThisHost will be returned.

If one wishes to also export properties from the superclass (or some other ancestor class), then a PropertyList can be created which also contains property information from the desired ancestor class. This involves using a different constructor, which takes a second argument specifying the ancestor class from which to copy properties:

   protected static PropertyList myProps =
      new PropertyList(ThisHost.class, Ancestor.class);
   public PropertyInfoList getAllPropertyInfo () {
      return myProps;
   }

All properties exported by Ancestor will now also be exported by ThisHost.

What if we want only some properties from an ancestor class? In that case, we can edit the PropertyList to remove properties we don’t want. We can also replace properties with new ones with the same name but possibly different attributes. The latter may be necessary if the class type of a property’s value changes in the sub-class:

   static
   {
       // remove the property "color"
       myProps.remove ("color");
       // replace the property called "mesh" with one which
       // uses a different kind of mesh object:
       myProps.remove ("mesh");
       myProps.add ("mesh", "quad mesh", null);
   }