Jump to: navigation, search

Extending Sapphire Wizard

Overview

Sapphire does a great deal of automated work behind the scenes for us in creating Wizards, Editors and Dialog boxes. Sometimes there might be some requirement for us in extending Sapphire and providing the registrations via plugin.xml, the Sapphire source distribution has lots of good examples which help us in creating the such Wizards, editors and dialogs. The examples show us how to invoke them from command handlers org.eclipse.ui.handlers extensions, in this article we can see how to extend the Sapphire Wizard and register our custom wizard via plugin.xml, thereby allowing us to Categorize our Wizards and invoke them using the Workbench New Wizard dialog.

For the demonstration purpose I will take a factitious example where will create a Wizard that will help us in creating a new Sapphire model interface that extends IModelElement


Lets start with our model elements definition,

Model

ISapphireModelDef .java

@GenerateImpl
@XmlRootBinding(elementName = "sapphire-model")
public interface ISapphireModelDef extends IExecutableModelElement {
 
    ModelElementType TYPE = new ModelElementType(ISapphireModelDef.class);
 
    // *** Package ***
 
    @Label(standard = "Model Package")
    @XmlBinding(path = "model-package")
    ValueProperty PROP_MODEL_PACKAGE = new ValueProperty(TYPE, "ModelPackage");
 
    Value<String> getModelPackage();
 
    void setModelPackage(String value);
 
    // *** ModelName ***
 
    @Label(standard = "Model Name")
    @XmlBinding(path = "model-name")
    ValueProperty PROP_MODEL_NAME = new ValueProperty(TYPE, "ModelName");
 
    void setModelName(String value);
 
    void setModelName(JavaTypeName value);
 
    // *** ValueProperties ***
 
    @Type(base = ISModelValueProperty.class)
    @Label(standard = "Value Properties")
    @XmlListBinding(mappings = { @XmlListBinding.Mapping(element = "", type = ISModelTypedValueProperty.class) })
    ListProperty PROP_VALUE_PROPERTIES = new ListProperty(TYPE,
            "ValueProperties");
 
    ModelElementList<ISModelValueProperty> getValueProperties();
 
    // *** Method: execute ***
 
    @DelegateImplementation(ISapphireModelDefOpMethods.class)
    Status execute(ProgressMonitor monitor);
 
}
ISModelPropertyBase.java
@GenerateImpl
public interface ISModelPropertyBase extends IModelElement {
 
    ModelElementType TYPE = new ModelElementType(ISModelPropertyBase.class);
 
    // *** Name ***
 
    @Label(standard = "Name")
    @XmlBinding(path = "name")
    ValueProperty PROP_Name = new ValueProperty(TYPE, "Name");
 
    Value<String> getName();
 
    void setName(String value);
 
    // *** Label ***
 
    @Label(standard = "Label")
    @XmlBinding(path = "label")
    ValueProperty PROP_LABEL = new ValueProperty(TYPE, "Label");
 
    Value<String> getLabel();
 
    void setLabel(String value);
 
    // *** XmlBinding ***
 
    @Label(standard = "Xml Binding")
    @XmlBinding(path = "xml-binding")
    ValueProperty PROP_XML_BINDING = new ValueProperty(TYPE, "XmlBinding");
 
    Value<String> getXmlBinding();
 
    void setXmlBinding(String value);
 
}
ISModelPropertyBase.java
@GenerateImpl
public interface ISModelTypedValueProperty extends ISModelPropertyBase {
    ModelElementType TYPE = new ModelElementType(
            ISModelTypedValueProperty.class);
 
}

ISModelValueProperty.java

@GenerateImpl
public interface ISModelValueProperty extends ISModelPropertyBase {
 
    ModelElementType TYPE = new ModelElementType(ISModelValueProperty.class);
 
 
}
ISapphireModelDefOpMethods.java
public final class ISapphireModelDefOpMethods
{
    public static final Status execute( final ISapphireModelDef context,
                                        final ProgressMonitor monitor )
    {
        // Do something here.
 
        return Status.createOkStatus();
    }
}

Sapphire UI Definition

<?xml version="1.0" encoding="UTF-8"?>
<definition>
    <import>
        <package>org.eclipse.sapphire.model</package>
    </import>
    <wizard>
        <id>new.sapphire.model.creation.wizard</id>
        <label>New Sapphire Model</label>
        <description>This wizard will allow the creation of New Sapphire Models</description>
        <image>icons/sapphire-32.png</image>
        <page>
            <id>basic.properties</id>
            <label>Model Basic Properties</label>
            <description>Define the basic properties of the model like name, package etc.,</description>
            <content>
                <property-editor>ModelPackage</property-editor>
                <property-editor>ModelName</property-editor>
            </content>
        </page>
        <page>
            <id>new-sapphire-model.valueproperties.page</id>
            <label>Value Properties</label>
            <description>Define all value properties for the model</description>
            <content>
                <property-editor>ValueProperties</property-editor>
            </content>
        </page>
    </wizard>
</definition>

We have now defined our model and defined the Sapphire UI definition we can now create our Custom Wizard classes and register it via plugin.xml

Wizard Classes

SapphireNewWizard.java
public abstract class SapphireNewWizard<M extends IExecutableModelElement>
        extends SapphireWizard<M> implements INewWizard {
 
    private IWorkbench workbench;
    private IStructuredSelection selection;
 
    public SapphireNewWizard(M modelElement, String wizardDefPath) {
        super(modelElement, wizardDefPath);
        setNeedsProgressMonitor(true);
    }
 
    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench,
     * org.eclipse.jface.viewers.IStructuredSelection)
     */
    @Override
    public void init(IWorkbench workbench, IStructuredSelection selection) {
        this.workbench = workbench;
        this.selection = selection;
 
    }
 
}
Note.png
' Note:'
The SapphireNewWizard apart from extending from SapphireWizard<M> it should implement org.eclipse.ui.INewWizard, otherwise a classcast exception will be thrown by the Workbench when invoking the Wizard


NewSapphireModelCreationWizard.java
public class NewSapphireModelCreationWizard extends
        SapphireNewWizard<ISapphireModelDef> {
 
    public final static String ID = "org.eclipse.sapphire.ui.wizards.NewSapphireModelCreationWizard";
    private final static String WIZARD_DEF_PATH = "com.sk.contentassist.demo/org/eclipse/sapphire/ui/wizards/sapphire-model-wizards.sdef!new.sapphire.model.creation.wizard";
    private final static ISapphireModelDef modelElement = ISapphireModelDef.TYPE
            .instantiate();
 
    /**
     * @param modelElement
     * @param wizardDefPath
     */
    public NewSapphireModelCreationWizard() {
        super(modelElement, NewSapphireModelCreationWizard.WIZARD_DEF_PATH);
    }
 
    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.sapphire.ui.swt.SapphireWizard#performPostFinish()
     */
    @Override
    protected void performPostFinish() {
        try {
            modelElement.resource().save();
        } catch (ResourceStoreException e) {
            SapphireUiFrameworkPlugin.log(e);
        }
    }
 
}

Now its the time to register the wizard via the plugin.xml as shown below

plugin.xml

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
 <extension
         point="org.eclipse.ui.newWizards">
      <category
            id="com.sk.contentassist.demo.sapphire.wizard"
            name="Sapphire">
      </category>
      <wizard
            canFinishEarly="false"
            category="com.sk.contentassist.demo.sapphire.wizard"
            class="org.eclipse.sapphire.ui.wizards.NewSapphireModelCreationWizard"
            icon="icons/sapphire-32.png"
            id="org.eclipse.sapphire.ui.wizards.NewSapphireModelCreationWizard"
            name="New Sapphire Model">
      </wizard>
   </extension>
</plugin>

Create Workspace Resource Wizard

As an IModelElement cannot be hooked up to a Resource (see here) after it has been created, here is a little custom Wizard to create a WorkspaceResource.

SapphireCreateWizard.java
public abstract class SapphireCreateWizard<M extends IModelElement> extends Wizard implements INewWizard {
	//TODO This class could inherit from org.eclipse.sapphire.ui.swt.SapphireWizard, but addPages and performFinish are final
 
	//Sapphire specific
	private final M element;
	private final ISapphireWizardDef definition;
	private final SapphireWizardPart part;
	private final SapphirePartListener listener;
 
	//Wizard specific
	private IStructuredSelection selection;
	private WizardNewFileCreationPage createFilePage;
 
	/**
	 * @param element
	 * @param wizardDefPath
	 */
	public SapphireCreateWizard(M element, final String wizardDefPath) {
		this.element = element;
		definition = SapphireUiDefFactory.getWizardDef(wizardDefPath);
 
		part = new SapphireWizardPart();
		part.init(null, element, definition, Collections.<String, String> emptyMap());
 
		setWindowTitle(this.part.getLabel());
 
		listener = new SapphirePartListener() {
			@Override
			public void handleEvent(final SapphirePartEvent event) {
				if (event instanceof ImageChangedEvent) {
					refreshImage();
				}
			}
		};
 
		part.addListener(listener);
		refreshImage();
		setNeedsProgressMonitor(true);
	}
 
	@Override
	public void addPages() {
		// If the Wizard wasn't called via Strg+n
		if (selection == null)
			selection = new StructuredSelection();
 
		// Add WizardNewFileCreationPage
		String title = "Create " + element.getClass().getSimpleName();
		createFilePage = new WizardNewFileCreationPage(title, (IStructuredSelection) selection);
		addPage(createFilePage);
 
		// Add declared sapphire pages
		for (SapphireWizardPagePart pagePart : part.getPages())
			addPage(new SapphireWizardPage(pagePart));
	}
 
	@Override
	public boolean performFinish() {
		// Create file, WorkspaceResourceStore and copy content to
		// RootXmlResource handle by the store
		try {
			IFile file = createFilePage.createNewFile();
			try {
				XmlResourceStore store = new XmlResourceStore(new WorkspaceFileResourceStore(file));
				RootXmlResource resource = new RootXmlResource(store);
				M newElement = element.getModelElementType().instantiate(resource);
				copyContents(element, newElement);
				store.save();
			} catch (ResourceStoreException e) {
				e.printStackTrace();
			}
			element.resource().save();
		} catch (ResourceStoreException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
 
	private final void refreshImage() {
		setDefaultPageImageDescriptor(toImageDescriptor(this.part.getImage()));
	}
 
	@Override
	public void init(IWorkbench workbench, IStructuredSelection selection) {
		this.selection = selection;
	}
 
	/**
	 * <p>Found no way how to copy contents from one to another IModelElement,
	 * so this method will do the work instead</p>
	 * @param source
	 * @param destination
	 */
	abstract protected void copyContents(M source, M destination);
 
}

For a reference implementation you can check this class.

Note.png
' Note:'
The SapphireCreateWizard doesn't extend SapphireWizard<M>, because addPages and performFinish are declared as final. That leads to code repetition, which should be avoided.


Conclusion

And there you go, when you start your Eclipse Plug-in, you should see a New Sapphire Model in the new wizards dialog under the Sapphire category. Now we have Sapphire based Wizard that can be invoked using the Workbench "New Wizard" dialog