| chapter:FirstExample[First Example] |
| |
| The purpose of this first example is to make use of the classical EMF Library Model example and |
| create a view for editing such models using an EMF Parsley enabled plug-in. We will use |
| one of the saveable views shipped with Parsley: a Tree Form View. |
| |
| So let's start by creating the model plug-in with |
| |
| ol[ |
| item[File -> New... -> Example...] |
| item[from Category "Eclipse Modeling Framework", select "Extended Library Model Example"] |
| item[press Next and Finish] |
| ] |
| |
| You will end up with three plug-ins: |
| |
| ul[ |
| item[org.eclipse.emf.examples.library (the model plug-in)] |
| item[org.eclipse.emf.examples.library.edit (the edit plug-in)] |
| item[org.eclipse.emf.examples.library.editor (the editor plug-in)] |
| ] |
| |
| The editor plug-in project can be removed. |
| |
| Please consider that here we are starting from this well known EMF model taken out-of-the-box from Eclipse, |
| but you can start from your EMF model (in that case you may probably omit the ".edit" and ".editor" plugins, depending on your model). |
| |
| Now you can create your first example with the appropriate wizard. |
| |
| ol[ |
| item[select "File" -> "New" -> "Project..."] |
| item[from the "Emf Parsley" category select "Emf Parsley Dsl based Project" |
| |
| img[images/01-new-project-dsl-wizard.png][][ ][] |
| |
| ] |
| item[click "Next"] |
| item[give a name to the project (e.g. "org.eclipse.emf.parsley.examples.firstexample") |
| and make sure the checkbox about using one of the templates is checked |
| |
| img[images/01-new-project-dsl-wizard2.png][][ ][] |
| |
| ] |
| item[click "Next"] |
| item[Select "Saveable Tree Form View" |
| |
| img[images/01-new-project-dsl-wizard3.png][][ ][] |
| |
| ] |
| item[click "Finish"] |
| ] |
| |
| The generated project has some classes and a e[module.parlsey] file, which opens automatically: |
| |
| code[EmfParsley][ |
| import org.eclipse.emf.parsley.examples.firstexample.FirstexampleSaveableTreeFormView |
| |
| /* org.eclipse.emf.parsley.examples.firstexample Emf Parsley Dsl Module file */ |
| module org.eclipse.emf.parsley.examples.firstexample { |
| |
| parts { |
| viewpart org.eclipse.emf.parsley.examples.firstexample { |
| viewname "Firstexample" |
| viewclass FirstexampleSaveableTreeFormView |
| } |
| } |
| |
| configurator { |
| resourceURI { |
| FirstexampleSaveableTreeFormView -> { |
| // TODO create and return a org.eclipse.emf.common.util.URI |
| return null; |
| } |
| } |
| } |
| |
| resourceManager { |
| initializeResource { |
| // Optional: initialize an empty Resource |
| // 'it' is of type Resource |
| // e.g., it.getContents += myFactory.createMyClass |
| } |
| } |
| } |
| ] |
| |
| The e[viewpart] corresponds to the standard Eclipse view part extension point; the Parsley |
| DSL will handle the generation and update of the e[plugin.xml] file. |
| Please have a look at ref:PluginXml[How the DSL handles the plugin.xml] for further |
| details. |
| |
| The wizard will also generate a view part class into the project |
| (in this example, e[FirstexampleSaveableTreeFormView]); you can add other controls |
| into that view, or customize other behaviors. Note that the Parsley DSL will never |
| touch the files into the e[src] source folder. On the contrary, files generated into |
| e[emfparsley-gen] source folder must never be manually modified, since its contents will |
| be regenerated each time you modify the e[module.parsley] file. |
| |
| For example, let's change the view name into "My Library Tree Form": |
| |
| code[EmfParsley][ |
| ... |
| module org.eclipse.emf.parsley.examples.firstexample { |
| |
| parts { |
| viewpart org.eclipse.emf.parsley.examples.firstexample { |
| viewname "My Library Tree Form" |
| ... |
| ] |
| |
| Let's save the file, wait for Eclipse to rebuild, and update the |
| e[plugin.xml] with the new e[plugin.xml_emfparsley_gen]. |
| |
| (Other e[viewpart] sections can be created; content assist is available for that). |
| |
| In the generated e[module.parsley], there is a e[configurator] section, with |
| a e[TODO] comment (The e[Configurator] is detailed in the section ref:Configurator[Configurator]): |
| |
| code[EmfParsley][ |
| configurator { |
| resourceURI { |
| FirstexampleSaveableTreeFormView -> { |
| // TODO create and return a org.eclipse.emf.common.util.URI |
| return null; |
| } |
| } |
| } |
| ] |
| |
| Let's focus on the above e[resourceURI]: our goal is allowing to manage |
| a library model instance which persists on a EMF codeRef[org.eclipse.emf.ecore.resource.Resource]. |
| So we must specify the codeRef[org.eclipse.emf.common.util.URI] of the resource |
| that will be edited by our tree form view. |
| In this example we choose to use the EMF default persistence (XMI), but you can provide any URI |
| (e.g. using Teneo, CDO or any other EMF Resource Persistence implementation) |
| In particular here we choose to persist the Resource in a XMI file named e["MyLibrary.library"] |
| into the user home folder (you might want to change it with any other path). |
| To achieve this, we just need to create such a URI (recall that content assist is available |
| when typing Xbase expressions): |
| |
| code[EmfParsley][ |
| configurator { |
| resourceURI { |
| FirstexampleSaveableTreeFormView -> { |
| return URI.createFileURI( System.getProperty("user.home") + "/MyLibrary.library" ); |
| } |
| } |
| } |
| ] |
| |
| If you simply copy and paste the above return statement, you'll get an error about |
| unresolvable Java type URI; "Organize Imports" context menu (or its shortcut "Ctrl+Shift+O") |
| can be used to automatically add the missing import (make sure you select |
| codeRef[org.eclipse.emf.common.util.URI]). |
| |
| Note that the specified URI, will be used for loading the resource only for our specific |
| view (the resource will be automatically created if it does not exist). |
| |
| In the e[module.parsley] there is another section that has been generated by the wizard: |
| |
| code[EmfParsley][ |
| resourceManager { |
| initializeResource { |
| // Optional: initialize an empty Resource |
| // 'it' is of type Resource |
| // e.g., it.getContents += myFactory.createMyClass |
| } |
| } |
| ] |
| |
| We can use this section to initialize our resource when it is empty |
| (i.e., the first time the resource is created); (The e[resourceManager] |
| is detailed in section ref:ResourceManager[Resource Manager]). |
| |
| In this example, we want to initialize an empty resource with a |
| e[Library] object; so |
| we first need a e[Dependency] from the model plug-in: so open e[MANIFEST.MF] file, go to e[Dependencies] |
| tab, press e["Add..."] button in e["Required Plug-ins"] section and insert e["org.eclipse.emf.examples.library"] |
| among dependencies. |
| |
| Now we can implement the e[initializeResource] method |
| (as described in the comment, the codeRef[org.eclipse.emf.ecore.resource.Resource] |
| to initialized is available through the parameter e[it]); the Library object |
| is created using the standard EMF API: we need the factory of the library model: |
| |
| code[EmfParsley][ |
| resourceManager { |
| initializeResource { |
| it.getContents += EXTLibraryFactory.eINSTANCE.createLibrary |
| } |
| } |
| ] |
| |
| Again, use the content assist while typing, e.g., for automatically importing the |
| type e[EXTLibraryFactory], or use the "Organize Imports" functionality. |
| |
| Now, we are ready to execute this example: |
| let's get back to the e[MANIFEST.MF] and run the example |
| |
| img[images/first-example-launch.png][][ ][] |
| |
| As an Eclipse RCP developer you know, of course, that this will start another Eclipse instance (unless |
| you add an Application plug-in to the launch or define an Application in the current plug-in). |
| |
| In this second Eclipse instance you can show the View in this way: |
| ol[ |
| item[e[Window -> Show View -> Other...]] |
| item[from Category "Other", select "My Library Tree Form"] |
| item[press e[OK]] |
| ] |
| |
| img[images/first-example-run.png][][ ][] |
| |
| With this simple view you can start editing the model instance. For example you can set the e["name"] |
| field; as soon as you start typing characters into this field you will notice that: |
| ol[ |
| item[the View turns to a e["dirty"] state (an asterisk symbol appears on the view tab)] |
| item[the e["Save"] toolbar button is enabled] |
| item[the typed characters are reflected into the label correspondent to the Library icon] |
| ] |
| |
| if you now perform a e["Save"] action the persistence mechanism will trigger and you will see that file |
| code[<user.home>/MyLibrary.library] |
| is being created on the file system. From now on, this file will keep the state of the model object whenever |
| you change and save it. |
| |
| To create a Writer into the Library just right-click on the Library object and select e[New Child -> Writer] |
| |
| img[images/createWriter.png][][ ][] |
| |
| Please note that you might see a slightly different content in the above context-menu in case you deleted |
| the .edit plugin when creating the model (e.g. e["Writers Writer"] instead of e["Writer"], e["Stock Book"] instead of e["Book"] and |
| similar (this is because with EMF it is possible to customize labels also via .edit plugin). |
| |
| Now set for instance the writer e["name"] field and save. |
| Now just play around creating Books, associating them to Writers and so on. |
| As you can see you can entirely manage the EMF model instance: creating, modifying and deleting elements. |
| |
| Whenever the current selection on the upper side of the view changes, then the lower side shows the detail |
| of this selection. |
| |
| img[images/first-example-default.png][][ ][] |
| |
| However, up to this point, you have no control over the field to be shown and its order; for example |
| you may want just the e["name"] attribute for the Library and e["name", "address" and "books"] attributes |
| for Writers and maybe e["title", "authors" and "category"] for Books. |
| |
| Well, it's indeed very easy to obtain this: just edit the e[module.parsley] file, |
| adding the following import (without ending line with ";") |
| |
| code[EmfParsley][ |
| import org.eclipse.emf.examples.extlibrary.* |
| ] |
| |
| and then defining the features to show (the e[featuresProvider] is detailed |
| in section ref:FeaturesProvider[Features Provider]): |
| |
| code[EmfParsley][ |
| module ... { |
| |
| ... |
| featuresProvider { |
| features { |
| Library -> name |
| Writer -> name, address, books |
| Book -> author, title, category |
| } |
| } |
| } |
| ] |
| |
| Remeber that code completion is available, just exploit it since it helps a lot. |
| |
| If you restart now the application you will see that, when selecting an object, only the features |
| specified in the above section will be shown for each specified classes. |
| Furthermore, they are shown in the specified order. |
| |
| NOTE: Did you run the application in Debug mode? Well, then you can change fields and order, save and see the |
| changes without even restarting the application. |
| |
| Do you want to change text used for attribute captions in the form for a specific |
| class? Just add the following |
| (e[featureCaptionProvider] is detailed in section ref:FeatureCaptionProvider[Feature Caption Provider]): |
| |
| code[EmfParsley][ |
| ... |
| featureCaptionProvider { |
| text { |
| Book : author -> "Written by:" |
| Writer : name -> "Name:" |
| } |
| } |
| ] |
| |
| Or do you want to change the label shown on the tree nodes on the upper side and as detail title? |
| Maybe want to format the book label like this? |
| (e[labelProvider] is detailed in section ref:ViewerLabelProvider[Viewer Label Provider]): |
| |
| code[EmfParsley][ |
| ... |
| labelProvider { |
| text { |
| Book b -> { '"' + b.title + '"' } |
| Writer w -> { w.name } |
| } |
| } |
| ] |
| |
| The result of all the above customizations is shown in the following screenshot |
| (compare it with the previous screenshot): |
| |
| img[images/first-example-customized.png][][ ][] |
| |
| Now, let's customize the context menus; by default, |
| Parsley will generate context menus using EMF.Edit: |
| |
| img[images/first-example-default-menus.png][][ ][] |
| |
| We will now customize the context menu for books and writers, using |
| the e[menuBuilder] in the DSL (context menu customization is detailed |
| in section ref:ContextualMenu[Contextual Menu]). |
| |
| What we want to achieve is to have a context menu for a e[Writer] |
| to add a new book in the library, and set its author to the |
| selected writer (similarly, we want a context menu for a e[Book] |
| to add a new writer in the library, and set the selected book as |
| one of the new writer's books): |
| |
| code[EmfParsley][ |
| ... |
| menuBuilder { |
| val factory = EXTLibraryFactory.eINSTANCE |
| |
| emfMenus { |
| Writer w -> #\[ |
| actionChange("New book", w.eContainer as Library, |
| \[ |
| library | |
| val book = factory.createBook |
| library.books += book |
| book.title = "A new book" |
| book.author = w |
| \] |
| ), |
| \] |
| Book b -> #\[ |
| actionChange("New writer", b.eContainer as Library, |
| \[ |
| library | |
| val writer = factory.createWriter |
| library.writers += writer |
| writer.name = "A new writer" |
| writer.books += b |
| \] |
| ) |
| \] |
| } |
| } |
| ] |
| |
| In this code we use Xbase features like list literals (e[#\[...\]]) and |
| lambda expressions. |
| We use e[actionChange] that allows to specify a menu performing some actions |
| on the model's elements, keeping track of such changes so that they can be |
| undone -- redo will work as well. The implementation of the menu is specified |
| in the lambda expression passed as the last argument of actionChange; this lambda |
| will receive as argument the model's element specified as the second argument. |
| Only those modifications performed in the lambda concerning such specified model's element |
| will be recorded for undo/redo. |
| |
| If you now restart the application, you see that the new |
| context menu appears on writer elements: |
| |
| img[images/first-example-custom-menus1.png][][ ][] |
| |
| And selecting such a menu on a writer will add a new book, with |
| a title, and whose author is the selected writer: |
| |
| img[images/first-example-custom-menus2.png][][ ][] |
| |
| You may want to try the new context menu on a book as well. |
| |
| We also add another context menu for books, using e[actionAdd]: |
| specifying the label for the menu, |
| the containment list in the model, the object to add in such list |
| and a lambda expression that will be executed ONLY after the menu |
| has been selected. In this example, this menu available for |
| a book object will add a new book to the library with the same |
| name of the selected book: |
| |
| code[EmfParsley][ |
| ... |
| menuBuilder { |
| val factory = EXTLibraryFactory.eINSTANCE |
| |
| emfMenus { |
| // ... as above |
| Book b -> #\[ |
| actionChange( |
| // ... as above |
| ), |
| actionAdd("New book (same title)", |
| (b.eContainer as Library).books, |
| factory.createBook, |
| \[title = b.title\] |
| ) |
| \] |
| } |
| } |
| ] |
| |
| Now, let's customize the contents shown in the tree view: by default, |
| as you can see from the previous screenshots, the tree will show all |
| the contents of the library. If we want to show only the writers and |
| the books we can specify this section in the DSL |
| (the customization of the content provider is detailed in |
| section ref:ViewerContentProvider[Viewer Content Provider]): |
| |
| code[EmfParsley][ |
| ... |
| viewerContentProvider { |
| children { |
| Library -> { |
| writers + books |
| } |
| } |
| } |
| ] |
| |
| and the result can be seen in the following screenshot: |
| |
| img[images/first-example-custom-contents1.png][][ ][] |
| |
| By default, double-clicking on a tree viewer of a saveable view will |
| show a dialog to edit that object (if you customized the |
| e[featuresProvider], the dialog will use your customized version); |
| by default, if you edit a field in such dialog, the modifications will |
| be applied immediately to the resource: this can be seen in the |
| labels of the tree which are automatically updated and in the dirty state of |
| the view: |
| |
| img[images/first-example-default-dialog.png][][ ][] |
| |
| Such a strategy for editing is delegated to an injected |
| codeRef[org.eclipse.emf.parsley.edit.IEditingStrategy], which is |
| implemented by default by codeRef[org.eclipse.emf.parsley.edit.OnTheFlyEditingStrategy]. |
| |
| One may want to avoid this automatic update of the resource, and |
| have the changes applied only when the "OK" dialog button is pressed |
| (if "Cancel" is pressed, no changes should be applied at all). |
| To achieve this behavior, it is enough to bind the alternative implementation |
| codeRef[org.eclipse.emf.parsley.edit.UndoableEditingStrategy], in the Guice module. |
| This can be achieved in the DSL using the e[binding] section |
| (Guice bindings are detailed in section ref:GuiceBindings[Guice Bindings]): |
| |
| code[EmfParsley][ |
| ... |
| bindings { |
| type IEditingStrategy -> UndoableEditingStrategy |
| } |
| ] |
| |
| We strongly suggest you use the content assist to discover default |
| bindings, since they also show Javadoc for each default binding: |
| |
| img[images/first-example-custom-binding1.png][][ ][] |
| |
| Besides types, you can also bind (i.e., inject) specific values |
| that are used in the framework; for example, you can change the |
| orientation of the tree form sash as follows |
| |
| code[EmfParsley][ |
| ... |
| bindings { |
| type IEditingStrategy -> UndoableEditingStrategy |
| value int TreeFormSashStyle -> SWT.HORIZONTAL |
| } |
| ] |
| |
| and see the result: |
| |
| img[images/first-example-custom-orientation.png][][ ][] |
| |
| This ends the first tutorial. |