Sapphire Developer Guide > Releases > 0.2

Migration Guide for 0.2

This documents covers code changes that need to be made by Sapphire adopters as part of migrating to 0.2 release. Only changes from the previous release are covered.

Table of Contents

  1. Annotations
  2. Element Properties
  3. NoDuplicates and UniqueValueValidator
  4. SapphireEditorForXml
  5. SapphireCondition
  6. Browse Handlers
  7. Jump Handlers
  8. Action Links
  9. Dynamic Label
  10. Project Metadata
  11. Miscellaneous

Annotations

Annotations on model elements:

Before After
@DefaultValue( "something" )
@DefaultValue( text = "something" )
@DefaultValueProvider( impl = CustomImpl.class )

public class CustomImpl extends DefaultValueProviderImpl
{
    ...
}
@DefaultValue( service = CustomImpl.class )

public class CustomImpl extends DefaultValueService
{
    ...
}
@PossibleValuesFromModel( path = "/SomeProperty" )
@PossibleValues( property = "/SomeProperty" )
@PossibleValuesProvider( impl = CustomImpl.class )

public class CustomImpl extends PossibleValuesProviderImpl
{
    ...
}
@PossibleValues( service = CustomImpl.class )

public class CustomImpl extends PossibleValuesService
{
    ...
}
@ValueSerializer( impl = CustomImpl.class )

public class CustomImpl extends ValueSerializerImpl
{
    ...
}
@ValueSerialization( service = CustomImpl.class )

public class CustomImpl extends ValueSerializationService
{
    ...
}
@Enabler( impl = CustomImpl.class )

public class CustomImpl extends EnablerImpl
{
    ...
}
@Enablement( service = CustomImpl.class )

public class CustomImpl extends EnablementService
{
    ...
}
@EnabledWhen( "..." )
@Enablement( expr = "..." )
@EnabledByBooleanProperty( "SomeProperty" )
@Enablement( expr = "${ SomeProperty }" )
@EnabledByEnumProperty( property = "SomeProperty", values = { "A", "B", "C" } )
@Enablement( expr = "${ SomeProperty == 'A' || SomeProperty == 'B' || SomeProperty == 'C' }" )
@Reference( target = Object.class, resolver = CustomImpl.class )

public class CustomImpl extends ReferenceResolverImpl
{
    ...
}
@Reference( target = Object.class, service = CustomImpl.class )

public class CustomImpl extends ReferenceService
{
    ...
}

Element Properties

Before After
@Type( base = IAddress.class )

ElementProperty PROP_ADDRESS = new ElementProperty( TYPE, "Address" );

IAddress getAddress();

...

@GenerateXmlBinding( elementPath = "address" )

public interface IAddress extends IModelElementForXml
{
    ...
}
@Type( base = IAddress.class )
@XmlBinding( path = "address" )

ImpliedElementProperty PROP_ADDRESS = new ImpliedElementProperty( TYPE, "Address" );

IAddress getAddress();

...

@GenerateImpl

public interface IAddress extends IModelElement
{
    ...
}
@Type( base = IAssistant.class )
@XmlBinding( path = "assistant" )

ElementProperty PROP_ASSISTANT = new ElementProperty( TYPE, "Assistant" );

IAssistant getAssistant();
IAssistant getAssistant( boolean createIfNecessary );
@Type( base = IAssistant.class )
@XmlBinding( path = "assistant" )

ElementProperty PROP_ASSISTANT = new ElementProperty( TYPE, "Assistant" );

ModelElementHandle<IAssistant> getAssistant();
<with>
  <property>Address</property>
  <content>
    ...
  </content>
</with>
<with>
  <property>Address</property>
  <default-panel>
    <content>
      ...
    </content>
  </default-panel>
</with>
<element-property-composite>
  <property>Assistant</property>
  <conditional>delegate some tasks to an assistant</conditional>
  <default-panel>
    <content>
      ...
    </content>
  </default-panel>
</element-property-composite>
<with>
  <property>Assistant</property>
  <label>delegate some tasks to an assistant</label>
  <panel>
    <key>IAbc</key>
    <content>
      ...
    </content>
  </panel>
</with>

The following Eclipse search/replace regular expressions can be used to perform the last migration migration in the table:

Scope:    *.sdef
Search:   (?s)<element-property-composite>(.*?)<conditional>(.*?)</conditional>(.*?)<default-panel>(.*?)</default-panel>(.*?)</element-property-composite>
Replace:  <with>\1<label>\2</label>\3<panel><key>?????</key>\4</panel>\5</with>

After applying the above search and replace, look for <key>?????</key> in your sdef files and specify the actual key. In this case, it would be the type name of the element held by the property.

NoDuplicates and UniqueValueValidator

The @NoDuplicates annotation is now applied to a value property (for elements contained in a list) rather than the list property. It can be used any place where UniqueValueValidator class has previously been used.

Before After
@DependsOn( "*/Name" )
@Validator( impl = UniqueValueValidator.class )

ValueProperty PROP_NAME = new ValueProperty( TYPE, "Name" );
@NoDuplicates

ValueProperty PROP_NAME = new ValueProperty( TYPE, "Name" );
@Type( base = IConnection.class )
@NoDuplicates
                             
ListProperty PROP_CONNECTIONS = new ListProperty( TYPE, "Connections" );

ModelElementList<IConnection> getConnections();

...

public interface IConnection extends IModelElement
{
    @DependsOn( "*/Name" )
    @Validator( impl = UniqueValueValidator.class )

    ValueProperty PROP_NAME = new ValueProperty( TYPE, "Name" );
}
@Type( base = IConnection.class )
                             
ListProperty PROP_CONNECTIONS = new ListProperty( TYPE, "Connections" );

ModelElementList<IConnection> getConnections();

...

public interface IConnection extends IModelElement
{
    @NoDuplicates

    ValueProperty PROP_NAME = new ValueProperty( TYPE, "Name" );
}

SapphireEditorForXml

Before After
public class SampleEditor extends SapphireEditorForXml 
{
    public SampleEditor() 
    {
        super( "org.something.ui" );
        setEditorDefinitionPath( "org.something.ui/sdef/SampleEditor.sdef/main" );
    }

    @Override
    protected IModel createModel( ModelStore modelStore ) 
    {
        ...
    }
}
public class SampleEditor extends SapphireEditorForXml 
{
    public SampleEditor() 
    {
        super( "org.something.ui" );
        setRootModelElementType( ISampleModel.TYPE );
        setEditorDefinitionPath( "org.something.ui/sdef/SampleEditor.sdef/main" );
    }
}

SapphireCondition

Any existing implementations of SapphireCondition that override getDependencies() method should be changed to override SapphireModelCondition instead.

Before After
public class SampleCondition extends SapphireCondition 
{
    @Override
    public boolean evaluate() 
    {
        ...
    }
    
    @Override
    public List getDependencies()
    {
        ...
    }    
}
public class SampleCondition extends SapphireModelCondition 
{
    @Override
    public boolean evaluate() 
    {
        ...
    }
    
    @Override
    public List getDependencies()
    {
        ...
    }    
}

It is important that any subclass of SapphireModelCondition that overrides initCondition() method, also calls the super implementation of this method. Failing to do that will prevent the condition from refreshing on dependent property changes.

Browse Handlers

Browse handler definition in the UI definition file:

Before After
<property-editor>
    <browse-handler>
        <class>MyBrowseHandler</class>
    </browse-handler>
</property-editor>
<property-editor>
    <action-handler>
        <action>Sapphire.Browse</action>
        <impl>MyBrowseHandler</impl>
    </action-handler>
</property-editor>

Scope:    *.sdef
Search:   <browse-handler>(\s*)<class>(.*?)</class>(\s*)</browse-handler>
Replace:  <action-handler>\1<action>Sapphire.Browse</action>\1<impl>\2</impl>\3</action-handler>

Browse handler definition in an extension:

Before After
<plugin>
    <extension point="org.eclipse.sapphire.ui.browseHandlers">
        <browse-handler factory="org.something.MyBrowseHandlerFactory"/>
    </extension>
</plugin>

Sapphire no longer uses Eclipse extension system. Instead, create sapphire-extension.xml file in the META-INF folder. The extension must be located in the same classloader as Sapphire. On an OSGi system this is done by creating a fragment to the org.eclipse.sapphire.ui bundle.

Note that the factory concept has been removed. The browse handler implementation class is specified directly. Applicability is controlled by a separate condition class that must extends SapphireCondition.

<extension xmlns="http://www.eclipse.org/sapphire/xmlns/extension">
    <action-handler>
        <action>Sapphire.Browse</action>
        <impl>org.something.MyBrowseHandler</impl>
        <condition>org.something.MyBrowseHandlerCondition</condition>
    </action-handler>
</extension>

Browse handler implementation:

Before After
public class MyBrowseHandler extends BrowseHandler
{
    @Override
    public String browse( SapphireRenderingContext context )
    {
        ...
    }
}
public class MyBrowseHandler extends SapphireBrowseActionHandler
{
    @Override
    public String browse( SapphireRenderingContext context )
    {
        ...
    }
}

Jump Handlers

Jump handler definition in the UI definition file:

Before After
<property-editor>
    <jump-handler>MyJumpHandler</jump-handler>
</property-editor>
<property-editor>
    <action-handler>
        <action>Sapphire.Jump</action>
        <impl>MyJumpHandler</impl>
    </action-handler>
</property-editor>

Scope:    *.sdef
Search:   ([ \t]*)<jump-handler>(.*?)</jump-handler>
Replace:  \1<action-handler>\n\1  <action>Sapphire.Jump</action>\n\1  <impl>\2</impl>\n\1</action-handler>

Jump handler definition in an extension:

Before After
<plugin>
    <extension point="org.eclipse.sapphire.ui.jumpHandlers">
        <jump-handler class="org.something.MyJumpHandler"/>
    </extension>
</plugin>

Sapphire no longer uses Eclipse extension system. Instead, create sapphire-extension.xml file in the META-INF folder. The extension must be located in the same classloader as Sapphire. On an OSGi system this is done by creating a fragment to the org.eclipse.sapphire.ui bundle.

Note that the logic from the isApplicable method needs to be extracted into a separate condition class. The condition class must extend SapphireCondition.

<extension xmlns="http://www.eclipse.org/sapphire/xmlns/extension">
    <action-handler>
        <action>Sapphire.Jump</action>
        <impl>org.something.MyJumpHandler</impl>
        <condition>org.something.MyJumpHandlerCondition</condition>
    </action-handler>
</extension>

Jump handler implementation:

Before After
public class MyJumpHandler extends JumpHandler
{
    @Override
    public boolean isApplicable( ValueProperty property )
    {
        return true;
    }

    @Override
    public int getPriority()
    {
        return 0;
    }

    @Override
    public boolean canLocateJumpTarget( SapphirePart part,
                                        SapphireRenderingContext context,
                                        IModelElement element,
                                        ValueProperty property )
    {
        // Logic for evaluating whether the jump handler is active goes here.
        
        return false
    }

    @Override
    public void jump( SapphirePart part,
                      SapphireRenderingContext context,
                      IModelElement element,
                      ValueProperty property )
    {
        // Logic for executing the jump goes here.
    }
}

The isApplicable method has been replaced with a separate condition class that is specified as part of handler definition. This is usually only necessary when a jump handler is contributed in an extension (as opposed to locally as part of a property editor definition).

The getPriority method has been replaced with location hints specified as part of handler definition.

public class MyJumpHandler extends SapphireJumpActionHandler
{
    @Override
    public void init( SapphireAction action,
                      ISapphireActionHandlerDef def )
    {
        super.init( def );
        
        // Setup listeners on external resources relevant in determining whether
        // the jump handler is active. The listeners should call refreshEnablementState
        // method. If a listener infrastructure is not available, a polling thread
        // can be used instead.
        
        // If all relevant resources are other properties in the same model, skip
        // overriding this method and override initDependencies method instead.
    }

    @Override
    protected void initDependencies( List<String> dependencies )
    {
        super.initDependencies( dependencies );
        
        // The default implementation will add the property whose editor the jump handler
        // is attached to. If the enablement state of the jump handler is dependent on other
        // properties in the model, paths to those properties should be added here.
    }

    @Override
    protected void refreshEnablementState()
    {
        // Logic for evaluating whether the jump handler is active goes here.
        
        setEnabled( false );
    }

    @Override
    protected Object run( SapphireRenderingContext context )
    {
        // Logic for executing the jump goes here.
        
        return null;
    }

    @Override    
    public void dispose()
    {
        super.dispose();
        
        // Remove listeners and stop threads configured in the init method.
    }
}

Action Links

Defined by referencing existing action:

Before After
<content>
    <action-link>
        <action-id>node:add</action-id>
        <label>Add a contact</label>
    </action-link>
</content>

While the XML markup for definining action links hasn't changed for this scenario, the IDs for all system actions have changed. The appropriate system action ID can be found here.

<content>
    <action-link>
        <action-id>Sapphire.Add</action-id>
        <label>Add a contact</label>
    </action-link>
</content>

Defined by referencing existing action handler:

Before After
<content>
    <action-link>
        <action-id>node:add:IListItemWithInteger</action-id>
        <label>Add a list item with integer</label>
    </action-link>
</content>
<content>
    <action-link>
        <action-id>Sapphire.Add</action-id>
        <action-handler-id>Sapphire.Add.IListItemWithInteger</action-handler-id>
        <label>Add a list item with integer</label>
    </action-link>
</content>

Defined with inline action implementation:

Before After
<content>
    <action-link>
        <action-class>MyAction</action-class>
        <label>Link</label>
    </action-link>
</content>
<content>
    <action-link>
        <action-id>MyAction</action-id>
        <label>Link</label>
        <action>
            <id>MyAction</id>
        </action>
        <action-handler>
            <action>MyAction</action>
            <impl>MyActionHandler</impl>
        </action-handler>
    </action-link>
</content>

Dynamic Label

With the new support for expressions, some old workarounds have been retired. One of these is the "dynamic-label" used for specifying label for the content outline. You must now use an expression to achieve the same affect.

Before After
<node-list>
  <node-template>
    <dynamic-label>
      <property>Name</property>
      <null-value-label>&lt;contact&gt;</null-value-label>
    </dynamic-label>
  </node-template>
</node-list>
<node-list>
  <node-template>
    <label>${ Name == null ? "&lt;contact&gt;" : Name }</label>
  </node-template>
</node-list>

The following Eclipse search/replace regular expressions can be used to perform this migration:

Scope:    *.sdef
Search:   <dynamic-label>\s*<property>(.*)</property>\s*<null-value-label>(.*)</null-value-label>\s*</dynamic-label>
Replace:  <label>\$\{ \1 == null \? \"\2\" \: \1 \}</label>

Project Metadata

In the .project file:

Before After
<buildCommand>
    <name>org.eclipse.sapphire.ui.builder</name>
    <arguments>
        <dictionary>
            <key>input</key>
            <value>sdef</value>
        </dictionary>
        <dictionary>
            <key>output</key>
            <value>.resources/sdef</value>
        </dictionary>
    </arguments>
</buildCommand>
<buildCommand>
    <name>org.eclipse.sapphire.sdk.builder</name>
    <arguments>
    </arguments>
</buildCommand>

In the customBuildCallbacks.xml file:

Before After
<taskdef 
  resource="org/eclipse/sapphire/ui/build/antlib.xml"
  classpathref="sapphire.classpath"/>

<sapphire.extract-string-resources src="sdef" dest=".resources/sdef"/>
<taskdef 
  resource="org/eclipse/sapphire/sdk/build/antlib.xml"
  classpathref="sapphire.classpath"/>

<sapphire src="." dest=".resources"/>

Miscellaneous

Child property editor definition:

Before After
<property-editor>
    <child-property>
        <name>MyChildProperty</name>
    </child-property>
</property-editor>
<property-editor>
    <child-property>
        <property>MyChildProperty</property>
    </child-property>
</property-editor>