#22 Property Page
- Download:
- source code Project Files in Zip (117 KB)
- mp4 Full Size H.264 Video (40.3 MB)
- m4v Smaller H.264 Video (24.9 MB)
- webm Full Size VP8 Video (20.8 MB)
- ogv Full Size Theora Video (46 MB)
Implementing a property page instead of a preference page allows sharing settings between developers by just commiting corresponding settings files to the source code repository.
Non-UI plugin
Storing & retrieving properties
public class Properties { public static final String PROPERTY_TEXT = PLUGIN_ID + ".text"; public static final String PROPERTY_DEFAULT_TEXT = "Hello"; public static String getText(IProject project) { IEclipsePreferences node = new ProjectScope(project).getNode(PLUGIN_ID); return node.get(PROPERTY_TEXT, PROPERTY_DEFAULT_TEXT); } public static void storeText(IProject project, String text) throws BackingStoreException { IEclipsePreferences node = new ProjectScope(project).getNode(PLUGIN_ID); node.put(PROPERTY_TEXT, text); node.flush(); } }
Using our new Properties
class directly from the Xtext generator will course problems in the standalone or test environments, where some parts of the eclipse infrastructure may be missing. So we define an interface PropertiesProvider
with the default implementation that we can @Inject
into the Xtext generator. Later on we may define other implementations, like CommandLineArgumentBasedPropertiesProviderImpl
.
@ImplementedBy(PropertiesProviderImpl.class) public interface PropertiesProvider { public String getText(IProject project); }
public class PropertiesProviderImpl implements PropertiesProvider { @Override public String getText(IProject project) { return Properties.getText(project); } }
For the above code to compile we need to add following bundles to the MANIFEST.MF
:
Require-Bundle: ... ... org.eclipse.core.resources, org.eclipse.core.runtime
Using properties in the Xtext generator
Now we can customize the code generation using our new PropertiesProvider
. Note, that in the getIProject
method we use ?.
operator to prevent NullPointerException
s to be thrown. If for example findMember
method returns null
calling file?.project
on null
simply return null
.
class MyDslGenerator implements IGenerator { @Inject extension PropertiesProvider propertiesProvider override void doGenerate(Resource resource, IFileSystemAccess fsa) { fsa.generateFile('greetings.txt', ''' «FOR greeting : resource.contents.filter(Model).map[greetings].flatten» «getText(getIProject(greeting))» «greeting.name» «ENDFOR» ''') } def getIProject(EObject object) { val path = object.eResource.URI.toPlatformString(true) val file = ResourcesPlugin.workspace?.root?.findMember(path) as IFile file?.project } }
UI plugin (.addons)
It's a good practice to make non-Xtext related UI contributions within a separate plugin (here: org.xtext.example.mydsl.ui.addons
). This prevents complex merge conflicts between plugin.xml
and plugin.xml_gen
while e.g. upgrading Xtext.
We hook our property page under "MyDsl" property page by setting category to org.xtext.example.mydsl.MyDsl
.
We also require adapter of the type IProject
to prevent the property page to be shown for resources others then projects (e.g. files). Then we add a filter to show our property page only for Xtext project.
<extension point="org.eclipse.ui.propertyPages"> <page class="org.xtext.example.mydsl.ui.addons.properties.TextPropertyPage" id="org.xtext.example.mydsl.ui.addons.properties.samplePropertyPage" name="Text" category="org.xtext.example.mydsl.MyDsl" > <enabledWhen> <adapt type="org.eclipse.core.resources.IProject"/> </enabledWhen> <filter name="projectNature" value="org.eclipse.xtext.ui.shared.xtextNature"/> </page> </extension>
The implementation of the property page is straight forward and is mostly based on the code generated by the wizard.
public class TextPropertyPage extends PropertyPage { private static final String TEXT_TITLE = "Text:"; private static final int TEXT_FIELD_WIDTH = 50; private Text textText; private void addTextSection(Composite parent) { Composite composite = createDefaultComposite(parent); Label textLabel = new Label(composite, SWT.NONE); textLabel.setText(TEXT_TITLE); textText = new Text(composite, SWT.SINGLE | SWT.BORDER); GridData gd = new GridData(); gd.widthHint = convertWidthInCharsToPixels(TEXT_FIELD_WIDTH); textText.setLayoutData(gd); textText.setText(Properties.getText(getProject())); } private IProject getProject() { return (IProject) getElement().getAdapter(IProject.class); } /** * @see PreferencePage#createContents(Composite) */ protected Control createContents(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); composite.setLayout(layout); GridData data = new GridData(GridData.FILL); data.grabExcessHorizontalSpace = true; composite.setLayoutData(data); addTextSection(composite); return composite; } private Composite createDefaultComposite(Composite parent) { Composite composite = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(); layout.numColumns = 2; composite.setLayout(layout); GridData data = new GridData(); data.verticalAlignment = GridData.FILL; data.horizontalAlignment = GridData.FILL; composite.setLayoutData(data); return composite; } protected void performDefaults() { super.performDefaults(); textText.setText(Properties.PROPERTY_DEFAULT_TEXT); } public boolean performOk() { try { Properties.storeText(getProject(), textText.getText()); return true; } catch (Exception e) { return false; } } }
Settings file
The project property settings get stored withing project/.settings/
directory and can be shared together with the source code:
eclipse.preferences.version=1 org.xtext.example.mydsl.text=Hello
Version information
- Eclipse: 4.3 (Kepler)
- Xtext: 2.4.2