Sapphire Developer Guide > Releases > 8.1

Enhancements in Sapphire 8.1

  1. Lazy Loading of Editor Pages
  2. ElementData

Lazy Loading of Editor Pages

For improved responsiveness, the loading of the model and an editor page should be deferred until the user accesses that page. Since the system needs to know the page name without loading the page definition, this is an opt-in enhancement. Existing editor implementations will continue to load all pages on editor startup until they are updated.

If SapphireEditorForXml class is used directly, the pageName attribute needs to be added to enable lazy loading.

<extension point="org.eclipse.ui.editors">
  <editor
    id="org.eclipse.sapphire.samples.catalog.CatalogEditor"
    name="Catalog Editor (Sapphire Sample)"
    icon="org/eclipse/sapphire/samples/SapphireFile.png"
    filenames="catalog.xml"
    default="true">
    <class class="org.eclipse.sapphire.ui.swt.xml.editor.SapphireEditorForXml">
      <parameter name="sdef" value="org.eclipse.sapphire.samples.catalog.CatalogEditor"/>
      <parameter name="pageName" value="Catalog"/>
      <parameter name="pageDefinitionId" value="CatalogEditorPage"/> <!-- Can be omitted if only one page definition exists in the sdef -->
    </class>
  </editor>
</extension>

If SapphireEditor class is extended, the subclass needs to use addDeferredPage() and possibly override getDefinition() or getDefinitionLoader().

public final class PurchaseOrderEditor extends SapphireEditor
{
    private StructuredTextEditor sourcePage;

    @Override
    protected void createEditorPages() throws PartInitException 
    {
        addDeferredPage( "General", "GeneralPage" );
        addDeferredPage( "Entries", "EntriesPage" );
        
        this.sourcePage = new StructuredTextEditor();
        this.sourcePage.setEditorPart( this );
        
        int index = addPage( this.sourcePage, getEditorInput() );
        setPageText( index, "Source" );
    }

    @Override
    protected Element createModel()
    {
        return PurchaseOrder.TYPE.instantiate( new RootXmlResource( new XmlEditorResourceStore( this, this.sourcePage ) ) );
    }
}
public abstract class SapphireEditor
{
    /**
     * Adds a page that will be loaded from its definition when the user first opens it. When this method
     * is used, the editor will load the page definition by calling {@link #getDefinition(String)}. By default,
     * the definitions are loaded from an sdef file with the same name as the editor class. If the default
     * behavior is inadequate, either {@link #getDefinitionLoader()} or {@link #getDefinition(String)} should be
     * overridden.
     *
     * @since 8.1
     * @param index the position of the page in the editor's page list
     * @param pageName the localizable name of the page
     * @param pageDefinitionId the id of the page definition
     * @throws IllegalArgumentException if index is less than -1 or more than current page count
     * @throws IllegalArgumentException if pageName is null
     */
    
    protected final void addDeferredPage( int index, String pageName, String pageDefinitionId )

    /**
     * Adds a page that will be loaded from its definition when the user first opens it. When this method
     * is used, the editor will load the page definition by calling {@link #getDefinition(String)}. By default,
     * the definitions are loaded from an sdef file with the same name as the editor class. If the default
     * behavior is inadequate, either {@link #getDefinitionLoader()} or {@link #getDefinition(String)} should be
     * overridden.
     *
     * @since 8.1
     * @param pageName the localizable name of the page
     * @param pageDefinitionId the id of the page definition
     * @throws IllegalArgumentException if pageName is null
     */
    
    protected final void addDeferredPage( String pageName, String pageDefinitionId )

    /**
     * Called when the editor should create its pages. The default implementation calls {@link #createSourcePages()},
     * {@link #createFormPages()} and {@link #createDiagramPages()} methods, in that order.
     * 
     * @since 8.1
     * @throws PartInitException if a page could not be created
     */
    
    protected void createEditorPages() throws PartInitException

    /**
     * Returns the definition loader to be used with this editor. The default implementation calls
     * DefinitionLoader.sdef( getClass() ) and returns the result. This will load the definition
     * from an sdef file with the same name as the editor class.
     * 
     * @since 8.1
     */
    
    protected DefinitionLoader getDefinitionLoader()

    /**
     * Returns the page definition corresponding to the specified id. The default implementation relies on
     * the {@link #getDefinitionLoader()} method.
     * 
     * @since 8.1
     * @param pageDefinitionId the page definition id or null to load the first page definition that's found
     */
    
    protected Reference<EditorPageDef> getDefinition( String pageDefinitionId )

    /**
     * Creates an editor page based on the page definition.
     *
     * @since 8.1
     * @param pageDefinitionId the page definition id
     * @return the created page
     * @throws IllegalArgumentException if the definition is not found
     */
    
    protected IEditorPart createPage( String pageDefinitionId )

    /**
     * Creates an editor page based on the page definition.
     *
     * @since 8.1
     * @param definition the page definition
     * @return the created page
     */
    
    protected IEditorPart createPage( Reference<EditorPageDef> definition )
}

ElementData

Sometimes one needs to represent in memory the content of an element without creating an element instance. Besides the issue of efficiency, some elements simply cannot be instantiated outside of their model because their services depend on the surrounding context. The new ElementData class provides a data structure for this purpose.

/**
 * A light-weight container for element content. It does not provide the rich facilities of an actual
 * element in a model, but can be useful when element content needs to be represented in memory without
 * adding it to a model (at least not immediately). 
 * 
 * @since 8.1
 */

public final class ElementData
{
    /**
     * Creates a new {@link ElementData} object.
     * 
     * @param type the element type
     * @throws IllegalArgumentException if element type is null
     */
    
    public ElementData( ElementType type )
    
    /**
     * Returns the element type.
     */
    
    public ElementType type()
    
    /**
     * Reads a property.
     * 
     * @param property the property name
     * @return the property content or null
     * @throws IllegalArgumentException if property is null
     */
    
    public Object read( String property )
    
    /**
     * Writes a property.
     * 
     * @param property the property name
     * @param content the property content
     * @throws IllegalArgumentException if property is null
     */
    
    public void write( String property, Object content )
}
public interface Element
{
    /**
     * Copies all properties from the provided source element to this element. The source element does not
     * have to be of the same type as target. Only properties that match on name and type will be copied.
     * 
     * @param source the element to copy from
     * @throws IllegalArgumentException if source is null
     * @throws IllegalStateException if this element or the source element is already disposed
     */
    
    void copy( Element source )
    
    /**
     * Copies all properties from the provided source element data to this element. The source element data
     * does not have to be of the same type as target. Any property that is not found in source or is of the wrong
     * type, will be cleared in target.
     * 
     * @since 8.1
     * @param source the element data to copy from
     * @throws IllegalArgumentException if source is null
     * @throws IllegalStateException if this element is already disposed
     */
    
    void copy( ElementData source )
}
public abstract class Property
{
    /**
     * Copies property content from the provided source element. The source element does not have to
     * be of the same type as target. The copy will happen if the source element has a property with
     * the same name and type as this property. Otherwise, no change will be performed.
     * 
     * @param source the element to copy from
     * @throws IllegalArgumentException if source is null
     * @throws UnsupportedOperationException if this property is read-only
     * @throws IllegalStateException if this property or the source element is already disposed
     */
    
    public abstract void copy( Element source )
    
    /**
     * Copies property content from the provided source element data. The source element data does not
     * have to be of the same type as target. Any property that is not found in source or is of the wrong
     * type, will be cleared in target.
     * 
     * @since 8.1
     * @param source the element to copy from
     * @throws IllegalArgumentException if source is null
     * @throws UnsupportedOperationException if this property is read-only
     * @throws IllegalStateException if this property is already disposed
     */
    
    public abstract void copy( ElementData source )
}