# Migration Guide for Sapphire 0.5

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

Table of Contents

1. CompositeStatusFactory
2. IModelElement
3. LayeredListBindingImpl
4. ListBindingImpl
5. MasterDetailsEditorPage
6. MasterDetailsEditorPagePart
7. ModelElementList
8. ModelElementListener
9. ModelElementType
10. ModelProperty
11. ModelPropertyListener
12. PropertyListeners
13. SapphireActionHandler
14. SapphireDiagramActionHandler
15. SapphireEditor
16. SapphireWizardPageListener
17. Actuator
18. Content Outline Node Image
19. Diagram Drag-n-Drop
20. Diagram Image
21. Graphiti to GEF Transition

## CompositeStatusFactory

Before | After
--- | ---
    factory.add( status );
    factory.merge( status );

## IModelElement

Before | After
--- | ---
    element.getModelElementType();
    element.type();
    element.isPropertyEnabled( property );
    element.enabled( property );
    element.addListener( listener );
    element.attach( listener );
    element.addListener( listener, path );
    element.attach( listener, path );
    element.removeListener( listener );
    element.detach( listener );
    element.removeListener( listener, path );
    element.detach( listener, path );
    element.notifyPropertyChangeListeners( property );
    element.refresh( property );

## LayeredListBindingImpl

The list binding API has been improved to more efficiently support insertion into the middle of the list along with arbitrary movement of existing list items.

Before | After
--- | ---
    public class ExampleListBinding extends LayeredListBindingImpl
    {
        @Override
        protected Resource createResource( Object obj )
        {
            ...
        }

        @Override
        protected Resource addUnderlyingObject( ModelElementType type )
        {
            ...
        }

        @Override
        public void swap( Resource x, Resource y )
        {
            ...
        }

        ...
    }
    public class ExampleListBinding extends LayeredListBindingImpl
    {
        @Override
        protected Resource resource( Object obj )
        {
            ...
        }

        @Override
        protected Object insertUnderlyingObject( ModelElementType type, int position )
        {
            final Object obj = addUnderlyingObject( type );
            move( resource( obj ), position );
            return obj;
        }

        private Object addUnderlyingObject( ModelElementType type )
        {
            ...
        }

        @Override
        public void move( Resource resource, int position )
        {
            List<Resource> list = read();
            int size = list.size();

            if( position < 0 || position > size )
            {
                throw new IllegalArgumentException();
            }
            else
            {
                int oldPosition = list.indexOf( resource );

                if( position < oldPosition )
                {
                    for( int i = oldPosition; i >= position; i-- )
                    {
                        moveUp( list, resource );
                    }
                }
                else
                {
                    for( int i = oldPosition; i <= position; i++ )
                    {
                        moveDown( list, resource );
                    }
                }
            }
        }

        private void moveUp( List<Resource> list, Resource resource )
        {
            int index = list.indexOf( resource );

            if( index == -1 )
            {
                throw new IllegalArgumentException();
            }

            if( index > 0 )
            {
                Resource previous = list.get( index - 1 );
                swap( resource, previous );
            }
        }

        private void moveDown( List<Resource> list, Resource resource )
        {
            int index = list.indexOf( resource );

            if( index == -1 )
            {
                throw new IllegalArgumentException();
            }

            if( index < list.size() - 1 )
            {
                Resource next = list.get( index + 1 );
                swap( resource, next );
            }
        }

        private void swap( Resource x, Resource y )
        {
            ...
        }

        ...
    }
The above represents the quickest way to migrate an existing list binding, but is far from ideal. A better solution would be to implement the new insertUnderlyingObject() and move() methods directly.

## ListBindingImpl

The list binding API has been improved to more efficiently support insertion into the middle of the list along with arbitrary movement of existing list items.

Before | After
--- | ---
    public class ExampleListBinding extends ListBindingImpl
    {
        @Override
        public Resource add( ModelElementType type )
        {
            ...
        }

        @Override
        public void swap( Resource x, Resource y )
        {
            ...
        }

        ...
    }
    public class ExampleListBinding extends ListBindingImpl
    {
        @Override
        public Resource insert( ModelElementType type, int position )
        {
            Resource resource = add( type );
            move( resource, position );
            return resource;
        }

        private Resource add( ModelElementType type )
        {
            ...
        }

        @Override
        public void move( Resource resource, int position )
        {
            List<Resource> list = read();
            int size = list.size();

            if( position < 0 || position > size )
            {
                throw new IllegalArgumentException();
            }
            else
            {
                int oldPosition = list.indexOf( resource );

                if( position < oldPosition )
                {
                    for( int i = oldPosition; i >= position; i-- )
                    {
                        moveUp( list, resource );
                    }
                }
                else
                {
                    for( int i = oldPosition; i <= position; i++ )
                    {
                        moveDown( list, resource );
                    }
                }
            }
        }

        private void moveUp( List<Resource> list, Resource resource )
        {
            int index = list.indexOf( resource );

            if( index == -1 )
            {
                throw new IllegalArgumentException();
            }

            if( index > 0 )
            {
                Resource previous = list.get( index - 1 );
                swap( resource, previous );
            }
        }

        private void moveDown( List<Resource> list, Resource resource )
        {
            int index = list.indexOf( resource );

            if( index == -1 )
            {
                throw new IllegalArgumentException();
            }

            if( index < list.size() - 1 )
            {
                Resource next = list.get( index + 1 );
                swap( resource, next );
            }
        }

        private void swap( Resource x, Resource y )
        {
            ...
        }

        ...
    }
The above represents the quickest way to migrate an existing list binding, but is far from ideal. A better solution would be to implement the new insert() and move() methods directly.

## MasterDetailsEditorPage

Before | After
--- | ---
    page.getContentTree();
    page.outline();

## MasterDetailsEditorPagePart

Before | After
--- | ---
    page.getContentTree();
    page.outline();

## ModelElementList

Before | After
--- | ---
    list.addNewElement( ExampleElementVariant.TYPE );
    list.insert( ExampleElementVariant.TYPE );
    list.addNewElement();
    list.insert();

## ModelElementListener

Before | After
--- | ---
    public ExampleListener extends ModelElementListener
    {
        @Override
        public void propertyChanged( ModelPropertyChangeEvent event )
        {
            ...
        }
    }
    public ExampleListener extends FilteredListener<PropertyEvent>
    {
        @Override
        protected void handleTypedEvent( PropertyEvent event )
        {
            ...
        }
    }
    public ExampleListener extends ModelElementListener
    {
        @Override
        public void validationStateChanged( final ValidationStateChangeEvent event )
        {
            ...
        }
    }
    public ExampleListener extends FilteredListener<ElementValidationEvent>
    {
        @Override
        protected void handleTypedEvent( ElementValidationEvent event )
        {
            ...
        }
    }
    public ExampleListener extends ModelElementListener
    {
        @Override
        public void handleElementDisposedEvent( final ModelElementDisposedEvent event )
        {
            ...
        }
    }
    public ExampleListener extends FilteredListener<ElementDisposeEvent>
    {
        @Override
        protected void handleTypedEvent( ElementDisposeEvent event )
        {
            ...
        }
    }

## ModelElementType

Before | After
--- | ---
    ModelElementType.getModelElementType( ... )
    ModelElementType.read( ... )
    type.getProperties()
    type.properties()
    type.getProperty( ... )
    type.property( ... )

## ModelProperty

Before | After
--- | ---
    property.addListener( ... )
    property.attach( ... )
    property.getListeners()
| No replacement has been provided. This method was an internal implementation detail. Any adopter with a dependency on this method is urged to post details to Sapphire Adopter Forum.

## ModelPropertyListener

Before | After
--- | ---
    public ExampleListener extends ModelPropertyListener
    {
        @Override
        public void public void handlePropertyChangedEvent( ModelPropertyChangeEvent event )
        {
            ...
        }
    }
    public ExampleListener extends FilteredListener<PropertyEvent>
    {
        @Override
        protected void handleTypedEvent( PropertyEvent event )
        {
            ...
        }
    }
The above is the most direct migration target, but in many cases more efficient results can be achieved by filtering on PropertyContentEvent instead.
    public ExampleListener extends FilteredListener<PropertyContentEvent>
    {
        @Override
        protected void handleTypedEvent( PropertyContentEvent event )
        {
            ...
        }
    }
The other subclasses of PropertyEvent are PropertyEnablementEvent and PropertyValidationEvent.

## PropertyListeners

Before | After
--- | ---
    @PropertyListeners( ... )
    @Listeners( ... )

## SapphireActionHandler

Before | After
--- | ---
    import org.eclipse.sapphire.ui.def.ISapphireActionHandlerDef;
    import org.eclipse.sapphire.ui.def.ActionHandlerDef;
    public class CustomActionHandler extends SapphireActionHandler
    {
        @Override
        public void init( SapphireAction action, ISapphireActionHandlerDef def )
        {
            super.init( action, def );

            ...
        }

        ...
    }
    public class CustomActionHandler extends SapphireActionHandler
    {
        @Override
        public void init( SapphireAction action, ActionHandlerDef def )
        {
            super.init( action, def );

            ...
        }

        ...
    }

## SapphireDiagramActionHandler

Before | After
--- | ---
    public class ExampleActionHandler extends SapphireDiagramActionHandler
    {
        @Override
        public boolean canExecute( Object obj )
        {
            ...
        }

        ...
    }
    public class ExampleActionHandler extends SapphireActionHandler
    {
        ...
    }

## SapphireEditor

Before | After
--- | ---
    setPageId( editorPage, editorPageId, editorPagePart )
| Remove method call. It is no longer necessary.

## SapphireWizardPageListener

The wizard page part has been converted to use common listener framework.

Before | After
--- | ---
    public class ExampleListener extends SapphireWizardPageListener
    {
        @Override
        public void handleShowPageEvent( SapphirePartEvent event )
        {
            ...
        }
    }
    public class ExampleListener extends FilteredListener<VisibilityChangedEvent>
    {
        @Override
        protected void handleTypedEvent( VisibilityChangedEvent event )
        {
            if( ( (SapphireWizardPagePart) event.part() ).isVisible() )
            {
                ...
            }
        }
    }
    public class ExampleListener extends SapphireWizardPageListener
    {
        @Override
        public void handleHidePageEvent( SapphirePartEvent event )
        {
            ...
        }
    }
    public class ExampleListener extends FilteredListener<VisibilityChangedEvent>
    {
        @Override
        protected void handleTypedEvent( VisibilityChangedEvent event )
        {
            if( ! ( (SapphireWizardPagePart) event.part() ).isVisible() )
            {
                ...
            }
        }
    }
    part.addListener( listener )
    part.attach( listener )
    part.removeListener( listener )
    part.dettach( listener )

## Actuator

The action link facility in sdef language has been generalized to support other presentations. In the process it was renamed as actuator.

Before | After
--- | ---
    <action-link>
        <action-id>Sapphire.Add</action-id>
        <label>add a contact</label>
    </action-link>
    <actuator>
        <action-id>Sapphire.Add</action-id>
        <label>add a contact</label>
    </actuator>
    <action-link>
        <action-id>Sapphire.Add</action-id>
        <label>add a contact</label>
        <show-image/>
    </action-link>
    <actuator>
        <action-id>Sapphire.Add</action-id>
        <label>add a contact</label>
        <show-image>true</show-image>
    </actuator>

`Scope: *.sdef
Search: (?s)<action-link>(.*?)<show-image/>(.*?)</action-link>
Replace: <actuator>\1<show-image>true</show-image>\2</actuator>`

`Scope: *.sdef
Search: (?s)<action-link>(.*?)</action-link>
Replace: <actuator>\1</actuator>`

## Content Outline Node Image

Prior to advent of Sapphire Expression Language, sdef language included the "use-model-element-image" switch as a way to indicate to the system that content outline node's image should be the same as the image for the associated model element. With advent of EL, the special switch is no longer necessary as an expression can be used to capture the same semantics.

Before | After
--- | ---
    <use-model-element-image/>
    <image>${ Image() }</image>

`Scope: *.sdef
Search: <use-model-element-image[ ]*/>
Replace: <image>\${ Image() }</image>`

`Scope: *.sdef
Search: <use-model-element-image>[ ]*</use-model-element-image>
Replace: <image>\${ Image() }</image>`

## Diagram Drag-n-Drop

Drag-n-drop support in the diagram editor has been reworked to use service mechanism instead of action mechanism.

Before | After
--- | ---
    public class ExampleDragAndDropActionHandler extends SapphireDiagramActionHandler
    {
        @Override
        public boolean canExecute( Object obj )
        {
            ...
        }

        @Override
        protected Object run( SapphireRenderingContext context )
        {
            DiagramRenderingContext ctx = (DiagramRenderingContext) context;
            Object obj = ctx.getObject();

            ...
        }
    }
    public class ExampleDragAndDropService extends DragAndDropService
    {
        @Override
        public boolean droppable( DropContext context )
        {
            Object obj = context.object();

            ...
        }

        @Override
        public void drop( DropContext context )
        {
            Object obj = context.object();

            ...
        }
    }
    <diagram-page>
        <action-handler>
            <action>Sapphire.Drop</action>
            <impl>ExampleDragAndDropActionHandler</impl>
        </action-handler>
    </diagram-page>
    <diagram-page>
        <service>
            <implementation>ExampleDragAndDropService</implementation>
        </service>
    </diagram-page>

## Diagram Image

Image specifications inside diagram parts are now consistent with the rest of the sdef. Images are now referenced at the point of use, rather than defined separately and then referenced by id.

Before | After
--- | ---
    <image-decorator>
        <id>defaultIndicator</id>
        <path>icons/default-indicator.png</path>
    </image-decorator>
| Remove, path will be specified at the point of use.
    <image-decorator>
        <id>defaultIndicator</id>
        <decorator-placement>image</decorator-placement>
        <horizontal-align>center</horizontal-align>
        <vertical-align>top</vertical-align>
    </image-decorator>
    <image-decorator>
        <path>icons/default-indicator.png</path>
        <decorator-placement>image</decorator-placement>
        <horizontal-align>center</horizontal-align>
        <vertical-align>top</vertical-align>
    </image-decorator>
    <tool-palette-image>
        <id>flowPaletteImage</id>
        <path>icons/control-flow-16.png</path>
    </tool-palette-image>
    <tool-palette-image>icons/control-flow-16.png</tool-palette-image>
    <image>
        <id>routerIcon</id>
        <placement>top</placement>
        <possible>
          <id>routerIcon</id>
          <path>icons/node-router.png</path>
        </possible>
    </image>
    <image>
        <path>icons/node-router.png</path>
        <placement>top</placement>
    </image>

## Graphiti to GEF Transition

The Graphiti-based diagram presentation (introduced in 0.4 release) has been replaced with a presentation written directly on top of GEF. This has significantly reduced Sapphire's dependencies and enabled diagram support to more tightly integrate with the rest of Sapphire.

Before | After
--- | ---
    import org.eclipse.sapphire.ui.gef.diagram.editor.[etc];
    import org.eclipse.sapphire.ui.swt.gef.[etc];
    page = new SapphireDiagramEditor( model, path );
    addPage( 0, page, SapphireDiagramEditorFactory.createEditorInput( file ) );
    page = new SapphireDiagramEditor( this, model, path );
    addPage( 0, page );

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