Migration Guide for 0.3
This documents covers code changes that need to be made by Sapphire adopters as part of migrating
to 0.3 release. Only changes from the previous release are covered.
Table of Contents
- Modularity Changes
- ReferenceValue Changes
- Services
- Validation
- Enablement
- Whitespace Handling
- Java Support
- HTML Content Presentation
- Related Content
- Composite Reference
- Section Description
- Function Name Method
- With Directive
- Model Element Image
- Required Annotation
- Master Details Content Node Factory
- Jump Action Handler
- Show In Source Action
The contents of some bundles have changes and several new bundles have been created. These changes
may require adopters to modify Requite-Bundle declarations and import statements.
Old Bundle |
Old Classes |
New Bundle |
New Classes |
org.eclipse.sapphire.modeling
|
org.eclipse.sapphire.modeling.xml.* org.eclipse.sapphire.modeling.xml.annotations.* org.eclipse.sapphire.modeling.xml.schema.*
|
org.eclipse.sapphire.modeling.xml
|
unchanged
|
org.eclipse.sapphire.modeling
|
org.eclipse.sapphire.modeling.java.*
|
org.eclipse.sapphire.java
|
org.eclipse.sapphire.java.*
|
org.eclipse.sapphire.modeling
|
org.eclipse.sapphire.modeling.WorkspaceFileResourceStore
|
org.eclipse.sapphire.workspace
|
org.eclipse.sapphire.workspace.WorkspaceFileResourceStore
|
org.eclipse.sapphire.modeling
|
org.eclipse.sapphire.modeling.annotations.EclipseWorkspacePath
|
org.eclipse.sapphire.workspace
|
org.eclipse.sapphire.workspace.WorkspaceRelativePath
|
org.eclipse.sapphire.ui
|
org.eclipse.sapphire.ui.xml.*
|
org.eclipse.sapphire.ui.swt.xml.editor
|
org.eclipse.sapphire.ui.swt.xml.editor.*
|
The core dependency on IStatus, IPath and IProgressMonitor types from org.eclipse.core.runtime bundle
has been eliminated. These classes have been replaced with Status, Path and ProgressMonitor classes in
org.eclipse.sapphire.modeling bundle. StatusBridge, PathBridge and ProgressMonitorBridge classes are available
in org.eclipse.sapphire.platform bundle when back-n-forth conversion is necessary.
Before |
After |
public class CustomValidationService extends ModelPropertyValidationService<Value<String>>
{
public IStatus validate()
{
if( ... )
{
return createErrorStatus( Resources.errorMessage );
}
return Status.OK_STATUS;
}
}
|
public class CustomValidationService extends ModelPropertyValidationService<Value<String>>
{
public Status validate()
{
if( ... )
{
return Status.createErrorStatus( Resources.errorMessage );
}
return Status.createOkStatus();
}
}
When necessary, use StatusBridge class from org.eclipse.sapphire.platform bundle to convert back-n-forth between Sapphire's Status object and
Eclipse's IStatus object.
|
@PossibleValues( service = CustomPossibleValuesService.class, invalidValueSeverity = IStatus.OK )
|
@PossibleValues( service = CustomPossibleValuesService.class, invalidValueSeverity = Status.Severity.OK )
|
public class CustomPossibleValuesService extends PossibleValuesService
{
...
public int getInvalidValueSeverity( String value )
{
if( ... )
{
return IStatus.ERROR;
}
else if( ... )
{
return IStatus.WARNING;
}
return IStatus.OK;
}
}
|
public class CustomPossibleValuesService extends PossibleValuesService
{
...
public Status.Severity getInvalidValueSeverity( String value )
{
if( ... )
{
return Status.Severity.ERROR;
}
else if( ... )
{
return Status.Severity.WARNING;
}
return Status.Severity.OK;
}
}
|
@Type( base = IPath.class )
@Required
@MustExist
@WorkspaceRelativePath
@ValidFileSystemResourceType( FileSystemResourceType.FILE )
@ValidFileExtensions( "xml" )
ValueProperty PROP_LOCATION = new ValueProperty( TYPE, "Location" );
Value<IPath> getLocation();
void setLocation( String location );
void setLocation( IPath location );
These migration sites will not be obvious via problems at compile times. Search for "Value<IPath>".
|
@Type( base = Path.class )
@Required
@MustExist
@WorkspaceRelativePath
@ValidFileSystemResourceType( FileSystemResourceType.FILE )
@ValidFileExtensions( "xml" )
ValueProperty PROP_LOCATION = new ValueProperty( TYPE, "Location" );
Value<Path> getLocation();
void setLocation( String location );
void setLocation( Path location );
|
public class CustomBasePathsProvider extends BasePathsProviderImpl
{
public List<IPath> getBasePaths( IModelElement element )
{
...
}
}
|
public class CustomRelativePathService extends RelativePathService
{
public List<Path> roots()
{
...
}
}
|
public class ProjectRootsBasePathsProvider extends BasePathsProviderImpl
{
public List<IPath> getBasePaths( IModelElement element )
{
final List<IPath> paths = new ArrayList<IPath>();
for( IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects() )
{
paths.add( project.getLocation() );
}
return paths;
}
}
|
public class ProjectRootsRelativePathService extends RelativePathService
{
public List<Path> roots()
{
final List<Path> paths = new ArrayList<Path>();
for( IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects() )
{
paths.add( PathBridge.create( project.getLocation() ) );
}
return paths;
}
}
When necessary, use PathBridge class from org.eclipse.sapphire.platform bundle to convert back-n-forth between Sapphire's Path object and
Eclipse's IPath object.
|
Sapphire extension system no longer requires a flag from Eclipse extension system in order to discover extensions.
Before |
After |
<plugin>
...
<extension point="org.eclipse.sapphire.modeling.extension"/>
</plugin>
|
remove
|
Sapphire now loads all resources (such as images and sdef files) from the class loader. To access resources from another
bundle use standard OSGi facilities to require another bundle or to import packages. To make re-use from other bundles
easier, place images and sdef files into exported packages along with Java source code.
Before |
After |
<definition>
<import>
<bundle>org.sample.foo</bundle>
<package>org.sample.foo</package>
<package>org.sample.foo.xyz</package>
<definition>sdef/Foo.sdef</definition>
</import>
<import>
<bundle>org.sample.bar</bundle>
<package>org.sample.bar</package>
</import>
...
</definition>
|
<definition>
<import>
<package>org.sample.foo</package>
<package>org.sample.foo.xyz</package>
<definition>org/sample/foo/Foo.sdef</definition>
<package>org.sample.bar</package>
</import>
...
</definition>
Note that multiple import elements are no longer supported.
|
@Image( path = "org.sample.foo/images/something.png" )
|
@Image( path = "images/something.png" )
The above migration is sufficient if the annotated type is located in the same plugin as the image. If the image is located
in a different plugin, then you need to arrange to place the image in an exported package and adjust the path in the annotation
accordingly.
|
The ReferenceValue class now takes two type parameters instead of one. The first is the
type of the reference. This used to be assumed to always be a string. The second is the type
of what the reference resolves to.
Before |
After |
ReferenceValue<ImageDescriptor> getImage();
|
ReferenceValue<String,ImageDescriptor> getImage();
|
A few changes have been made to the services API.
Before |
After |
public class MyService extends ModelElementService
{
public void init( final IModelElement element )
{
super.init( element );
...
}
}
|
public class MyService extends ModelElementService
{
public void init( final IModelElement element,
final String[] params )
{
super.init( element, params );
...
}
}
|
@Reference( target = IComponent.class, service = ComponentReferenceService.class )
|
@Reference( target = IComponent.class )
@Service( impl = ComponentReferenceService.class )
|
@PossibleValues( service = CustomPossibleValuesService.class )
|
@Service( impl = CustomPossibleValuesService.class )
|
@DefaultValue( service = CustomDefaultValueService.class )
|
@Service( impl = CustomDefaultValueService.class )
|
@BasePathsProvider( CustomBasePathsProvider.class )
|
@Service( impl = CustomRelativePathService.class )
|
public class CustomBasePathsProvider extends BasePathsProviderImpl
{
public List<Path> getBasePaths( IModelElement element )
{
...
}
}
|
public class CustomRelativePathService extends RelativePathService
{
public List<Path> roots()
{
...
}
}
|
Validators are now model element services. They can be registered globaly via the extension
system or at the level of an individual property using the @Service annotation.
Before |
After |
@Validator( service = MyValidator.class )
|
@Service( impl = MyValidationService.class )
|
@Validators
(
@Validator( impl = MyValidator1.class ),
@Validator( impl = MyValidator2.class )
)
|
@Services
(
@Service( impl = MyValidationService1.class ),
@Service( impl = MyValidationService2.class )
)
|
public class CustomValidator extends ModelPropertyValidator<Value<?>>
{
@Override
public void init( String[] params )
{
...
}
public IStatus validate( Value<?> value )
{
...
}
}
|
public class CustomValidationService extends ModelPropertyValidationService<Value<?>>
{
@Override
public void init( IModelElement element,
ModelProperty property,
String[] params )
{
...
}
public Status validate()
{
final Value<?> value = target();
...
}
}
|
@Validator( impl = UniqueValueValidator.class )
|
@NoDuplicates
|
public class CustomValidator extends UniqueValueValidator
{
...
}
|
public class CustomValidationService extends UniqueValueValidationService
{
...
}
|
Enablement is now handled by true model element services. They can be registered globaly via the extension
system or at the level of an individual property using the @Service annotation.
Before |
After |
@Enablement( service = CustomEnablementService.class )
|
@Service( impl = CustomEnablementService.class )
|
public class CustomEnablementService extends EnablementService
{
@Override
public void init( IModelElement element,
ModelProperty property,
String[] params )
{
super.init( element, property, params );
...
}
@Override
public boolean isEnabled()
{
...
}
}
|
public class CustomEnablementService extends EnablementService
{
@Override
protected void initEnablementService( IModelElement element,
ModelProperty property,
String[] params )
{
super.initEnablementService( element, property, params );
...
}
@Override
public boolean state()
{
refresh();
return super.state();
}
@Override
protected boolean compute()
{
...
}
}
The above represents the quickest way to migrate an existing enablement service, but is far from
ideal. A better solution would be to not override the state() method with a refresh on every invocation.
To do that, the service would need to listen on changes to data used in the compute() method and call
refresh() method when that data changes.
|
Whitespace handling has been made more flexible via the new @Whitespace annotation and the
ValueNormalizationService API. The collapseWhitespace attribute on @XmlValueBinding annotation
is no longer supported.
Before |
After |
@XmlValueBinding( path = "description", collapseWhitespace = true )
|
@XmlValueBinding( path = "description" )
@Whitespace( collapse = true )
|
Support for writing models that reference Java types has been improved. All properties that that hold
Java type names should now be modeled as a reference from JavaTypeName to JavaType in order to receive validation
and content assist support.
Before |
After |
@Type( base = JavaTypeName.class )
@JavaTypeConstraints( ... )
@MustExist
ValueProperty PROP_IMPL_CLASS = new ValueProperty( TYPE, "ImplClass" );
Value<JavaTypeName> getImplClass();
void setImplClass( String value );
void setImplClass( JavaTypeName value );
|
@Type( base = JavaTypeName.class )
@Reference( target = JavaType.class )
@JavaTypeConstraint( ... )
@MustExist
ValueProperty PROP_IMPL_CLASS = new ValueProperty( TYPE, "ImplClass" );
ReferenceValue<JavaTypeName,JavaType> getImplClass();
void setImplClass( String value );
void setImplClass( JavaTypeName value );
|
Support for presenting HTML content in Sapphire UI was introduced in the 0.2 release in
the form of @HtmlContent annotation and a read-only property editor. This approach proved
to be too inflexible and has been replaced with a dedicated HTML UI part that can display
HTML content that's statically specified, drawn from the model or even fetched remotely.
Before |
After |
@HtmlContent
|
remove
|
<property-editor>
<property>MessageBody</property>
<hint>
<name>show.label</name>
<value>false</value>
</hint>
</property-editor>
|
<html>
<content>${ MessageBody }</content>
<show-border/>
</html>
|
See New Feature Documentation
The ability to augment a property editor with an auxiliary property editor has been generalized
into a more powerful related content feature.
Before |
After |
<property-editor>
<property>MainProperty</property>
<aux-property-editor>RelatedProperty</aux-property-editor>
</property-editor>
|
<property-editor>
<property>MainProperty</property>
<related-content>
<property-editor>RelatedProperty</property-editor>
</related-content>
</property-editor>
|
<property-editor>
<property>MainProperty</property>
<aux-property-editor>RelatedProperty1</aux-property-editor>
<aux-property-editor>RelatedProperty2</aux-property-editor>
</property-editor>
|
<property-editor>
<property>MainProperty</property>
<related-content>
<property-editor>
<property>RelatedProperty1</property>
<related-content>
<property>RelatedProperty2</property>
</related-content>
</property-editor>
</related-content>
</property-editor>
|
See New Feature Documentation
The composite-ref construct has been replaced with a more flexible include construct which allows
any form part to be included.
Before |
After |
<composite-ref>
<id>bug.report</id>
</composite-ref>
|
<include>bug.report</include>
|
<composite-ref>
<id>bug.report</id>
<param>
<name>SomeParam</name>
<value>SomeValue</value>
</param>
</composite-ref>
|
<include>
<part>bug.report</part>
<param>
<name>SomeParam</name>
<value>SomeValue</value>
</param>
</include>
|
The following Eclipse search/replace regular expression can be used to perform this
migration (only works when there are no params):
Scope: *.sdef
Search: (?s)<composite-ref>.*?<id>(.*?)</id>.*?</composite-ref>
Replace: <include>\1</include>
The description construct under section has been removed. Use label and spacer to achieve the same result.
Before |
After |
<section>
<description>description</description>
</section>
|
<section>
<content>
<label>description</label>
<spacer/>
</content>
</section>
|
Implementations of custom expression language functions (classes that extend Function) need to be updated
to implement the required new name method. The returned name should be consistent with the name used when
registering the function in sapphire-extension.xml file. The new method is used during toString() execution
on a parsed expression.
Before |
After |
public class CustomFunction extends Function
{
@Override
public FunctionResult evaluate( FunctionContext context )
{
...
}
}
|
public class CustomFunction extends Function
{
@Override
public String name()
{
return "Custom";
}
@Override
public FunctionResult evaluate( FunctionContext context )
{
...
}
}
|
The with directive capabilities have been expanded to allow ancestor element access via a path. To
accommodate this feature, the "property" XML element in the sdef syntax has been renamed to "path".
Before |
After |
<with>
<property>SomeProperty</property>
<default-panel>
<content>
...
</content>
</default-panel>
</with>
|
<with>
<path>SomeProperty</path>
<default-panel>
<content>
...
</content>
</default-panel>
</with>
|
The following Eclipse search/replace regular expression can be used to perform this
migration:
Scope: *.sdef
Search: (?s)<with>(.*?)<property>(.*?)</property>(.*?)</with>
Replace: <with>\1<path>\2</path>\3</with>
The API for model element image provider has been changed to unify it with the model element services
API and to support notification of image changes.
Before |
After |
@Image( small = "..." )
|
@Image( path = "..." )
|
@Image( provider = AttendeeImageProvider.class )
public interface IAttendee extends IModelElement
{
...
}
...
public class AttendeeImageProvider extends ImageProvider
{
private static final String IMG_PERSON
= SapphireSamplesPlugin.PLUGIN_ID + "/org/eclipse/sapphire/samples/contacts/Contact.png";
private static final String IMG_PERSON_FADED
= SapphireSamplesPlugin.PLUGIN_ID + "/org/eclipse/sapphire/samples/contacts/ContactFaded.png";
@Override
public String getSmallImagePath( IModelElement element )
{
if( ( (IAttendee) element ).isInContactsDatabase().getContent() )
{
return IMG_PERSON;
}
else
{
return IMG_PERSON_FADED;
}
}
@Override
public String getSmallImagePath( ModelElementType type )
{
return IMG_PERSON;
}
}
|
@Image( path = "Contact.png" )
@Service( impl = AttendeeImageService.class )
public interface IAttendee extends IModelElement
{
...
}
...
public class AttendeeImageService extends ImageService
{
private static final ImageData IMG_PERSON
= ImageData.readFromClassLoader( IContact.class, "Contact.png" );
private static final ImageData IMG_PERSON_FADED
= ImageData.readFromClassLoader( IContact.class, "ContactFaded.png" );
private ModelPropertyListener listener;
@Override
public void init( IModelElement element, String[] params )
{
super.init( element, params );
this.listener = new ModelPropertyListener()
{
@Override
public void handlePropertyChangedEvent( final ModelPropertyChangeEvent event )
{
notifyListeners( new ImageChangedEvent( AttendeeImageProviderService.this ) );
}
};
element.addListener( this.listener, IAttendee.PROP_IN_CONTACTS_DATABASE.getName() );
}
@Override
public ImageData provide()
{
if( ( (IAttendee) element() ).isInContactsDatabase().getContent() )
{
return IMG_PERSON;
}
else
{
return IMG_PERSON_FADED;
}
}
@Override
public void dispose()
{
super.dispose();
element().removeListener( this.listener, IAttendee.PROP_IN_CONTACTS_DATABASE.getName() );
}
}
|
The following Eclipse search/replace regular expression can be used to perform some of this
migration:
Scope: *.java
Search: (?s)@Image\((.*?)small(.*?)=(.*?)"(.*?)"(.*?)\)
Replace: @Image\(\1path\2=\3"\4"\5\)
The NonNullValue annotation has been renamed to Required to better reflect its broader usage
with both value and element properties.
Before |
After |
@NonNullValue
ValueProperty PROP_NAME = new ValueProperty( TYPE, "Name" );
Value getName();
void setName( String name );
|
@Required
ValueProperty PROP_NAME = new ValueProperty( TYPE, "Name" );
Value getName();
void setName( String name );
|
The following Eclipse search/replace regular expressions can be used to perform this
migration:
Scope: *.java
Search: (?s)@NonNullValue
Replace: @Required
Scope: *.java
Search: (?s)import org\.eclipse\.sapphire\.modeling\.annotations\.NonNullValue;
Replace: import org\.eclipse\.sapphire\.modeling\.annotations\.Required;
The syntax of master details content node factory has been modified to better reflect its
broader usage with both list and element properties.
Before |
After |
<node-list>
<property>HeterogeneousList</property>
<node-template>
<model-element-type>IChildElementWithInteger</model-element-type>
<label>${ StringValue == null ? "<child-with-integer>" : StringValue }</label>
<section>
<label>child element with integer</label>
<content>
<property-editor>StringValue</property-editor>
<property-editor>IntegerValue</property-editor>
</content>
</section>
</node-template>
<node-template>
<model-element-type>IChildElementWithEnum</model-element-type>
<label>${ StringValue == null ? "<child-with-enum>" : StringValue }</label>
<section>
<label>child element with enum</label>
<content>
<property-editor>StringValue</property-editor>
<property-editor>EnumValue</property-editor>
</content>
</section>
</node-template>
<node-template>
<model-element-type>IChildElement</model-element-type>
<label>${ StringValue == null ? "<child>" : StringValue }</label>
<section>
<label>child element</label>
<content>
<property-editor>StringValue</property-editor>
</content>
</section>
</node-template>
</node-list>
|
<node-factory>
<property>HeterogeneousList</property>
<case>
<model-element-type>IChildElementWithInteger</model-element-type>
<label>${ StringValue == null ? "<child-with-integer>" : StringValue }</label>
<section>
<label>child element with integer</label>
<content>
<property-editor>StringValue</property-editor>
<property-editor>IntegerValue</property-editor>
</content>
</section>
</case>
<case>
<model-element-type>IChildElementWithEnum</model-element-type>
<label>${ StringValue == null ? "<child-with-enum>" : StringValue }</label>
<section>
<label>child element with enum</label>
<content>
<property-editor>StringValue</property-editor>
<property-editor>EnumValue</property-editor>
</content>
</section>
</case>
<case>
<model-element-type>IChildElement</model-element-type>
<label>${ StringValue == null ? "<child>" : StringValue }</label>
<section>
<label>child element</label>
<content>
<property-editor>StringValue</property-editor>
</content>
</section>
</case>
</node-factory>
|
<node-ref>CommonNode</node-ref>
|
<node-include>CommonNode</node-include>
|
<node-list-ref>CommonNodeFactory</node-list-ref>
|
<node-include>CommonNodeFactory</node-include>
|
The following Eclipse search/replace regular expressions can be used to perform this
migration:
Scope: *.sdef
Search: (?s)<node-list>
Replace: <node-factory>
Scope: *.sdef
Search: (?s)</node-list>
Replace: </node-factory>
Scope: *.sdef
Search: (?s)<node-template>
Replace: <case>
Scope: *.sdef
Search: (?s)</node-template>
Replace: </case>
Scope: *.sdef
Search: (?s)<node-ref>
Replace: <node-include>
Scope: *.sdef
Search: (?s)</node-ref>
Replace: </node-include>
Scope: *.sdef
Search: (?s)<node-list-ref>
Replace: <node-include>
Scope: *.sdef
Search: (?s)</node-list-ref>
Replace: </node-include>
Jump action handler API has been improved slightly to make it less likely that a subclass will override
enablement logic from the parent class rather than augmenting it.
Before |
After |
public class CustomJumpActionHandler extends SapphireJumpActionHandler
{
@Override
public void refreshEnablementState()
{
setEnabled( ... );
}
}
|
public class CustomJumpActionHandler extends SapphireJumpActionHandler
{
@Override
protected boolean computeEnablementState()
{
if( super.computeEnablementState() == true )
{
return ...
}
return false;
}
}
|
The "show in source" action has been generalized to apply to other contexts besides the content outline.
This necessitated change of the id.
Before |
After |
Sapphire.Outline.ShowInSource
|
Sapphire.ShowInSource
|