Sapphire Developer Guide > Releases > 0.5

Migration Guide for 0.5

This documents covers code changes that need to be made by Sapphire adopters as part of migrating to 0.5 release. Only changes from the previous 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 );