Using IMP to Develop Preference Services and Pages

ALERT: Parts of this document are out of date. 

This document provides guidance on the use of  IMP to develop a preference service and preference pages for -based IDEs.

Contents

The IMP Preferences Model
The IMP Prefrences Service
    Operations on the Service
    Comparison to the Eclipse Preferences Service
    Comparison to the JFace Preference Store
    Using the IMP Preferences Service
    Implementing the IMP Preferences Service
IMP Preference Pages
    Using a IMP preference page
    Implementing a IMP preference page
    Alternative page designs
IMP Preference Fields
    Field types
    Adding preference fields to a preferences tab
    Level-specific considerations
    Details links and dialogs
    Field toggles
    Layout and spacing
    Special values
Implemening Other Things for Preference Pages
    Special elements of the project tab
    Preferences initializers and constants
Buttons and their Behavior
Implementing IMP Preference Field Editors
    General responsibilities
    Interactions of concerns
    Other considerations
IMP Preferences Utilities  
The IMP 'New Preference Page' Wizard
Examples

The IMP Preferences Model

IMP support for the development of preference pages is based on the Eclipse Preferences Service (org.eclipse.core.internal.preferences.PreferencesService, since 3.0).  Thus, the IMP model of preferences is basically that of the Preferences Service.  Key aspects of the IMP model (virtually all of which come from the Eclipse model) are as follows:

  1. Default
  2. Configuration (i.e., workspace configuration)
  3. Instance (i.e., workspace instance)
  4. Project
Preferences set at a given level are scoped accordingly, with default preferences applying anywhere that a more specific preference value is lacking.









   

The IMP Preferences Service

The IMP Preferences Service implements the IMP Preferences Model.  The service is defined by an interface IPreferencesService (org.eclipse.imp.preferences.IPreferencesService), which is implemented by the class PreferencesService (org.eclipse.imp.preferences.PreferencesService).

Operations on the Service

The IMP Preferences Service provides operations to do the following:


The IMP Preferences Service is implemented using the Eclipse Preferences Service and relies on the Eclipse Preferences Service to manage the storage of files that contain the preference values for each level of the preferences model.  The persistent storage of these data is not a concern that appears in the IMP Preferences Service API.  Note that all instances of the IMP Preferences Service use the same rules for locating the files that persistently store preference values.  Thus all instances of the Service can be considered to share a common persistent store.

Comparison to the Eclipse Preferences Service

The IMP Preferences Service adopts many of the basic concepts the Eclipse Preferences Service (see The IMP Preferences Model) but is more restricted in certain respects and more flexible in others.  Some of the major differences:



   




To reiterate, some of the general similarities between the two models (which reflect points the IMP model has adopted from the Eclipse model) include the use of multiple levels or scopes of preferences, the four particular levels or scopes of preferences, the use of Eclipse preferences nodes, inheritance of values across scopes or levels, the use of namespaces (whether constrained or unconstrained), the basic types of preferences, and management of preference storage by the service,

Comparison to the JFace Preference Store

Prior to the introduction of the Eclipse Preferences Service (and perhaps remaining so in practice today) the principal mechanism for storing preferences was the JFace Preference Store (org.eclipse.jface.preference.PreferenceStore, which implements a persistent version of org.eclipse.jface.preference.IPreferenceStore).

There are some significant differences between the Preference Store approach and the general Preferences Service approach:




Using the IMP Preferences Service

An instance of the IMP Preferences Service is made available through the language plugin for each IMP-supported language.  This Service instance is set by default for the particular language represented by the plugin.  The preference values are initialized automatically.  (Values for the default preferences level are set programmatically; values for other preferences levels are available from the files used to store them persistently.)  The Service instance is not automatically specialized for any particular project.

How a client of a Service instance should accesses the instance will depend on its purpose and on assumptions about other possible clients of the instance that may have compatible or conflicting purposes and assumptions.

Consider the simple case of a single client that will work with a single language in a single project and that is interested just in reading applicable preference values.  Then the default language and project for the Service instance can be set accordingly and the language and project can be assumed in future calls on the service.  Similarly, the client can ignore the preference level in making calls on the service as the service will automatically return the preference value that is set at the lowest (most specific) level.

If the client has a need to work with multiple languages or multiple projects, it can change the default settings in the Service instance and continue to make service requests that do not provide these as parameters.  Alternatively, the client can make service requests in which the project, or language and project, are specified with each request.

If the client of a Service instance will be operating in an environment in which there are other clients of the same Service instance that are concerned with the same language and project, then it should be safe for those clients to rely on the default settings for language and project in the Service instance.

However, ff the client of a Service-instance will be operating in an environment in which there may be other clients of the Service instance that are concerned with differing languages or projects, then the default settings for language and project in the Service instance should not be relied on.  In such cases, to be safe, the clients should make the language and project explicit in their service requests.

Typical application clients of a IMP Preferences Service instance will be concerned with reading preference values but not writing them, and these clients will typically not be concerned with the level at which the preference is set.  On the other hand, other clients of a Service instance, notably those concerned with managing preferences, will both read and write preference values, and these clients will be concerned with the level on which a value is set.

As with clients that just read preference values, clients that both read and write preference values may operate in environments where there may be one or more clients in operation concurrently and where the language or project of concern may be fixed or varied within or across clients.  Depending on the conditions that may apply, the clients may rely on the default values that are set for language and project in the Service instance or the clients may specify the language and project in individual service requests.  This can be done for the setting of preference values just as it is done for the getting of preference values.

The setting of preference values necessarily occurs on a particular level, so methods that set preference values all take a parameter that specifies the level.  (There is not notion of a "default level" in the IMP Preferences Service.)  For clients, such as preference managers, that are concerned with preferences on specific levels, there are methods to get a preference value on a given level as well.  (Ordinarily, these will not be used by typical application clients that are simply reading preference values.)

Implementing the IMP Preferences Service

Users of IMP do not need to do anything to implement the IMP Preferences Service for their language or IDE.  The Service is implemented in IMP framework classes, and instances of the Service are made available automatically as part of the language plugins that are constucted by IMP when the New Language Wizard is run.

IMP Preference Pages

IMP initially supports a single design for preference pages, which is a preference page with four tabs, one for each of the four preference levels.  The page and the four tabs (with certain annotations and controls) are generated automatically by IMP.

The tabs are intended to display preference fields and related controls (see IMP Preference Fields).  The specific fields on a page must be added by the developer of the page.  The fields are not restricted with respect ot their number, type, identification, layout and other aspects of display; these are up to the page developer.  However, normally the same fields will be presented in the same way on each tab.  That is, each tab is intended to be a view of the same set of language-related preferences, but with values appropriate to the level represented by the tab.

The project-level tab also allows a project to be selected and displays the currently selected project.  IMP generates the implementation for this automatically.

Because IMP uses tabs at the top level within a page to represent preferences levels, tabs can't be used at the top level of one of these pages for some other purpose (such as grouping preferences relating to different concerns).  IMP doesn't restrict the use of tabs within tabs for that purpose, but such an approach might be awkward.  Given that IMP has more or less preempted the use of tabs to display preferences levels, other mechanisms should probably be used to organize alternative groups of preferences, such as different pages (at the top level or nested levels) or (possibly) links to additional dialogs or pages.

IMP preference pages, like typical preference pages, can display error messages.  However, IMP preference pages, unlike most preference pages, have multiple tabs from which errors may arise.  Each tab, in turn, may have multiple fields from which errors arise.  To accommodate these multiple sources of errors, each tab maintains a list of error messages that arise from errors in its fields.  The page displays an error message from the current tab, if the tab has an error, or the page title, if the current tab does not have an error.  (The alternate display of page titles on correct pages and error messages on erroneous pages is a typical behavior for Eclipse preference pages.)   Any tab that has errors will have its name marked to make apparent that the tab is erroneous even if it (and its error messages) are not displayed on the page.  The current approach is to bracket the name of the tab with an "error mark."  By default the error mark is is "**"; thus the project level tab is currently labeled as "Project" when its fields are correct and as "**Project**" when it has an erroneous field  This handling of error mesages is supported in IMP framework classes for preference pages, tabs, and fields.  (The error mark or the approach to error marking of tabs can be readily changed.)

IMP preference pages are also intended to have a consistent appearance and behavior that must be supported in part by the preference fields.   These include a consistent approach to indicating field values that are inherited (currenly shown with a blue background color), a consistent approach to indicating that a field has been modified (currently shown by a '>' at the start of the label text), and a consistent response to various controls (e.g., remoal of the current value, or the setting of a special value).  These aspects of appearance and behavior are supported by the set of IMP framework classes for preference field types.

The IMP preference pages, tabs, and dialogs also have a standard set of buttons (which may be adapted).  Buttons and their Behavior are discussed below.

Using an IMP preference page

IMP preference pages are used largely like any other preference page by typing in text boxes, checking checkboxes, pushing radio buttons, and so on.  IMP preference pages also have some features that typical preference pages do not.

One prominent feature is the tabs that represent preferences on different levels of the preferences moel.  Preferences are always viewed and operated on with respect to some level of the preferences model.

On the project-level tab, there is also a field for selecting the project that is used to determine a relevant set of project-level preferences.  Preferences that are displayed on the project-level tab, when a project is selected, are the effective or in-force preferences relative to that project (inheriting values from higher levels for any preferences that are not set on the project level).

Finally, each preference field may have an associated "details" link that will lead to a dialog that may offer buttons for setting (or unsetting) the value of a field in particular way (see Details links and dialogs)

Implementing a IMP preference page

A working implementation for a preference page with buttons and tabs is generated automatically by the New Preference Page wizard.  The principal work in implementing a page is in implementing the fields (and related elements) in the tabs on the page. 

This consists mainly of constructing and laying out an appropriate set of preference fields, along with the associated details links (if used as recommended) and arranging for "toggle" relationships among fields if necessary (as when one field is enabled of disabled by the checking of another).  This is described in more detail under IMP Preference Fields.

Typically a corresponding set of fields and links will be used on the tabs for all levels, although parameterized somewhat differently depending on the level.  Typically the same layout will be used on the tabs for each level.  Some additional work is needed for the project-level tab, which must be made responsive to the selection of projects.  Approaches to laying out the fields can be seen in the Examples section and in the preference pages for JikesPG or x10.

All tabs must also provide implementations of the methods that respond to button pushes--these simply need to take the appropriate action on the fields actually used on the page.  This is described in more detail under Details links and dialogs.

Alternative page designs

The initial design of IMP-based preference pages has certain characterisitcs:
This approach makes a full set of preference-related information and controls available, with some separation of features to simplify particular views or tasks.

Depending on the goals for the pages, other page designs may be appropriate.  Some alternative page designs:

  • A more comprehensive page (or tabs) for preferences managers, combining the information and controls that are now divided between the tabs and the details dialogs.  This would amount to a comprehensive "control panel" for preference managers.

We have not (yet) experimented with alternative page designs such as these, but many should be possible with substantial reuse of the IMP framework classes for preferences.  Please feel free to contact the IMP team with questions or suggestions on this topic (as with any others).

IMP Preference Fields

Preference values are represented on preference pages by preference fields.  There are different types of preference field corresponding to different types of preference values and different ways of representing the preferences on preference pages.

Field types

Preference fields are supported by a hierarchy of IMP framework classes (all found in org.eclipse.imp.preferences.fields).  The root of the hierarchy is the abstract type IMPFieldEditor (which extends org.eclipse.jface.preference.FieldEditor).  The hierarchy includes several abstract and concrete types that generally correspond to subtypes of FieldEditor.  The hierarchy of provided types is as follows:

IMPFieldEditor (abstract)
IMPBooleanFieldEditor
IMPComboFieldEditor
IMPRadioGroupFieldEditor
IMPStringFieldEditor
IMPIntegerFieldEditor
IMPStringButtonFieldEditor (abstract)
IMPDirectoryListFieldEditor
IMPFileFieldEditor

In implementation these combine elements of the corresponding FieldEditor subtypes (mainly for management of GUI controls) with aspects relating to the use of the IMP Preferences Service (rather than the Eclipse Preference Store), the inheritance of preference values, and various controls and attributes used in IMP preference pages.
   
The set of field types supported by IMP is intended to be representative and usefully broad but not exhaustive.  It may grow in the future if there is sufficient demand for additional types.  Users are welcome to develop and contribute their own field types.

Adding preference fields to a preferences tab

The IMP Preferences Tabs are designed so that the preference fields for each tab are created in a method dedicated to that purpose:
        protected IMPFieldEditor[] createFields(Composite composite) {...}
This method is automatically passed the Composite that represents the tab to which the fields are added and it returns an array of the field editors that are created to represent the fields on the tab.

This method has three parts:  1) Declare the field editors, 2) create the field editors and related controls, and 3) put them into an array and return it.  Of course, the only nontrivial step here is the second.

IMP provides utility methods (currently in org.eclipse.imp.preferences.IMPPreferencesUtilities) that allow a preference field of a particular type to be created with a single call.  The specification of the method to create a StringFieldEditor (which is representative) is as follows:

        public IMPStringFieldEditor makeNewStringField(
                PreferencePage page,
                IMPPreferencesTab tab,
                IIMPPreferencesService service,
                String level, String key, String text,
                Composite parent,
                boolean isEnabled, boolean isEditable,
                boolean hasSpecialValue, String specialValue,
                boolean emptyValueAllowed, String emptyValue,
                boolean isRemovable) { ... }

The "page", "tab", and "service" parameters should be self-explanatory; values for these should all be avaliable in contexts where this method would be used.  The "level"parameter is the string name of the preferences level on which the field occurs (which should be consistent with the level represented by the tab).

The "key" parameter is the key by which the preference value will be stored in the Preferences Service; the "text" parameter is the name of the preference as it will appear on the label to the field.

The "parent" parameter is a graphical element that is created within the tab folder to hold the fields and other elements that are placed on the tab.  This object is created automatically by IMP and passed automatically to the createFields(..) method and so should be avaliable in the context in which fields are created.  The implementor of fields on the tab must pass it along but can otherwise ignore it.

The remaining parameters relate to attributes of the field, some of which are inherent to the FieldEditor type, some of which are defined and used as part of the IMP preference pages.

The "isEnabled" parameter determines whether the field is enabled--something that applies to all FieldEditor instances.  To be enabled means, of course, that it can be used (e.g., clicked on or typed into), whereas to be disabled means that it cannot be used.  Typically disabled GUI controls are shown as "grayed out" in some way.  That convention is followed in IMP.

(Note to field implementors:   1) The "graying out" of disabled fields does not happen automatically, it is controlled independently of the enabled state and must be explicitly coordinated with it.  2)  All field editors actuall contain multiple GUI controls--at least a label and an element to represent the field value, if not others--and each of these controls within the field can be managed independently.  Thus, when a field as a whole is enabled or disabled--or any other property of the field as a whole is set--the effect must be propagated to the various controls within the field editor.  We have done this in a particular way for the field editor types we have implemented for IMP, but it could reasonable be done in other ways.)

The "isEditable" parameter is similar to the "isEnabled" parameter in that it governs the usability of the field.  In the implementation, "isEditable" actually applies to the Text wiget used to hold the text in the GUI representation of the field (and so this parameter doesn't apply to field editors that do not have a text box).  Generally, isEditable and isEnabled should be set in parallel (otherwise you can have a disabled field that you can still edit or an enabled filed that you can't edit).

The "hasSpecialValue" parameter is a flag that indicates whether the field has an associated "special value".  The use of a special value for a field is optional, and whether a special value is used, and what it may signify, are dependent on field-specific semantcs.  Typical uses would be to provide a field-specific default value, to provide an alternative representation of some value that cannot be represented directly, or to provide a preferred value that is something other than the default value.  These cases are discussed in the section Special Values.  The parameter "specialValue" is used to represent the special value if the field has one.  (Thus, if "hasSpecialValue" is true, "specialValue" should generally not be null; also, if "hasSpecialValue" is false, the value of "specialValue" is ignored.)

The "emptyValueAllowed" parameter is used to indicate whether the field can take on an empty value.  This attribute is adopted from the StringFieldEditor type and in IMP is generalized to other types of fields as well.  In effect the "empty" value is a special value for which a dedicated attribute is provided.  For Strings the empty value is naturally the empty string.  For other field types that make use of Strings, an empty String might also be used for the empty value.  In any case, the parameter "emptyValue" is provided to enable a type-specific or field-specific empty value to be specified.  Empty values may not be supported for some field types (in which case the "emptyValueAllowed" and "emptyValue" parameters will not be available in the utility routines or constructor methods for creating instances of those types.)

(Note to field implementors:  In general, whether a field may have an empty value depends on three things:  1)  whether it makes sense abstractly for the type and field; 2) whether there is some practical way to show the empty value in the GUI; and 3)  whether there is some possible way to store the value in the preferences service.)

The final parameter is "isRemovable", which is a flag that governs whether a value stored for the field (that is, stored on the preferences level where the field is used) can be removed.  In general, the values of fields below the default level can be removed whereas the values of fields on the default level cannot be.  That is because the removal of a value on one level causes the value from the next higher level to be inherited.  However, "isRemovable" can be set to false for some field below the default level if it is intended that the field should always have a value that is set at that level and not inherited.  (This is more or less the effect that you get when you enable project-specific preferences in the Eclipse JDT.)

Level-specific considerations

On the details level, as noted, preference values should generally not be removable, as the details level is there to assure that preferences are defined even when values are removed from other levels.  Preferences may be disabled or not (and uneditable or not) according to whether it should be possible to change the default value during a single execution of the IDE.  Preference values on the default level are statically initialized when the preference page is created, and values in those fields are not stored by the Preferences Service.  Still, it is possible (if a field is enabled and--if applicable--editable) to change the default-level values that are stored in the runtime preferences model and displayed on the default-level tab.  Values changed on the default-level tab may remain in effect so long as the IDE continues to execute.  Whether default-level preferences should be changeable is up to the designers of a particular preferences page.

On the (workspace) configuration and (workspace) instance levels, there are no level-specific issues.  These are the intermediate levels of the preferences model with no special restrictions or issues.

On the project level, the fields should take on values--even inherited values--only when a project is defined.  Mechanisms built into the project tab generally take care of this once a field is defined.  Note, though, that no project is defined automatically by default; in other words, when a page is first created, the project is undefined.  Thus, when fields are created on the project level, they should be created with "isEnabled" set to false (and similarly for "isEditable", if present). 

Details links and dialogs

The IMP versions of field editors have more behaviors and attributes than do their JFace counterparts.  Some of this is exposed in the approach we have taken to preferences tabs, for example, showing inherited fields with a distinctive background color and marking the lables of fields that have been modified.  However, there is additional information about IMP ields that is not shown on the preferences tab because we thought the tab would be too confusing for some purposes if all the information is shown.  For that reason, we have associated a "details" link with each field:  navigating the details link for a field brings up a dialog with more complete information about the field and with additional controls for operating on the field.

The information that is shown in a typical details dialog for a field includes:

The controls that may be available include buttons to   
These may be variously present or enabled depending on the type of the field and the state of its attributes.  (See Buttons and their Behavior for more information on these.)

The IMP framework provides utility methods to create details dialogs and links for various types of fields (like the methods to create fields, these are currently in org.eclipse.imp.preferences.IMPPreferencesUtilities).  Some of these, like the one for creating a link to a StringFieldEditor, work for the specific type and subtypes:

public Link createDetailsLink(
Composite detailsHolder,
final IMPStringFieldEditor field,
final Composite fieldHolder,
String text)
{ ... }

The "parent" parameter is the same Container that was used as the "parent" in the corresponding field.

The field for which the dialog is to provide details is provided as the source of those details and target of operations supported by the dialog.  An set of appropriate controls will be created and enabled automatically according to the type of field and state of its attributes    .

The "fieldHolder" parameter is the value returned by the "getParent()" method on the principal control in the field (for example "myStringField.getTextControl().getParent()" or "myBooleanField.getChangeControl().getParent()").  (In our implementations, there is usually a level of container between the container represented by the "parent" parameter and the field itself, as this is sometimes helpful in managing the GUI layout.  In any case, you can always obtain the needed element from the field, more or less as shown.)

The "text" parameter gives the label that is to be used on the created link; we have conventionally used "Details ..."

Field toggles

It is not infrequently the case that one field is enabled according to how another is set.  The typical case is when a boolean field controls the enabled state of some other field.  In the JikesPG preferences, for example, there is a boolean checkbox which can be checked (or not) to indicate whether the default generator executable file should be used (or not).  There is an accompanying String field in which the path to an alternative generator executable can be specified.  This String field should be enabled just when the checkbox is unchecked.  We refer to such a relationship between fields as a "field toggle", although the kind of toggle just described is actually a relatively simple case of a more general set of field-dependence relationships.

IMP provides support for setting up a field toggle between a boolean field and a String field, that is, the method IMPPreferencesUtilities.createFieldToggleListener(..).  This method makes the enabled state of a String field depenent on the value of a boolean field, either consistent with the boolean field or complementary to it.  We have generally created field-toggle listeners in the "createFields(..)" method along with the fields and details links.

If experience indicates a substantial need for other sorts of dependency relationships among fields, the IMP framework can be extended to support these as well.

Layout and spacing

For the preferences pages that we have implemented we have used a two-column format, with fields on the left and the associated details link on the right.  The dialog to which the link leads is created as a consequence of creating the link.  If you create a details link immediately after creating the field to which it applies then these will appear as successive elements in the preferences tab.  (Alternatively, you can create them non-successively and explicitly arrange them however you want to.)

In the two column format we occasionally want to "skip a line" between fields or to fill out the end of a row where we've included notes in just the first column.  That involves using invisible elements as placeholders to fill out cells that will appear as empty space on the tab.  The IMPPreferencesUtilities class (cited previously) contains a routine to help with this:

public static void fillGridPlace(Composite composite, int num) {...}

Here the "composite" parameter is the same "parent" composite that contains the fields and details links; "num" represents the number of cells to be skipped.  This routine is not specific to a two-column format; for example, you can call this with "num" set to two and skip one row in two-column format or two rows in one-column format, and so on.  The element used as the placeholder is a label with its visibility set to false; labels aren't the tallest of GUI elements, so to skip an extensive vertical space it may be necessary to insert multiple rows of them.

Special values

As mentioned above, a preference field may have an associated "special value" that may play various roles depending on the semantics of the field.  Some examples of possible uses for this field are as follows:
  1. To provide a field-specific default value:  In preference pages that are based on the Eclipse Preference Store, every field has a prevailing value and a default value; the default value is stored along with the prevailing value for the field and is always there as a "falllback" in the event that the prevailing value must be discarded.  With the IMP Preferences Service (following from the Eclipse Preferences Service), there is no default value stored along with the prevailing value for a field; rather, the default value is more properly conceived of as the value that is inherited form the next higher preferences level, the highest of which is designated the "default" level (which gets its default values from the initialization code).  As a result, on levels below the default level, there is no field-specific "fallback" value that can always be used to replace a prevailing value.  The "special value" can play this role.
  2. To provide an alternative representation of some value that cannot be represented directly:  Some fields may not allow the representation of values that are problematic in some way, such as empty strings or undefined values that fall outside of the range of legal representations.  In such cases the special value may be used to give a representable form to a value that is otherwise not reprsentable, such as "empty" for the empty string (where those are not allowed) or "undefined" for values that are not set yet.  (Note:  Of course the type of the special value must be consistent with the type of the field.  Many fields, even those not ostensibly of String types, nevertheless use strings to represent their values, so Stings can be used to define special values for most field types.)
  3. To provide a preferred value that is something other than the default value:  A field may have a legitimate default value that is generally suitable in the abstract, but it may also have non-default values that would be preferred for some applications or in some contexts.  For example, consider a combo box (pulldown list of chioces) where the choices are various countries.  The default value for the field may be the empty (or blank) string--but nobody lives or works in the empty or blank country.  Thus, depending on where the application is expected to be used, the name of a specific conutry may be preferred to the default value.  The special value can be used to represent a preferred value of this sort.
Other uses for the special value may emerge.

Given that there are several roles that a special value may play, and given that these roles are generally not exclusive, it would seem that the special value may be overloaded and underpowered.  If sufficient need emerges, we can provide additional field attributes that are dedicated to more specific roles.

Implementing Other Things for Preference Pages

There are two other areas in which implementation is required to get a fully functioning IMP preference page.  These are special elments of the project tab and preferences initializers and constants.  (Users are also free to implement their own field-editor types, but that won't be ncessary where the provided field-editor types can be used.)

Special elements of the project tab

The preferences tab for the project level has fields and (nominally) details links just as do the tabs for the other preferences levels.  However, the project tab has a number of additional elements that are related to the dynamic selection of projects for which preferences are to be displayed.

All of the tabs have a "createFields(..)" method that must be implemented with calls to create the fields (and associated details links).  The project tab also has a method  "addressProjectSelection(..) that takes a project selection event and the composite parent that holds the fields on the tab.  The project selection event contains the old and new project-preference nodes from the preferences model.  In principle, either of these may be null if they represent a state in which no project is set.

The main implementation tasks needed to address project selection are as follows.  In the case that the new preferences node is not null:
  1. Each field needs to be set.  IMPPreferencesUtilities has "setField" methods for various types of fields that will set the fields with stored or inherited values, as appropriate, and set associated attributes accordingly.
  2. The enabled state of each field needs to be set.  Generally, each field will be enabled, unless the enabled state of one field depends on the value or enabled state of another field, in which case the enabled state of the dependent field will have to be set conditionally.
  3. A preference-change listener needs to be added to enable each field to listen for changes in the preferences model.  There is a method defined for this purpose:  ProjectPreferencesTab.addProjectPreferenceChangeListeners(..).
In the case that the new preferences node is null, the fields need to be disabled.  (This can't be done generically because it depends on the type of control(s) in the field and on the methods used to disable them.)

Instructions about these implementation steps and some supporting steps are provided in the generated project-tab class.  Several additional actions that must be taken in response to the selection (or deselection) of a project in the tab are addressed automatically.

Preferences initializers and constants

IMP generates an empty "Preferences Initializer" class for each generated preference page.  This class is an extension of
org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer and it requires an implementation of the method initializeDefaultPreferences().  The generated implementation of that method is empty, since it depends on the particular fields to be initialized.  Implementing initializeDefaultPreferences() simply requires setting the initial value of each preference at the default level in the preferences store; for example:

service.setBooleanPreference(
        IIMPPreferencesService.DEFAULT_LEVEL,
        PreferenceConstants.P_EMIT_MESSAGES,
        getDefaultEmitMessages());

In this case the default-level value for the preference named by PreferenceConstants.P_EMIT_MESSAGES is set to the value returned by getDefaultEmitMessages().

In the IMP Preferences Service (as in the Eclipse Preferences Service) the Preferences Initializer is not just the initializer of default values but the immediate definer of those values.  That is because the initial default values are never stored by the Service but only obtained from the Initialzer, because these are the values that are restored when default values are restored on the default preferences level, and because these are the only default values that persist between invocations of the IDE.  Of course, the values provided by the Initializer may come from any source, but the Initializer can be used to encode the default values of preferences (as we have done for JikesPG and X10).

IMP similarly generates an empty "Preference Constants" class for each generated preference page.  This class is simply intended for the defintiion of String constant names for preferences that can be reused in the Preferences Initializer and elsewhere.  It might be used for the definition of other constants as well.

Buttons and their Behavior

Each IMP preferences page should have two buttons:  "Cancel" and "OK."

The Cancel button should close the preferences page without saving the state of the page, thereby discarding any unsaved ("non-applied") changes.  The Cancel button should be enabled at all times.

The OK button should close the preferences page after first saving the state of the page (that is, writing the field values into the service).  The OK button should be enabled whehever there are no errors on the page (that is, no errors on any tab on the page).

Each IMP preferences tab should have two additional buttons:  "Restore Defaults" and  "Apply."

The Restore Defaults button should restore the default value of each field on the tab.  Note that, unlike the preference-store approach, in which (in usual practice) each field has its own local default value, in the preferences-service approach there is no locally stored default value for each field.  Rather, in the service approach, "default" represents the most global level of preferences.  In order to support the Restore Defaults action, some notion of the default value for levels other than the default level needs to be defined.  IMP takes the approach that the default value for a lower level is the value that is in force at the next higher level.  So, for example, the default values at the project level are the values in force on the (workspace) instance level.  This rule holds for the project, instance, and configuration levels.  At the default level there is no higher level, but the default level has initial values that are set programmatically and that can be taken as default values (or "default default" values).  So, the behavior of the Restore Defaults button on any level that is not the default level is to remove any preferences that are stored at that level (allowing  values to be inherited from the next higher level), and the behavior on the default level is to reinitialize the preferences model with the programmed values.  These operations should be applied to each field of the tab, even if it may already display the appropriate default value; as a side effect in the fields, each field in the tab should end up marked as modified.

The Apply button should store any locally set field values on the tab into Preferences Service; values that are inherited should not be stored.  (The store method for each field type should actually only store values that are set in the field and not inherited; thus, the implementation of the action for the Apply button can simply "store" each field on the tab and safely assume that inherited values will be ignored.)  As a side effect in the fields, any modified marks on the labels of field should be removed.  As another side effect in the fields, some fields that were shown as inherited may end up shown as locally set, if the Apply entails the storing of a locally set value that replaces a previously inherited value.  (The change in field appearance from "inherited" to "locally set" occurs only when the field is finally stored, not when it is first updated.)

As mentioned above, additional buttons occur on the dialogs reached through the "details" links that may be associated with a field.  These include some subset of "Copy In", "Set Special", "Set Empty", "Remove" and "OK."

The Copy In button has the effect of copying an inherited value into the local field.  On levels other than the default level the button should be disabled whenever the value of the field is not inherited (so pressing the button should have the effect of disabling it).  On the defalut level there can be no inherited values, so the button should be omitted (preferrably) or permanently disabled.

The Set Special button has the effect of setting a "special" value into the local field, if one is defined for the field.  (The IMP framework field types support the definition of a special value, but the use of these values is optional.  See the discussion of special values.)   The button should be enabled for any field for which a special value is defined and it should be absent (preferrably) or permanently disabled for any field for which no special value is defined.

The Set Empty button has the effect of seting an "empty" value into the local field, if one is defined for the field.  (The IMP framework field types support the definition of an empty value, but the use of these values is optional.  See the discussion of empty values.)   The button should be enabled for any field for which a special value is defined and it should be absent (preferrably) or permanently disabled for any field for which no special value is defined.

The Remove button should have the effect of removing a value that is locally stored in a field, thereby allowing an inherited value to come into effect.  Values of fields on the default level can never be removed, as there is no higher level from which another value may be inherited.  Fields on other levels can be designated as removable or not, although cases in which fields would not be removable are expected to be rare (this would require that once a value is set locally it must always be set locally).  On levels below the default level, the Remove button should be enable whenever the field is removable and contains a locally set value that can be removed (and disabled otherwise).  On the default level, the Remove button should be absent (preferrably) or permanently disabled.

The OK button just closes the dialog.

The buttons listed above are all supported by IMP framework classes.  These buttons have proven useful and sufficient in our early experience, but users should feel free to add further buttons as their applications may require.

Implementing IMP Preference Field Editors

There is more that might be said about the implementation of a field editor for IMP than can reasonably be fit into a user's guide of this sort.  Here we will just point out some of the general responsibilities and concerns that must be addressed by the implementor of a field editor.  For the real details, the IMP field editors themselves should be studied.

General responsibilities

The main responsibilities of a IMP field editor include:

Interactions of concerns

The complexity of field editor types arises not just from the variety of concerns that they must address but from the interaction of these concerns.  Some facets of this interaction:

Mutual consistency of field and model:  The value of a preference is represented both in a field and in the preferences model (as represented by the preferences service).  Generally these representations should be consistent with one another, but their mutual consistency is subject to a number of possible perturbations:  The value in the field may be changed through the field's GUI controls or through the field's API, and the value in the model may be changed through its API.  Typically a change of one will entail a corresponding change in the other.  This issue is further linked to the issue of value inhteritance.

Accommodation of value inheritance:   The value shown in a field may not be stored on the corresponding level of the preferences model but obtained from some higher level.  Thus a new value stored into a field may replace a previously inherited one, or a stored value removed from a field may require replacement by an inherited one.  Additionally, when a value is newly added, modified, or removed in a field on one level, the effect of that change must be propagated down to fields that may inherit from the changed field.

Validity of values:  The different types of preference field are characterized by different types of validity concerns, for example, whether a string field may be empty or whether an integer field should be restricted to a subrange.  Validity conditions may be fixed for the type or parameterized for specific fields of the type.  Each field type is responsible for checking the relevant validity conditions when a field's value changes, whatever the source of the change.  Validity conditions may be imposed and checked at any level of the field-type hierarchy.  As the IMP field types are implemented, each type is responsible for asserting or retracting its own error messages.  Higher-level types must coordinate checks by their subtypes and integrate the results of multiple checks on multiple levels, generally (in the event of failure) to produce a single error message for the field (and retracting any previously set messages).  Changes in the validity status of a field must be communicated to the containing tab (so that the tab can assess its own validity, enable or disable its controls accordingly, and communicate its validity to the containing page).

As the IMP field types are implemented, the validity of a field is not directly reflected in the presentation of the field.  This could have been done, but effects on the display of the field have been used to convey other information instead.  The validity of a field is reflected in error messages that the field passes to the tab and that are displayed by the page when the tab is visible on the page.  (A page can display one message, and each invalid field on a tab will generate its own message, so, when there are multple invalid fields, some will not have their error messages displayed.  However, the error message of each invalid field will be displayed at some point.)

Effects on GUI presentation:  There are a number of attributes of fields that might be reflected in the way they are presented on the preferences page.  As IMP field editors are implemented, they reflect two particular attributes.

One of these attributes is whether the value shown in the field is set on the level of the field or inherited from a higher level.  This is done by setting a distinct background color in inherited fields (which, by default, is blue), whereas locally set fields are shown with the usual background color (white).  This approach was chosen because all field editors (at least those encountered so far) have a background that can be given a distinctive color fairly easily, and this is likely to be easily noticed in the display (at least for those without color blindness).  The use of distinctive fonts to indicate inherited values is harder to program and probably less noticable in the display; the tagging of inherited values with distinctive characters isn't consistent with some value types (i.e., those that are not  logically strings).  The use of a separate field to indicate inheritance would avoid many of the problems with other approaches but would clutter up the displaly.  The modifications of field labels could serve this purpose and would avoid the clutter problem but the modification of field lables is used for another purpose, as described next.
                                                         
The other attribute reflected directly in the way the field is displayed is whether the field has been modified.  As the IMP fields are implemented, modification is taken to mean not just a change in the field value but a change in the origin of the value.  So, if a field has a particular inherited value, and that value is copied into the field, that is considered a modification, even though application clients of the preferences service would not notice any change.  The significance of a modification in a case like this is that it implies a need to modify the underlying preferences model (in this example to store a value locally where there was none before).  Modified fields are indicated by prepending a "modified mark" to the beginning of the name of the field as it appears in the label text.  (By default this mark is a right angle bracket, '>'.)   This approch is easy to implement, since all field editors (at least those encountered so far) have text labels, and it has an effect that is relatively noticable.

Other considerations

The IMP field editor types reflect two main sources of concern:  concerns related to the wigets and other GUI-related elements of the editors (which are addressed in JFace), and concerns arising from the IMP preferences model and Service.  Since the latter are more numerous than the former, the IMP field editor types have been implemented by defining a IMPFieldEditor supertype in which many of the IMP-specific concerns can be supported and adapting GUI-related elements of JFace field editors for implementing corresponding field-editor types in the IMP field-editor type hierarchy.  The number, type, and structure of the GUI controls in the IMP field-editor types is the same as in the corresponding JFace field editor types, but the methods that manipulate the controls have been modified in various ways, for example, to work with the IMP Preferences Service rather than the JFace Preference Store, and to keep track of IMP-related attributes.

The IMP-specific part of the interface for field-editor types is largely systematic; that is, many of the same operations apply across many (if not all) field-editor types.  The JFace part of the interface to the field-editor types is somewhat less systematic, which can require some additional acclimitzation on the part of a field-editor implementor.  The lack of regularity is due in part to the variable number and types of controls within different types of field editor, but it also arises from the use of different names for conceptually similar operations (notably, updating the field) and to the requirement with some field-editor types but not others that the containing composite be provided as a parameter for certain operations.

IMP Preferences Utilities

IMP provides a utility class with routines that simplify some of the work that implementors of preference pages may need to perform.  That class is org.eclipse.imp.preferences.IMPPreferencesUtilities.  Methods for creating fields and their details links have already been mentioned.

The main categories of methods and types provided by IMPPreferencesUtilities are as follows:

The IMP 'New Preference Page' Wizard

IMP has a 'New Preference Page' Wizard to help in creating new IMP preference pages.  The wizard can be found and invoked through the File -> New menu, under the name IDE Language Support/Core Services/Preferences Dialog.

The dialog has six fields, as follows:

Project:  Takes the name of the plugin project in which the IMP-based IDE is being defined.  This field is required.

Language:  Takes the name of the language for which the IMP-base IDE is being defined.  The language and project must be consistent in that the project must contain a plugin for the language (although the names of the project and language do not have to be the same).  This field is required.

Id:  Should be a unique identifier for the preference page that is suitable for use within Eclipse.  Its role is to differentiate multiple preference pages for the same language or IDE and to allow the extension for one page to refer to that for another.  (Such references are used, for example, to organize nesting of perference pages in the main Eclipse preferences dialog.)  While the use of an id is not strictly required (for instance, it would not be needed where there will only ever be one preference page for a language), the provision of an id is strongly encouraged.

Name:  Should be a (probably) unique identifier for the preference page that is suitable for use by people.  As with the id, the main purpose of the name is to help differentiate and organize multiple pages for the same language, and its use is not strictly required but is strongly encouraged.

Class:  Takes the qualified name of the class that will implement the preference page; this name will be given to the class that is generated by IMP.  If the packages designated in the name do not already exist then IMP will create them.  This field is required.

Category:  Represents the item in the Eclipse preferences menu, if any, under which the new page should be listed.  To have the new page listed under an existing page, provide the id of the existing page.  To have the new page listed at the top level of the menu, leave the field blank.

When you run the 'New Preference Page' wizard, seven classes are generated:

Examples

As you can see, there are not yet any examples here.  The best examples are the templates for the preference page and tabs that are found in org.eclipse.imp/templates and the implementations of preference pages and tabs for JikesPG (org.eclipse.imp.lpg.preferences) and for X10 (org.eclipse.imp.x10dt.preferences).  The IMP framework classes relating to preference pages are found in org.eclipse.imp.runtime.preferences and its subpackages.

Incubation
Incubation