Exercise 1. Code Generation, Regeneration, and Merge
What This Exercise Is About
You will build a
model and editor from an XML Schema document. Once the editor is generated,
you will have an opportunity to explore the regeneration and merge
capabilities of EMF by tweaking the code code for the editor and regenerating
it to see the results. The schema you will use describes a fairly simple model
for purchase orders. It is equivalent to the following UML class diagram,
which depicts the class structure of the application.

The schema also
describes the details of an XML serialization for pruchase orders, which could
be used for storage or transmission. The model code generated in this exercise
will also be resued in exercises 2 to 4.
What You Should Be Able To Do
At the end of the
lab, you should be able to:
- Import a model
into Eclipse using EMF
- Generate a
model editor
- Use the generated editor to create an instance of the model
- Modify the
generated editor code to implement UI changes without losing those changes when regenerating
Required Materials
- Eclipse 3.2
- Eclipse Modeling Framework (EMF) 2.2
General Advice / Warnings
- Eclipse conventions suggest naming plugin projects with a fully qualified plugin ID, such as com.example.po. Do not use dashes in project names - this will create invalid entries in your MANIFEST.MF file.
- Make sure you're using the IBM JDK or else have implemented the Sun JDK Crimson DOM bug workaround (http://eclipse.org/emf/downloads-xerces.php)
- For more on XML Schema:
Exercise Instructions
This exercise is
carried out entirely using the Eclipse Software Development Kit (SDK)
version 3.2 with the Eclipse Modeling Framework (EMF) 2.2 installed
into it. The exercise instructions refer to this product as either
Eclipse or as "the workbench."
These instructions
are located in your workspace, in the EMF_Workshop/Exercise1_CodeGenRegen_Merge
folder. If you browse into this location by expanding the folders, you should find two
additional files: PurchaseOrder.xsd and PurchaseOrder.mdl. These contain two different
representations of the purchase order model: the XML Schema document that you will use
in this exercise and the Rose model pictured above. You can open the schema in a text
editor or the Sample XML Schema Editor to see how it presents the same information
as the class diagram.
The full solution to this exercise is available in the EMF_Workshop/Solution1
folder. This folder contains the four complete projects that you will generate in this exercise, as
well as a data folder with the instance document you will create in the
generated editor.
Directions
Step A: Verify workbench configuration
- Ensure Eclipse
is running, and that EMF is properly installed.
- Bring up the
Help -> About Eclipse SDK dialog.
- Click on
Feature Details. Click the Feature ID
column heading to order the features by that field. Then, check
that at least the following EMF features are listed:
- org.eclipse.emf
- org.eclipse.emf.source
- org.eclipse.xsd
- org.eclipse.xsd.source
- Ensure that the JDK used by Eclipse is valid.
- Bring up the
"Help/About Eclipse Platform" dialog.
- Click on
"Configuration Details". Scroll down to where eclipse.vm
is shown. This should be something like:
eclipse.vm=c:\ibm-java2-1.4\bin\java,
eclipse.vm=/opt/ibm-java2-1.4/bin/java, or similar
- If your Eclipse install has defaulted to using the Sun JDK 1.4, you must use a different one to avoid a bug in the Crimson DOM
when importing from XSD. The following JDKs will work:
- IBM JDK 1.4.2sr2 or later
- IBM JDK 5.0
- Sun JDK 5.0
Step B: Import the model
There are three supported sources for importing a model into EMF: XML Schema (XSD), annotated Java and
UML (Rational Rose). You can create an Ecore model and generate code from any of these, but in this
exercise you will use a schema.
- Switch to the
Plug-in Development perspective, if not already there.
- Select Window
-> Open Perspective -> Other... -> Plug-in Development.
- In the Package Explorer view, browse to the Exercise1_CodeGen_Regen_Merge
folder and select the file PurchaseOrder.xsd.
- Create a new
EMF Project called com.example.po.
- Right-click and select New -> Project... (or hit CTRL-N). You can also select New -> Project... from the File menu.
- Expand the twisty next to Eclipse Modeling Framework and select EMF Project. Hit Next.
- Name the
project com.example.po and hit Next.
- Select XML Schema. Hit Next.
- Hit Load to load the schema file that's been prefilled for you. Hit Next.
- Because the model is specified in one schema with a single target namespace, its Ecore representation is a single package. Notice that this package, po, has been selected. Hit Finish.
- Expand the
generator model, PurchaseOrder.genmodel, in the EMF Generator.
- Walk the tree
to see what was imported. Contrast with the XML Schema source file.
- Rename the Po node to PO so that the generated classes (for the package, factory, resource implementation, eeditor, etc.) will have more conventional names.
- Select the Po node, right-click, and select Show Properties View (if this view is not already shown).
- Scroll the Properties view to find the All twisty. Underneath is the Prefix property. Set that property to PO instead of Po.
- Save your changes.

Step C: Generate Editor Code
- Generate all code from the genmodel file.
- Open the created genmodel file (if not
already opened by Eclipse).
- Select the topmost node PurchaseOrder. Make sure you select the genmodel root, not a package or class below in the tree, as the selected node will determine what artifacts are generated. The benefit here is that if you later make changes that affect just a single class, you can regenerate only the relevant code, which is faster than regenerating everything.
- Right-click and select Generate All.

- Three new projects
will be created: com.example.po.edit (edit support
code not dependent on Eclipse), com.example.po.editor
(editor code dependent on Eclipse), and com.example.po.tests
(JUnit test skeletons for
exercising any volatile features and operations defined in the
model). Additionally, new classes and packages will
be created within your com.example.po project.
- The code should be compiled
automatically as it is generated, and should recompile whenever it
is changed. If you have disabled automatic building in the
workbench preferences, you can initiate compilation manually by
selecting Build All from the Project menu.
- Observe the Problems view. There should be warnings about code
never being used locally. These stubs will be used by tests that you could write to test your model,
but that is out of the scope of this exercise. You can safely ignore these warnings.

Step D: Create a Model Instance (Run Generated Model Editor Code)
All steps below will be done in a second, testing workbench, or Eclipse Application workbench.
- Launch a new Eclipse Application to try out your editor.
- Select your plugin project com.example.po in the Package Explorer.
- Select Run As/Eclipse Application from the Run toolbar drop-down or menu. Note that the Run menu is context-sensitive, so options available under Run As will change depending on where your cursor is (which view/editor and what file type).
- Once it opens, close or minimize the Welcome view.
- Select About Eclipse SDK from the Help menu, click on Plug-in Details,
and look for the contributed PurchaseOrder plugins, com.example.po, com.example.po.edit, com.example.po.editor, and com.example.po.tests.
- The PurchaseOrder Model wizard can now be used to create a new instance of the
model.
- Bring up the File/New/Project... dialog.
- Expand General and select Project. Click the Next button.
- Give the project a name, such as po, and click the Finish button.
- Right-click the project and select New/Other... from the pop-up menu.
- Expand Example EMF Model Creation Wizards and select PO Model.
Click the Next button.
- Enter a file name for the PurchaseOrder model, such as PO.po. Make sure it ends with
a .po extension. Then, click the Next button.
- Select Order as the model object and click the Finish button.
- The newly created PurchaseOrder model is opened in an editor.
- The root object in this editor corresponds to the PO.po resource. Under it lies a DocumentRoot, which acts as a container for the single order document element corresponding to the PurchaseOrder object.
- The steps below will show you how to use the model editor to create instance data. You can also skip this step by copying Solution1/data/PO.po from your first workbench to your second, then editing the resulting file if desired.
- Expand the platform:/resource/po/PO.po resource and the Document Root to see the
Purchase Order object.
- If the Properties view isn't already showing, right-click the PurchaseOrder
object and select Show Properties View from the pop-up menu.
- In the Properties view, click on the Value column of the Comment property,
and provide a comment for the purchase order. The label in the editor will be updated when
you hit Enter.
- Right-click the order and select New Child/Item from the pop-up menu.
A new Item is added to the purchase order. Note that the correct syntax for the SKU (Part Num) is /\d{3}-[A-Z]{2}/, or three digits, a dash, then two uppercase letters.
- You can copy the file data/PO.po from the Solution1 folder into your second workbench to save time. Otherwise, you can manually enter a comment, price, quantity, and ship date (yyyy-mm-dd) for the item in the Properties view.
- Similarly, a Bill To US Address and Ship To US Address can be added to the order.
- When adding dates, use the XML Schema representation of a date, in ISO format: yyyy-mm-dd.
- Select File -> Save to serialize the purchase order.
- Open the model data using the text editor to see it serialized in XML format.
- To verify that the instance data conforms to the model, you can use the EMF Validation Framework to quickly check. With your .po file open and the Document Root node selected, run PO Editor -> Validate, or right-click any node in your model and select Validate. Errors - if any - will appear in the Problems view. Note that the data in Solution1/data/PO.po is intentionally wrong. We will be fixing it programmatically in a later exercise.
- Correct any problems with your instance data, revalidate, save, and close the Eclipse Application window (the second workbench). Leave the first workbench open for the next step. We will revisit the Validation Framework later.

Step E: [Optional] Modify Code and Regenerate
In these optional steps, you'll see how the EMF code generator deals with regenerating and merging code. To do so, you'll change a label in the generated editor.
First, you will make a change in the generator model, which will affect the code that gets generated. Then, you'll edit the generated code directly, ensuring your changes are retained when the code is regenerated.
- Close your second Eclipse workbench (if not already closed). The following coding steps will be done in your first workbench, and changes will be tested by relaunching the second workbench.
- As described in the EMF.Edit
Framework Overview, EMF.Edit uses item providers to, among other things,
determine what label to display for a given type of object. In particular, it
is the getText() method that does this, and that you will need to change.
- Open the file com.example.po.edit/src/org.eclipse.example.po.provider/USAddressItemProvider.java.
- Scroll down to the getText() method (or select getText() in the Outline view to jump there directly).
- Notice that the generated code currently uses getName() (that is, the name of the person or company associated with a given address) to define the label shown for the address item when displayed in the model editor. The code looks like this:
/**
* This returns the label text for the adapted class.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public String getText(Object object) {
String label = ((USAddress)object).getName();
return label == null || label.length() == 0 ?
getString("_UI_USAddress_type") :
getString("_UI_USAddress_type") + " " + label;
}
- Open the purchase order generator model (PurchaseOrder.genmodel) and select the USAddress class (third level down: Model > Package > Class).
- In the Properties view, change the Label Feature to the street : String
attribute. This determines which feature will be used in the label for
USAddress objects, so this should set the field to display the street address instead of the name of the person or company at that address.
- To have this change take effect, you don't have to regenerate all the code.
You just need to regenerate the item provider class for USAddress.
Save your changes, then right-click USAddress and select Generate Edit Code
from the pop-up menu. There is also no harm in regenerating all the code -- it
just takes longer.
- If you switch over to the file com.example.po.edit/src/org.eclipse.example.po.provider/USAddressItemProvider.java, you'll see that the generated code now shows getStreet() instead of getName().
/**
* This returns the label text for the adapted class.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public String getText(Object object) {
String label = ((USAddress)object).getStreet();
return label == null || label.length() == 0 ?
getString("_UI_USAddress_type") :
getString("_UI_USAddress_type") + " " + label;
}
- Test the change.
- Launch the Eclipse Application again (CTRL-F11) and open the PO.po
resource.
- Expand the resource object and select the purchase order.
- Notice that
the street address of the USAddress is shown in the label, instead of the name.
- As generated, the getText() method simply appends the value of the label feature to the type (class) of the object (in this case, "US Address").
Since a purchase order uses the same type, USAddress, to represent both the bill to and ship to addresses, it would be helpful to modify the item provider
to produce a label that distinguishes between the two. It can do this by determining which feature of the PurchaseOrder contains the USAddress.
To implement this, you'll need to modify the code by hand.
- Open the file com.example.po.edit/src/org.eclipse.example.po.provider/USAddressItemProvider.java. Locate the getText() method.
- Replace the generated method with the following code:
/**
* This returns the label text for the adapted class.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public String getText(Object object)
{
String label = ((USAddress)object).getStreet();
StringBuffer result = new StringBuffer();
PurchaseOrder po = (PurchaseOrder)((EObject)object).eContainer();
if (po != null && po.getBillTo() == object)
{
result.append(getString("_UI_PurchaseOrder_billTo_feature"));
}
else if (po != null && po.getShipTo() == object)
{
result.append(getString("_UI_PurchaseOrder_shipTo_feature"));
}
if (label != null && label.length() != 0)
{
result.append(' ');
result.append(label);
}
return result.toString();
}
This code is relatively straightforward, but don't worry if any parts aren't clear. It uses a few things we haven't discussed yet, like the EObject API and externalized strings.
- The key point is to remove the @generated Javadoc tag, or suffix it with NOT. This ensures that the hand-written code will be retained when the code is regenerated.
- Pasting in this code introduces two errors due to missing imports on the following line:
PurchaseOrder po = (PurchaseOrder)((EObject)object).eContainer();
To fix them, you can either select the above line and hit CTRL-1 (Quick Fix context menu), or click the Quick Fix icon to the left of the line (a lightbulb with a red 'X' overlapping it). You can also use Source -> Organize Imports (CTRL-SHIFT-O) from either the main menu or by right-clicking. The following imports will be added:
import com.example.po.PurchaseOrder;
import org.eclipse.emf.ecore.EObject;
- Save the change, launch the Eclipse Application again, and open PO.po. Notice that the labels for USAddresses now identify which is the Bill To and which is the Ship To address for the purchase order.

- Regenerate the item provider class for USAddress again, and verify that the getText() method is not changed.
The implementation of getText() has been changed from what was
originally generated. The label feature property on the PurchaseOrder class in the
generator model no longer has any effect on the generated code. This is because
you removed the @generated Javadoc tag, preventing this method from being
overwritten during code generation.
Step F: [Optional] Redirect Generated Code to Merge with Custom Code
It is also possible to use a hand-written method in combination with the
generated implementation. In this example, you might like to have the new
getText() method call the old generated version. This
approach will still use the label feature specified in the generator model,
but will append its value to the name of the containing feature, instead of
just the class.
- Provide a target for the generated method body and regenerate it.
- Create a new method getTextGen(), with an @generated annotation.
- To show that it will be regenerated, create an empty method body with only a single null return statement.
/**
* This returns the label text for the adapted class.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public String getTextGen(Object object)
{
return null;
}
- Save USAddressItemProvider.java, switch back to the generator model,
select USAddress, and regenerate the edit code (as before).
- Switch back to the getTextGen() method in USAddressItemProvider.java.
Notice that the generator has recognized the Gen suffix and redirected the original getText() implementation (using the street for the label feature, as specified in the generator model) into it.
/**
* This returns the label text for the adapted class.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public String getTextGen(Object object)
{
String label = ((USAddress)object).getStreet();
return label == null || label.length() == 0 ?
getString("_UI_USAddress_type") :
getString("_UI_USAddress_type") + " " + label;
}
- Modify the hand-written getText() implementation to call the generated version.
- Change the first line of getText() to use getTextGen(), instead of calling
getStreet() on the address directly.
String label = getTextGen(object);
- When you relaunch your second workbench, you'll see that the addresses are now identified as Bill To or Ship To, but also use the specified label feature from getTextGen().

- You can go back to the generator model, change the label feature back to name : String,
regenerate the code, and verify that this does, indeed, affect only the generated code. Relaunching your second workbench, you'll see this:

Summary
You generated a model and editor from an XML Schema document, and then used that editor to create, validate, and save an instance of that model.
You modified the editor using both by using the generator model to customize the generated pattern and by hand-modifying the code, and you saw how the generator can merge its changes in with your hand-written code.