# Migration Guide for Sapphire 0.2

This documents covers changes that need to be made by Sapphire adopters as part of migrating to Sapphire 0.2 from a prior release. Only changes from the previous major 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<String> getDependencies()
        {
            ...
        }
    }

    public class SampleCondition extends SapphireModelCondition
    {
        @Override
        public boolean evaluate()
        {
            ...
        }

        @Override
        public List<String> 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>

Copyright (c) 2016 Oracle
Content made available under the terms of Eclipse Public License.