Welcome

Welcome to my first EMF tutorial.

This tutorial will guide you through all steps necessary to build a complete application with a fairly complex model.

In order to help you solve the real problems you will encounter with EMF, a real application with actual functionality will be created. The application will allow to manage all information necessary to write a book (novel, technical documentation, etc.).

Requirements

What does a writer need to have in reach when writing a book? Let's brainstorm:

Phew.

If that's not a recipe for a top seller, what is?

Some Thoughts on the Model

Let's start with the most important part of the model, the book:

Looks like a lot of work ... Let's see what EMF can offer to help.

EMF Models

EMF, the Eclipse Modelling Framework, specializes in generating code to create, load, save and change  models.

All parts of a model are defined in an EMF package or EPackage. These parts can be other EPackages, classes, types, attributes and references.

The first part which we'll look at is the EMF class which allows to define the Java classes which make up our model.

Why not simply use Java directly, you might ask?

You could. But EMF helps you with all the tedious (read: error-prone) bookkeeping stuff: Back references, containment, resolving proxies.

Huh?

Let me give you an example.

Is an author part of his book? Is a book part of an author?

Probably not. But a chapter is part of a book. Why?

We tend to attach things more closely if they can't exist without each other.

An author can easily (if uncomfortably) live without his book and the books don't cease to exist just because an author stops writing or dies.

On the other hand, a chapter is not very useful without the whole book. It's also quite unlikely that you will be able to use the exact same chapter in a second book.

Some authors might have tried that but I've never seen the same text in two different books (except in court).

So from out natural view, a chapter is part of (contained) in a book while an author is not.

In EMF, we can specify these details in the model. In Java, we cannot. We have to write code which implements the desired behavior but to see what the code does, you have to execute it using the Java runtime or in your brain.

But it's obvious that this task is a common one: A project will contain several books and several authors might be working on the project. A chapter might be available in several languages but only in one book. The same book can exist only once in the project. Characters, ideas and notes will exist per project but used at various places in the books.

That's what the EMF framework is for: Generate the code which does the boring work so you can concentrate on the ideas.

Let's start with out first project.

Starting the Project

Fire up Eclipse and make sure you have EMF installed. For details, see EMF tutorials.

Create a new project (Menu File / New / Project ...)

Choose a "Empty EMF Project" from the folder "Eclipse Modeling Framework".

New Project Wizard

You might be tempted to create an "EMF Project" but for that, you need an existing model description, for example an UML diagram created in Rational Rose or an XML Schema description. Since we have neither, we'll start from scratch and build from there.

Set the project name on the next page to "WriterTool".

Now, you'll have a new project in your workspace. Notice that Eclipse has already added the EMF JARs to your classpath so you not only can generate the Java code but actually run it.

Classpaths in new project

Creating the Model

Now, we'll create our first data model for our authoring tool.

If you followed the existing tutorials, you might be tempted to create annotated Java code and build from that.

Don't.

If you did that, you would end up with a model where some parts (those which come from your annotated Java) can't be modified from the EMF Ecore Editor. From the editor, it will look as if the model has changed but from the annotated Java, it will look as if it hasn't. In the end, there will be a lot of confusion, so never start with annotated Java to build your model.

Instead, create a new empty model:

  1. Create a Java package where your model code and EMF files will live (so you have everything in one place). I chose "de.philmanndark.writertool.model".
  2. Create a new empty model: Menu File / New / Other... or Ctrl-N, then select "Ecore Model" from "Example EMF Model Creation Wizards".
    New Ecore Model Wizard
  3. Make sure that the folder model in src/de/philmanndark/writertool is selected
  4. As filename, use "writertool.ecore".
  5. The model object (the root of our new model), choose EPackage.
  6. The encoding should be UTF-8.
  7. Finish the wizard.

Good Package Names

How did I come up with the package name?

The first part is an internet domain which I own (philmann-dark.de). This makes sure the package name is unique in the world. I have to remove the dash because it's illegal in a package name.

Next comes the name of the project (all lowercase) and lastly the part of the project which is implemented here (the model).

Now, you'll have a new file in the Package Explorer and it will be open in a new editor. This is the Sample Ecore Model Editor.

In this editor, you can define the properties of your model: The packages, the classes, the attributes and references. We'll come to that in a moment.

If you open the tree in the editor, you'll see a strange "null" item.

Strange item in the model tree

When you click on it, the status bar of Eclipse will say "Select Object: null". Not really useful. Try the context menu of the "null" item and select "Show Properties View".

This opens a new view where you can set all the attributes of the selected object.

Just for fun, click on the ecore-File in the Package Explorer.

As you can see, the properties view can tell you a lot about various things that you can select in Eclipse. Nice, isn't it?

Select the "null" item again. The view below will now display the properties of package. Unfortunately, it doesn't say what kind of element it is so you'll have to rely on the icons.

An EPackage with its properties

Give our object a name: Click into the empty space in the second column of the Name row and type "WriterTool".

Press Enter. Now, the object in the view above will have a proper name.

Notice that EMF also updated the field "EFactory Instance". EMF manages all properties it can automatically for you. In this case, each package is always connected to a factory, so EMF creates one for you.

We'll also need to speficy the XML namespace fields. Use "writertool" for the "Ns Prefix" and "http://www.philmann-dark.de/writertool.ecore" for the "Ns URI".

Save the ecore file.

"Ns Prefix"? URI?

PropertyDescription
NameThe name of the EPackage. This name will be used to determine the name of the Java factory and package classes (see text).
Ns PrefixThe XML namespace for the elements of the EPackage
Ns URIThe XML URI of the namespace.

Properties of EPackage

XML Namespaces in EMF

XML allows to store arbitrary data in one file (or document in XML lingo). Which data can be stored is defined by a DTD (Document Type Definition) or an XML Schema.

It's even possible to store data from two different definitons in the same XML file. And that even works if the two definitions use the same XML element names for different things.

To keep things apart, every item in the XML file has a namespace. Think of the namespace as a reference to the definition.

The "Ns Prefix" is the name of the namespace, the "Ns URI" is a symbolic name (even if it looks like a real, working URL) for the namespace.

The "Ns Prefix" can be chosen arbitrarily (so you don't get collisions when your XML document already contains a namespace "writertool".

The URI (Uniform Resource Indicator) is a fixed name defined in the model. It is used by EMF to determine to which package the items in the XML document belong. We cannot use the package name for this because it's not guranteed to be unique.

In practice, this means that you can mix arbitrary EMF models and packages in a single document.

For example, you could define an abstract Project model which just manages EObjects (the base class for all EMF model objects). For each type of project, you would create a new model which contains the specific objects necessary for this type of project.

And later, you could use all these project types without having to change the Project model.

Generating the Java Code

Our model is not really useful right now (there are no classes defined, yet), but we'll generate the Java code for it anyway, so we can see what an EPackage is all about.

To generate Java code, you'll need a genmodel. A genmodel is the generator view of the Ecore model.

The Ecore model contains information about your model: What classes are there and what fields do they have and how they relate to each other.

The genmodel contains the information how to turn the Ecore model into Java: Package names, source paths, project names, generator settings (Do we want an interfaces for each class?

  1. Make sure that your ecore file is saved.
  2. Select "writertool.ecore" in the Package Explorer.
  3. Menu File / New / Other... or Ctrl-N
  4. Eclipse Modeling Framework / EMF Model
  5. The parent folder should be "WriterTool/src/de/philmanndark/writertool/model", the file name "writertool.genmodel". Nice wizard, eh?
  6. Choose "Ecore model" as the Model Importer
  7. The wizard should have filled in the correct value of the Model URI already: "platform:/resource/WriterTool/src/de/philmanndark/writertool/model/writertool.ecore"
  8. All buttons on the bottom are disabled. Click "Load" next to the URI to load the model and to enable them.
  9. An ecore file can contain several packages. Now, you can select for which one you want to create the generator. Since we have only one EPackage, the choice is simple.
  10. Finish the wizard.

Keeping Ecore and Genmodel in Sync

In EMF 2.1, you had to refresh the genmodel file every time you saved the ecore file.

In EMF 2.2, this is no longer necessary: The moment you save your ecore file, the genmodel file will be refreshed and re-read. This way, both models are always in sync.

But there is one exception: EMF doesn't make sure you saved the other model before it refreshes it! So if you change something in the ecore file and then switch to the genmodel without saving first, then the genmodel will not have been updated.

Even worse, when you now change something in the genmodel, the ecore file might change! (and vice versa)

The moment you switch the editors again, you will be told that someone just changed the file on disk and whether you want to discard your changes.

To add injury to insult, if you didn't save the ecore and started the code generation in the genmodel, the code won't change.

To ease the problems a little bit, Ctrl-S in the Properties View will save the current editor, even when it's not active.

Therefore: Whenever you change something in the ecore editor, save immediately so you don't forget about it.

Click on the root of the genmodel.

Oh my god, it's full of properties.

These properties define how the Java code is generated. Let's look at some important things.

Notice that EMF has fixed the case of our model name. Change that back to "WriterTool" (capital T). When you press Enter, EMF will update a lot of fields.

That's all we need to do to generate the Java code.

Select "Generate Model Code" from the context menu of the genmodel root.

Since our model contains no classes, yet, not much will happen (there is no code to generate). But you'll notice that your project is suddenly a plugin project (and no longer an EMF project).[FIXME How do I prevent that?]

Our project was turned into an Eclipse plugin

Since the goal is to create an RCP application, that's a good thing. Let's continue with filling the model with some content.

Skip boring property explanations

Genmodel Properties, Section All

FIXME

PropertyDescription
Bundle ManifestFIXME
Copyright TextText to put into the copyright comment of all generated Java files.
Model NameThe name used in many places to generate java (class and filenames)
Model Plug-in IDIf you want to use your model in an Eclipse plugin, you can set the plug-in ID here. See the PDE documentation in the Eclipse online help for details.
Non-NLS MarkersIf you want to translate the strings in your code using property files, then you can tell EMF to flag all model strings (which need no translation) with Non-NLS markers. This allows you to turn on the compiler warning about non-translated strings without being drowned in false warnings.
Runtime CompatibilityFIXME
Runtime JarFIXME

Properties of genmodel, section All

Genmodel Properties, Section Edit

FIXME

PropertyDescription
FIXME

Properties of genmodel, section Edit

Genmodel Properties, Section Editor

FIXME

PropertyDescription
FIXME

Properties of genmodel, section Editor

Genmodel Properties, Section Model

PropertyDescription
FIXME

Properties of genmodel, section Model

Genmodel Properties, Section Model Class Defaults

FIXME

PropertyDescription
FIXME

Properties of genmodel, section Model Class Defaults

Genmodel Properties, Section Model Feature Defaults

FIXME

PropertyDescription
FIXME

Properties of genmodel, section Model Feature Defaults

Genmodel Properties, Section Templates & Merge

FIXME

PropertyDescription
FIXME

Properties of genmodel, section Templates & Merge

Genmodel Properties, Section Tests

FIXME

PropertyDescription
FIXME

Properties of genmodel, section Tests

Your First Book

Actually, it's only your first book object (or EObject).

  1. Switch to the ecore editor
  2. Select the "WriterTool" package
  3. Select New Child / EClass from the context menu.
  4. Name: Book
    The new book EClass
  5. Save the ecore file
  6. Switch to the genmodel editor

As you can see, there is a new item in the tree. Select it to see it's properties.

The Book EClass in the genmodel editor

In the Ecore section, you can see the same attributes as in the ecore editor but you can't edit them here. They are just for reference. Maybe a future implementation of the EMF Generator will offer this.

Generate the model again (from the context menu of the genmodel root). It might be tempting to select "Generate All". Don't. In a moment, you'll see why.

Undoing Code Generation

This time, the model generation took a bit longer.

And we have a problem: Instead of generating the model code in the same package as the ecore/genmodel file, EMF wrote it directly into the src folder.

And Undo is not available.

Argh!

There is an important lesson here: Whenever you generate, make absolutely sure that you can go back to the state before the code generation. There are many things which can go wrong at this step and usually, nothing short of a full backup will get you back to a working state.

In our case, we can simply delete the three packages which have been created for us. But imagine you have a lot of hand-written code in there (which EMF can merge) or uncommitted changes.

You have been warned.

Select the three folders which start with "WriterTool" and delete them.

Moving the Model Code to the Right Place

For the model, the actual Java classnames and packages used are irrelevant, so the settings to specify where the generated code will end up and how the names will be can be found in the genmodel.

To specify where the code should go, do this:

  1. Select the package (first child of the genmodel root)
  2. All / Base Package: de.philmanndark.writertool
    Fix the Base Package
  3. Save
  4. Generate

That's better but still not what we want: Now, we have an illegal Java package "de.philmanndark.writertool.WriterTool".

Delete the three packages and try this:

  1. Switch to the ecore editor
  2. Select the package
  3. Name: model
  4. The EFactore Instance will be adjusted automatically.
  5. Save
  6. Switch back to the genmodel editor
  7. Generate

Now, you'll see this:

The Package Explorer when the names are correct

Much better.

The Package and The Factory

In EMF, the file "Model"Package.java contains the metadata (class defintions, feature IDs, literals, etc).

You'll only use this data when you're doing reflection-type access to the model.

The factory, on the other hand, contains methods to create objects from the defined classes.

"What's that good for?", you might ask. "We have new and we can instantiate the classes directly, right?"

You could, yes. But with the factory, you can overload a model.

Imagine, you want to add a book to our model which has an ISBN.

The person who provided the model didn't think that necessary, so the books in the model have no support for that.

If there were no factory, that would be the end of it.

With the factory, we can create a derived factory which returns ISBNBook (derived from Book) when WriterToolFactory.createBook() is called.

This way, we can extend a model without having to change it.

Remember the problems we had during generation?

Theoretically, EMF can merge hand-made changes and ecore model changes. In practice, this is dangerous because we might have to delete the generated classes because of a mistake.

The factory gives us a perfect tool to move the hand-written code into a different class, so we can always delete the generated files without thinking twice.

Code Formatting

Everyone, who has worked on a larger project has eventually run into the code formatting problem.

Every single developer in the world has her/his own, preferred formatting style.

And she hates every other style with all her guts.

Imagine that you have to change the formatting of the model because the team rules have changed.

Dreadful silence.

Fear not:

  1. Go into the Eclipse preferences
  2. Adjust the Java Code Formatter according to the new rules
  3. Open the genmodel editor
  4. Templates & Merge / Code Formatting: true
  5. Generate
  6. That's it.

From now on, the generated code will adhere to your teams' rules.

The Name of the Book

The next step is a simple one: We'll add a name to out book. A name is not a class but a field (in Java terms) or an attribute (or EAttribute) in EMF.

  1. Switch to the ecore editor
  2. In the context menu of Book, select New Child / EAttribute
  3. Select the new child
  4. Name: name
  5. EType: EString <java.lang.String>
  6. Notice that setting the EType also sets the EAttribute Type

Some fields are automatic: The EContaining Class, for example, is set to Book.

This means that this attribute is contained in the Book class.

Or, in EMF lingo, Book is the container for name.

Eat Your Own Dogfood

Ecore files (and genmodel files, too) are also models. They are implemented and make heavy use of the EMF framework.

So many things which apply to our data model, also apply to the definition of out data model and the code generation model.

This is usally referred to as "eating one's own dogfood" which means that in order to provide some technology, you use the very same technology.

Eclipse is devloped in Eclipse. EMF is generated using the EMF code generator. A big part of Java is written in Java itself.

PropertyDescription
ChangeableSet this to false if you don't want to allow someone to change this attribute. Usage: Read-only attribute, calculated attribute or something which can only be set once (usualy during construction of the object).
Default ValueRead-only property provided by EMF which shows the current default value of the attribute.
Default Value LiteralA string (without quotes) from which EMF will try to create a default value (see above) by using the registered converters for this type of field.
DerivedThe attribute is volatile, transient and non-changeable, ie. it is calculated when the getter is called and not stored when the model is saved to a file and you can't change it directly. The length of a java.lang.List is an example for this.
EAttribute TypeSee EType
EContaining ClassThe class which contains this attribute (see text). Automatic attribute maintained by EMF.
ETypeThe type (class) of the attribute. In Java, we would say this is the field type.
IDSet this to true, to make this attribute (one of) the ID attribute(s) of the class. ID attributes will be used to determine if two objects of a class are equal or uniqueness if you put them into a list which requires that or a java.lang.Set.
Lower BoundInteger, >= 0. See text.
ManyAutomatic property. True, if Upper Bound != 1. If true, then this property of the class is a list/set (i.e., the Java field can several values of the same type).
NameThe name of the property. This will be the name used in the code generator for the field and getters/setters.
OrderedIf this attribute is a list (see Many), you can specifiy if the sequence of values should be ordered (like a list) or if it doesn't matter (set). The default is true but for performance reasons, you can set it to false.
RequiredAutomatic property. If true, then Lower Bound is > 0, that is the attribute must be set or you will get an error during loading and saving.
TransientTransient attributes are not persistet (saved to a file when the whole model is saved). Usually, calculated fields (like the length of a list) are transient, because the length is already given by the number of items in the file.
UniqueIf Many is true, you can also say that all values must be unique (different). For example, in a list of languages, you want each languages just once.
UnsettableEMF can manage another state for an attribute for you: Unset. Unset is not the same as "set to null". Unset means the attribute was never set or eUnset() was called on it to clear it. You can use eIsSet() to find out if the attribute is set or not.
Upper BoundInteger, -1 or > 0. You can specify an upper limit for the number of values one can store in this attribute. Use -1 to say: There is no limit.
VolatileThe value of the attribute is computed every time the getter is called. EMF will not generate a private field to store the value. You will have to fill in the body of the getter yourself.

Properties of EAttribute

Useful Combinations of EAttribute Properties

Take java.lang.List. The hidden field where the values of the list are stored, would have these attributes: Changeable = true, EType = java.lang.Object, ID = false, Lower Bound = 0, Ordered = true, Transient = false, Unique = false, Unsettable = false, Upper Bound = -1, Volatile = false.

This means, you can change it, the values will be persistet and there can be any number of values. It's not an ID, it can be empty and you can clear it.

The length of the list would be defined like this: Changeable = false, Derived = true, EType = int, ID = false, Lower Bound = 1, Transient = true, Upper Bound = 1, Volatile = true.

So there is always exactly one of it, it's an int and it can't be changed (because it's a calculated field).

java.lang.Set would look pretty much the same but with Unique = true.

So for most attributes, you will just set the name and the type.

If the value is calculated, you will set Changeable to false and usually Transient to true. You can leave Transient alone, for example, for error checking: If you load the list and the length says there should be 5 elements but you find only 4, you know there is something wrong.

If the value must exist, set Lower Bound to 1. [FIXME does that mean non-null?]

FIXME Give an example of an unsettable and a volatile.

Generated Code for Name

Save the model and generate the code once more. Makes you wish you could invoke generate from the ecore file, right?

You will notice that you can generate just a part of your model by selecting "Generate Model" in the context menu of the node.

Most of the time, this is not useful, though: If we did it here, we would have got just a new BookImpl.java. Book.java and the EPackage and the factory wouldn't change, so there would be compile errors.

Makes one wish that selecting generate from a node would always generate the whole model. Maybe in the next version of EMF.

If you did generate just the book alone, Book.java won't contain the getName() and setName() for the new field. Unfortunately, generating the code once more doesn't help: The missing declarations won't show up.

Again, the only way to solve this is to delete the Book.java file and to generate everything.

Skip the explanation of the code

A Look at the Generated Code

Book.java

The file Book.java contains the interface of the Book class. Why an interface?

EMF promotes code decoupling. Since all code is generated, this is essentially free for you. With the interface, you always work with Book. Whether the book comes from an XML file, a web server, an SQL, XML or OO database, a JUnit test case or the Eclipse workspace is irrelevant for it's use.

   1:/**
   2: * <copyright>
   3: * </copyright>
   4: *
   5: * $Id$
   6: */
   7:package de.philmanndark.writertool.model;
   8:
   9:import org.eclipse.emf.ecore.EObject;

Notice the copyright elements above which you can fill with the property "All/Copyright Text" of genmodel.

The package name comes from "All/Base Package" of the EPackage in genmodel.

The imports are automatically created by EMF.

  11:/**
  12: * <!-- begin-user-doc -->
  13: * A representation of the model object '<em><b>Book</b></em>'.
  14: * <!-- end-user-doc -->
  15: *
  16: * <p>
  17: * The following features are supported:
  18: * <ul>
  19: *   <li>{@link de.philmanndark.writertool.model.Book#getName <em>Name</em>}</li>
  20: * </ul>
  21: * </p>
  22: *
  23: * @see de.philmanndark.writertool.model.WriterToolPackage#getBook()
  24: * @model
  25: * @generated
  26: */
  27:public interface Book extends EObject
  28:{

If only all Java code was documented like that.

Of course, most of the comment is not really useful. Notice the user-doc-comments, though. These help EMF to distinguish between between generated comments and hand-written parts.

The list of features is useful when you start with multiple inheritance. Java usually doesn't offer this but EMF can merge the code from several model classes into a single Java class.

It's as if you wrote the code once and then copied it around except for the manual copy bit. With EMF, all copies of the code will be kept in sync automatically (well, mostly. We'll come to that).

Also notice the @model and @generated tags. The first tells EMF that this file is part of the generated model and the second that this part of the file (the class definition) can be overwritten (ie. there are no manual changes in it).

To some part of the generated code away from EMF, you can either delete the @generated tag (but you must leave the @model tag), or you can add "NOT" to it:

 * @generated NOT

Both will achieve the same but the NOT-version will make your intention more clear (and it's less effort to give control back to EMF).

  29:    /**
  30:     * Returns the value of the '<em><b>Name</b></em>' attribute.
  31:     * The default value is <code>""</code>.
  32:     * <!-- begin-user-doc -->
  33:     * <p>
  34:     * If the meaning of the '<em>Name</em>' attribute isn't clear,
  35:     * there really should be more of a description here...
  36:     * </p>
  37:     * <!-- end-user-doc -->
  38:     * @return the value of the '<em>Name</em>' attribute.
  39:     * @see #setName(String)
  40:     * @see de.philmanndark.writertool.model.WriterToolPackage#getBook_Name()
  41:     * @model default=""
  42:     * @generated
  43:     */
  44:    String getName();

This is how a standard getter looks like.

The comment is generated as usual. Notice the default="" after the @model tag. This is a bug in the development version of EMF which is fixed in the CVS version.

To get rid of this unwanted default value, you must edit the ecore file with a text editor (it's just an XML file, so don't worry). Select the ecore file in the Package Explorer and "Open With / Text Editor" from the context menu.

It looks like this:

   1:<?xml version="1.0" encoding="UTF-8"?>
   2:<ecore:EPackage xmi:version="2.0"
   3:    xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   4:    xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="model"
   5:    nsURI="http://www.philmann-dark.de/writertool.ecore" nsPrefix="writertool">
   6:  <eClassifiers xsi:type="ecore:EClass" name="Book">
   7:    <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
   8:        defaultValueLiteral=""/>
   9:  </eClassifiers>
  10:</ecore:EPackage>
  11:

Our problem is defaultValueLiteral="". Delete it but leave the "/>" afterwards alone.

Save the file and switch to the genmodel editor to generate the code once more:

  29:    /**
  30:     * Returns the value of the '<em><b>Name</b></em>' attribute.
  31:     * <!-- begin-user-doc -->
  32:     * <p>
  33:     * If the meaning of the '<em>Name</em>' attribute isn't clear,
  34:     * there really should be more of a description here...
  35:     * </p>
  36:     * <!-- end-user-doc -->
  37:     * @return the value of the '<em>Name</em>' attribute.
  38:     * @see #setName(String)
  39:     * @see de.philmanndark.writertool.model.WriterToolPackage#getBook_Name()
  40:     * @model
  41:     * @generated
  42:     */
  43:    String getName();

Notice that the default value was removed at several places!

Admittedly, it's a problem which we wouldn't have without the bug in the EMF editor but it shows how EMF keeps the code clean. If we would maintain the same code manually, chances are that the default value would have been gone but not in the description (who ever updates the javadoc?)

Btw, you could have used "Restore Default Value" from the context menu of the Default Value Literal property to get rid of the empty string but then, I couldn't have you open the ecore file and look into it.

  45:    /**
  46:     * Sets the value of the '{@link de.philmanndark.writertool.model.Book#getName <em>Name</em>}' attribute.
  47:     * <!-- begin-user-doc -->
  48:     * <!-- end-user-doc -->
  49:     * @param value the new value of the '<em>Name</em>' attribute.
  50:     * @see #getName()
  51:     * @generated
  52:     */
  53:    void setName(String value);
  54:
  55:} // Book

This is the setter defintion. Nothing outstanding here.

BookImpl.java

Some real meat, at last. This is the default implementation of the book interface which EMF generates by default.

   1:/**
   2: * <copyright>
   3: * </copyright>
   4: *
   5: * $Id$
   6: */
   7:package de.philmanndark.writertool.model.impl;

The usual header with the copyright. Imagine M$ buys your company and you will all have to fix the copyright headers of all your source files. A nightmare for everyone but you (well, not the code changing part, at least).

   9:import de.philmanndark.writertool.model.Book;
  10:import de.philmanndark.writertool.model.WriterToolPackage;
  11:
  12:import org.eclipse.emf.common.notify.Notification;
  13:
  14:import org.eclipse.emf.ecore.EClass;
  15:
  16:import org.eclipse.emf.ecore.impl.ENotificationImpl;
  17:import org.eclipse.emf.ecore.impl.EObjectImpl;

Automatic imports make Eclipse developers yawn but EMF still saves you the occasional "Organize Imports".

  19:/**
  20: * <!-- begin-user-doc -->
  21: * An implementation of the model object '<em><b>Book</b></em>'.
  22: * <!-- end-user-doc -->
  23: * <p>
  24: * The following features are implemented:
  25: * <ul>
  26: *   <li>{@link de.philmanndark.writertool.model.impl.BookImpl#getName <em>Name</em>}</li>
  27: * </ul>
  28: * </p>
  29: *
  30: * @generated
  31: */
  32:public class BookImpl extends EObjectImpl implements Book
  33:{

This looks like a clone of the interface and it is, except for some minor details like "implemented" here and "supported" in the interface.

  34:    /**
  35:     * The default value of the '{@link #getName() <em>Name</em>}' attribute.
  36:     * <!-- begin-user-doc -->
  37:     * <!-- end-user-doc -->
  38:     * @see #getName()
  39:     * @generated
  40:     * @ordered
  41:     */
  42:    protected static final String NAME_EDEFAULT = null;
  43:
  44:    /**
  45:     * The cached value of the '{@link #getName() <em>Name</em>}' attribute.
  46:     * <!-- begin-user-doc -->
  47:     * <!-- end-user-doc -->
  48:     * @see #getName()
  49:     * @generated
  50:     * @ordered
  51:     */
  52:    protected String name = NAME_EDEFAULT;

This is the standard code for default value handling. Notice that EMF actually defines the default value in a constant instead of just assigning it. This way, you can reuse the default value (for example, to reset an object) without worrying what the defaults actually are.

  54:    /**
  55:     * <!-- begin-user-doc -->
  56:     * <!-- end-user-doc -->
  57:     * @generated
  58:     */
  59:    protected BookImpl()
  60:    {
  61:        super();
  62:    }

Nothing to see here, move on.

  64:    /**
  65:     * <!-- begin-user-doc -->
  66:     * <!-- end-user-doc -->
  67:     * @generated
  68:     */
  69:    protected EClass eStaticClass()
  70:    {
  71:        return WriterToolPackage.Literals.BOOK;
  72:    }

This method returns the actual EClass of this EObject. The EClass contains the metadata from our ecore model, remember? In the EClass, you can find a list of all attributes and their properties (like name and type).

When you have the class, then you can ask the class for it's package to find the XML namespace declaration, for example.

  74:    /**
  75:     * <!-- begin-user-doc -->
  76:     * <!-- end-user-doc -->
  77:     * @generated
  78:     */
  79:    public String getName()
  80:    {
  81:        return name;
  82:    }

The getter is really simple in this case (aren't they always?).

But it's again a bit of code which you didn't have to write.

If this was an calculated attribute, you would add a "NOT" after the @generated tag and overwrite the method body.

  84:    /**
  85:     * <!-- begin-user-doc -->
  86:     * <!-- end-user-doc -->
  87:     * @generated
  88:     */
  89:    public void setName(String newName)
  90:    {
  91:        String oldName = name;
  92:        name = newName;
  93:        if (eNotificationRequired())
  94:            eNotify(new ENotificationImpl(this, Notification.SET,
  95:                    WriterToolPackage.BOOK__NAME, oldName, name));
  96:    }

The standard EMF setter. Notice the notification code: By default, when you change an attribute in an EMF model, a notification is generated. Of course, you can turn this off if you need to but usually, you won't.

To understand the power of these few lines of code, go into the ecore editor and change the name of the Book class to "BookX".

Now open the Edit menu and select Undo.

The book will be a book, again. And the "this file was modified" indicator is gone, too.

What do you think: How many lines of code are necessary in the ecore editor to implement this behavior?

One.

Uno.

One single line of code.

Because this is a standard feature of EMF. Here is how it works: There is an implementation of an Undo handler in the core of EMF. The Undo code will register a listener for all Notification.SET events (plus some more). When one happens, it will store the ENotification object in Eclipse's undo stack. When an undo is requested by the user, the EMF Undo handler will take the ENotification object and tell the EMF core to call the setter with the attribute, object and old value stored in the notification (WriterToolPackage.BOOK__NAME, this and oldName respectively).

That's it. No changes to the model, no nothing.

This code is the same for all EMF models, no matter how complex they are, no matter what the values are (EMF will automatically Integer to int, for example), no matter how the editor works or what the constraints are.

It even supports Undo merging (when you change several attributes at once), so one user action will need only one Undo step.

When you use EMF, you get undo for free.

Imagine you want to implement something like mtime (last modification time of a file). Without EMF, you would add a lot of code in a lot of place (basically in every setter) or you would have to add notification to your model (you did notice that we didn't say anything about notification in either the ecore nor the genmodel file?), maintaining the calls, keeping them in sync with model changes.

With EMF, you register a global SET listener. In the listener, you check if the object in question has the mtime attribute (using the eStaticClass(), for example). If it has, you would update the field.

15 lines of code, tops.

And it will always work, no matter what you do with your model, how complex the objects are or if there are bazillions of it.

  98:    /**
  99:     * <!-- begin-user-doc -->
 100:     * <!-- end-user-doc -->
 101:     * @generated
 102:     */
 103:    public Object eGet(int featureID, boolean resolve, boolean coreType)
 104:    {
 105:        switch (featureID)
 106:        {
 107:        case WriterToolPackage.BOOK__NAME:
 108:            return getName();
 109:        }
 110:        return super.eGet(featureID, resolve, coreType);
 111:    }

This code is part of EMF's reflective interface. Normally, you would use reflection to query or set attributes of an object of your model.

EMF allows that but you'd loose the nice notification feature. Instead, you can use eGet() which means: less code, no exception handling, problems with paranoid security settings, private fields.

 113:    /**
 114:     * <!-- begin-user-doc -->
 115:     * <!-- end-user-doc -->
 116:     * @generated
 117:     */
 118:    public void eSet(int featureID, Object newValue)
 119:    {
 120:        switch (featureID)
 121:        {
 122:        case WriterToolPackage.BOOK__NAME:
 123:            setName((String) newValue);
 124:            return;
 125:        }
 126:        super.eSet(featureID, newValue);
 127:    }

The same for the setter. This code will be called from the Undo handler, for example, to reset values.

 129:    /**
 130:     * <!-- begin-user-doc -->
 131:     * <!-- end-user-doc -->
 132:     * @generated
 133:     */
 134:    public void eUnset(int featureID)
 135:    {
 136:        switch (featureID)
 137:        {
 138:        case WriterToolPackage.BOOK__NAME:
 139:            setName(NAME_EDEFAULT);
 140:            return;
 141:        }
 142:        super.eUnset(featureID);
 143:    }

Remember the default values? You can call the setter directly or you can use this method. This method here is more simple when you want to reset the whole class: Just find the feature IDs (the EClass will tell you), and call eUnset() for each of them. Afterwards, the class will be in the same state as if you had just created it (except for unsettable fields, of course).

 145:    /**
 146:     * <!-- begin-user-doc -->
 147:     * <!-- end-user-doc -->
 148:     * @generated
 149:     */
 150:    public boolean eIsSet(int featureID)
 151:    {
 152:        switch (featureID)
 153:        {
 154:        case WriterToolPackage.BOOK__NAME:
 155:            return NAME_EDEFAULT == null ? name != null : !NAME_EDEFAULT
 156:                    .equals(name);
 157:        }
 158:        return super.eIsSet(featureID);
 159:    }

Another helper. Most people will never need it but it's just a few lines of code and if you need it, you'll find that your model already contains 500 classes.

And when the code goes productive, you'll notice that you forgot to handle the default value case (and if you did, you'll get NullPointerExceptions).

 161:    /**
 162:     * <!-- begin-user-doc -->
 163:     * <!-- end-user-doc -->
 164:     * @generated
 165:     */
 166:    public String toString()
 167:    {
 168:        if (eIsProxy())
 169:            return super.toString();
 170:
 171:        StringBuffer result = new StringBuffer(super.toString());
 172:        result.append(" (name: ");
 173:        result.append(name);
 174:        result.append(')');
 175:        return result.toString();
 176:    }
 177:
 178:} //BookImpl

The most used method of all (right after the constructor, that is). Don't you hate it, when you just get com.fck.off.BrokenClass@deadbeef?

Maybe the default is not really what you need but usually, what you need is only a few "delete line"'s away.

Don't forget to add "NOT" to the @generated tag, though! Otherwise, your change might be unexpectedly short-lived ...

WriterToolFactory.java

I'll omit most of the boring stuff because we have seen it, already. This is another feature of generated code: When you have seen a part of it, you have seen most of it but in a positive way.

  21:    /**
  22:     * The singleton instance of the factory.
  23:     * <!-- begin-user-doc -->
  24:     * <!-- end-user-doc -->
  25:     * @generated
  26:     */
  27:    WriterToolFactory eINSTANCE = de.philmanndark.writertool.model.impl.WriterToolFactoryImpl
  28:            .init();

This simple line of code allows to plug your own factory into an existing model. Remember it well.

  30:    /**
  31:     * Returns a new object of class '<em>Book</em>'.
  32:     * <!-- begin-user-doc -->
  33:     * <!-- end-user-doc -->
  34:     * @return a new object of class '<em>Book</em>'.
  35:     * @generated
  36:     */
  37:    Book createBook();

And after you have hijacked the factory, you can use these calls to force your devious creations into the innocent, helpless model.

Or, in Java lingo: This factory method allows you to inject Book-compatible objects without having to touch existing code.

I like my version better. Arrr!

  39:    /**
  40:     * Returns the package supported by this factory.
  41:     * <!-- begin-user-doc -->
  42:     * <!-- end-user-doc -->
  43:     * @return the package supported by this factory.
  44:     * @generated
  45:     */
  46:    WriterToolPackage getWriterToolPackage();

And not even the meta data is safe!

You will be 0wn3d!

WriterToolFactoryImpl.java

Let's have a look how a factory actually works.

  28:    /**
  29:     * Creates the default factory implementation.
  30:     * <!-- begin-user-doc -->
  31:     * <!-- end-user-doc -->
  32:     * @generated
  33:     */
  34:    public static WriterToolFactory init()
  35:    {
  36:        try
  37:        {
  38:            WriterToolFactory theWriterToolFactory = (WriterToolFactory) EPackage.Registry.INSTANCE
  39:                    .getEFactory("http://www.philmann-dark.de/writertool.ecore");
  40:            if (theWriterToolFactory != null)
  41:            {
  42:                return theWriterToolFactory;
  43:            }
  44:        } catch (Exception exception)
  45:        {
  46:            EcorePlugin.INSTANCE.log(exception);
  47:        }
  48:        return new WriterToolFactoryImpl();
  49:    }

The setup code is not in the constructor because the factory is a singleton.

First, it tried to find the factory in the EMF package registry. If it cannot be found there, a new instance will be created (which registers itself).

  62:    /**
  63:     * <!-- begin-user-doc -->
  64:     * <!-- end-user-doc -->
  65:     * @generated
  66:     */
  67:    public EObject create(EClass eClass)
  68:    {
  69:        switch (eClass.getClassifierID())
  70:        {
  71:        case WriterToolPackage.BOOK:
  72:            return createBook();
  73:        default:
  74:            throw new IllegalArgumentException("The class '" + eClass.getName()
  75:                    + "' is not a valid classifier");
  76:        }
  77:    }

This is the EMF reflective way to create model objects. The ecore and genmodel editors use it, for example, to create their objects without knowing much about the actual model.

  79:    /**
  80:     * <!-- begin-user-doc -->
  81:     * <!-- end-user-doc -->
  82:     * @generated
  83:     */
  84:    public Book createBook()
  85:    {
  86:        BookImpl book = new BookImpl();
  87:        return book;
  88:    }

For each EClass in the model, one of these is generated. Here, you can inject your own objects, do strange things like registering objects in a global cache or just counting them.

WriterToolPackage.java

  30:    /**
  31:     * The package name.
  32:     * <!-- begin-user-doc -->
  33:     * <!-- end-user-doc -->
  34:     * @generated
  35:     */
  36:    String eNAME = "model";
  37:
  38:    /**
  39:     * The package namespace URI.
  40:     * <!-- begin-user-doc -->
  41:     * <!-- end-user-doc -->
  42:     * @generated
  43:     */
  44:    String eNS_URI = "http://www.philmann-dark.de/writertool.ecore";
  45:
  46:    /**
  47:     * The package namespace name.
  48:     * <!-- begin-user-doc -->
  49:     * <!-- end-user-doc -->
  50:     * @generated
  51:     */
  52:    String eNS_PREFIX = "writertool";

These constants contain the values which you specified earlier in the ecore model editor.

  54:    /**
  55:     * The singleton instance of the package.
  56:     * <!-- begin-user-doc -->
  57:     * <!-- end-user-doc -->
  58:     * @generated
  59:     */
  60:    WriterToolPackage eINSTANCE = de.philmanndark.writertool.model.impl.WriterToolPackageImpl
  61:            .init();

Again a hook to allow to inject your own objects.

  63:    /**
  64:     * The meta object id for the '{@link de.philmanndark.writertool.model.impl.BookImpl <em>Book</em>}' class.
  65:     * <!-- begin-user-doc -->
  66:     * <!-- end-user-doc -->
  67:     * @see de.philmanndark.writertool.model.impl.BookImpl
  68:     * @see de.philmanndark.writertool.model.impl.WriterToolPackageImpl#getBook()
  69:     * @generated
  70:     */
  71:    int BOOK = 0;

Every EClass gets its own, (model) unique ID. It can be found in the EClass.getClassifierID(), for example, and is used in switch statements (because Java before 1.5 doesn't support objects in a switch).

  73:    /**
  74:     * The feature id for the '<em><b>Name</b></em>' attribute.
  75:     * <!-- begin-user-doc -->
  76:     * <!-- end-user-doc -->
  77:     * @generated
  78:     * @ordered
  79:     */
  80:    int BOOK__NAME = 0;

The same for the attributes of an EClass. This ends up in EAttribute.getFeatureID() and it's also used in switch() statements (Java will never support fields of a class as a key in a switch).

  82:    /**
  83:     * The number of structural features of the '<em>Book</em>' class.
  84:     * <!-- begin-user-doc -->
  85:     * <!-- end-user-doc -->
  86:     * @generated
  87:     * @ordered
  88:     */
  89:    int BOOK_FEATURE_COUNT = 1;

Just in case you want to know how many features an EClass has. This is used in the multiple inheritance code of EMF to calculate the offsets for the various features from different EClasses.

  91:    /**
  92:     * Returns the meta object for class '{@link de.philmanndark.writertool.model.Book <em>Book</em>}'.
  93:     * <!-- begin-user-doc -->
  94:     * <!-- end-user-doc -->
  95:     * @return the meta object for class '<em>Book</em>'.
  96:     * @see de.philmanndark.writertool.model.Book
  97:     * @generated
  98:     */
  99:    EClass getBook();

And finally all the information in one place. When you example an EClass object, you'll find all the information which is known about the respective kind of object.

 101:    /**
 102:     * Returns the meta object for the attribute '{@link de.philmanndark.writertool.model.Book#getName <em>Name</em>}'.
 103:     * <!-- begin-user-doc -->
 104:     * <!-- end-user-doc -->
 105:     * @return the meta object for the attribute '<em>Name</em>'.
 106:     * @see de.philmanndark.writertool.model.Book#getName()
 107:     * @see #getBook()
 108:     * @generated
 109:     */
 110:    EAttribute getBook_Name();

This is just the name attribute of the book. Another way to get this to use the feature ID BOOK__NAME on the EClass (returned by getBook() above or from calling eClass() on a book object).

 112:    /**
 113:     * Returns the factory that creates the instances of the model.
 114:     * <!-- begin-user-doc -->
 115:     * <!-- end-user-doc -->
 116:     * @return the factory that creates the instances of the model.
 117:     * @generated
 118:     */
 119:    WriterToolFactory getWriterToolFactory();

Reference to the factory. The factory has a reference back to us, remember?

Don't ask how this is intialized ...

In EMF, there are usually many ways to get at a piece of information.

 121:    /**
 122:     * <!-- begin-user-doc -->
 123:     * Defines literals for the meta objects that represent
 124:     * <ul>
 125:     *   <li>each class,</li>
 126:     *   <li>each feature of each class,</li>
 127:     *   <li>each enum,</li>
 128:     *   <li>and each data type</li>
 129:     * </ul>
 130:     * <!-- end-user-doc -->
 131:     * @generated
 132:     */
 133:    interface Literals
 134:    {
 135:        /**
 136:         * The meta object literal for the '{@link de.philmanndark.writertool.model.impl.BookImpl <em>Book</em>}' class.
 137:         * <!-- begin-user-doc -->
 138:         * <!-- end-user-doc -->
 139:         * @see de.philmanndark.writertool.model.impl.BookImpl
 140:         * @see de.philmanndark.writertool.model.impl.WriterToolPackageImpl#getBook()
 141:         * @generated
 142:         */
 143:        EClass BOOK = eINSTANCE.getBook();
 144:
 145:        /**
 146:         * The meta object literal for the '<em><b>Name</b></em>' attribute feature.
 147:         * <!-- begin-user-doc -->
 148:         * <!-- end-user-doc -->
 149:         * @generated
 150:         */
 151:        EAttribute BOOK__NAME = eINSTANCE.getBook_Name();
 152:
 153:    }
 154:
 155:} //WriterToolPackage

Here, you get the same information as above but in a static way.

This should only be used by the EMF initialization code because when you ask BOOK to create your objects, you'll be loosing the dynamic behavior of EMF plus the rest of the EMF code won't expect this, so you'll get really strange errors.

WriterToolPackageImpl.java

Now, let's have a look how a package works:

  28:    /**
  29:     * <!-- begin-user-doc -->
  30:     * <!-- end-user-doc -->
  31:     * @generated
  32:     */
  33:    private EClass bookEClass = null;

All EClasses are simple fields.

  35:    /**
  36:     * Creates an instance of the model <b>Package</b>, registered with
  37:     * {@link org.eclipse.emf.ecore.EPackage.Registry EPackage.Registry} by the package
  38:     * package URI value.
  39:     * <p>Note: the correct way to create the package is via the static
  40:     * factory method {@link #init init()}, which also performs
  41:     * initialization of the package, or returns the registered package,
  42:     * if one already exists.
  43:     * <!-- begin-user-doc -->
  44:     * <!-- end-user-doc -->
  45:     * @see org.eclipse.emf.ecore.EPackage.Registry
  46:     * @see de.philmanndark.writertool.model.WriterToolPackage#eNS_URI
  47:     * @see #init()
  48:     * @generated
  49:     */
  50:    private WriterToolPackageImpl()
  51:    {
  52:        super(eNS_URI, WriterToolFactory.eINSTANCE);
  53:    }

The constructor registers the new package and makes the connection to the factory.

Note that you can't call it (it's private). You must use init() which makes sure that there can't be more than one package.

  55:    /**
  56:     * <!-- begin-user-doc -->
  57:     * <!-- end-user-doc -->
  58:     * @generated
  59:     */
  60:    private static boolean isInited = false;
  61:
  62:    /**
  63:     * Creates, registers, and initializes the <b>Package</b> for this
  64:     * model, and for any others upon which it depends.  Simple
  65:     * dependencies are satisfied by calling this method on all
  66:     * dependent packages before doing anything else.  This method drives
  67:     * initialization for interdependent packages directly, in parallel
  68:     * with this package, itself.
  69:     * <p>Of this package and its interdependencies, all packages which
  70:     * have not yet been registered by their URI values are first created
  71:     * and registered.  The packages are then initialized in two steps:
  72:     * meta-model objects for all of the packages are created before any
  73:     * are initialized, since one package's meta-model objects may refer to
  74:     * those of another.
  75:     * <p>Invocation of this method will not affect any packages that have
  76:     * already been initialized.
  77:     * <!-- begin-user-doc -->
  78:     * <!-- end-user-doc -->
  79:     * @see #eNS_URI
  80:     * @see #createPackageContents()
  81:     * @see #initializePackageContents()
  82:     * @generated
  83:     */
  84:    public static WriterToolPackage init()
  85:    {
  86:        if (isInited)
  87:            return (WriterToolPackage) EPackage.Registry.INSTANCE
  88:                    .getEPackage(WriterToolPackage.eNS_URI);
  89:
  90:        // Obtain or create and register package
  91:        WriterToolPackageImpl theWriterToolPackage = (WriterToolPackageImpl) (EPackage.Registry.INSTANCE
  92:                .getEPackage(eNS_URI) instanceof WriterToolPackageImpl ? EPackage.Registry.INSTANCE
  93:                .getEPackage(eNS_URI)
  94:                : new WriterToolPackageImpl());
  95:
  96:        isInited = true;
  97:
  98:        // Create package meta-data objects
  99:        theWriterToolPackage.createPackageContents();
 100:
 101:        // Initialize created meta-data
 102:        theWriterToolPackage.initializePackageContents();
 103:
 104:        // Mark meta-data to indicate it can't be changed
 105:        theWriterToolPackage.freeze();
 106:
 107:        return theWriterToolPackage;
 108:    }

Init makes sure that the package is a singleton and sets up the internal structure.

 110:    /**
 111:     * <!-- begin-user-doc -->
 112:     * <!-- end-user-doc -->
 113:     * @generated
 114:     */
 115:    public EClass getBook()
 116:    {
 117:        return bookEClass;
 118:    }
 119:
 120:    /**
 121:     * <!-- begin-user-doc -->
 122:     * <!-- end-user-doc -->
 123:     * @generated
 124:     */
 125:    public EAttribute getBook_Name()
 126:    {
 127:        return (EAttribute) bookEClass.getEStructuralFeatures().get(0);
 128:    }

Here is how you can get at the attributes when you just have an EClass or an EObject.

 130:    /**
 131:     * <!-- begin-user-doc -->
 132:     * <!-- end-user-doc -->
 133:     * @generated
 134:     */
 135:    public WriterToolFactory getWriterToolFactory()
 136:    {
 137:        return (WriterToolFactory) getEFactoryInstance();
 138:    }

A helper method to avoid a cast.

 140:    /**
 141:     * <!-- begin-user-doc -->
 142:     * <!-- end-user-doc -->
 143:     * @generated
 144:     */
 145:    private boolean isCreated = false;
 146:
 147:    /**
 148:     * Creates the meta-model objects for the package.  This method is
 149:     * guarded to have no affect on any invocation but its first.
 150:     * <!-- begin-user-doc -->
 151:     * <!-- end-user-doc -->
 152:     * @generated
 153:     */
 154:    public void createPackageContents()
 155:    {
 156:        if (isCreated)
 157:            return;
 158:        isCreated = true;
 159:
 160:        // Create classes and their features
 161:        bookEClass = createEClass(BOOK);
 162:        createEAttribute(bookEClass, BOOK__NAME);
 163:    }

The code in createPackageContents() creates the (empty) EClasses and EAttributes. They will be setup in initializePackageContents().

 165:    /**
 166:     * <!-- begin-user-doc -->
 167:     * <!-- end-user-doc -->
 168:     * @generated
 169:     */
 170:    private boolean isInitialized = false;
 171:
 172:    /**
 173:     * Complete the initialization of the package and its meta-model.  This
 174:     * method is guarded to have no affect on any invocation but its first.
 175:     * <!-- begin-user-doc -->
 176:     * <!-- end-user-doc -->
 177:     * @generated
 178:     */
 179:    public void initializePackageContents()
 180:    {
 181:        if (isInitialized)
 182:            return;
 183:        isInitialized = true;
 184:
 185:        // Initialize package
 186:        setName(eNAME);
 187:        setNsPrefix(eNS_PREFIX);
 188:        setNsURI(eNS_URI);
 189:
 190:        // Add supertypes to classes
 191:
 192:        // Initialize classes and features; add operations and parameters
 193:        initEClass(bookEClass, Book.class, "Book", !IS_ABSTRACT, !IS_INTERFACE,
 194:                IS_GENERATED_INSTANCE_CLASS);
 195:        initEAttribute(getBook_Name(), ecorePackage.getEString(), "name", null,
 196:                0, 1, Book.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE,
 197:                !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
 198:
 199:        // Create resource
 200:        createResource(eNS_URI);
 201:    }
 202:
 203:} //WriterToolPackageImpl

After the objects have been created in createPackageContents(), their various fields are filled with the correct values.

Note the trick how to pass eigth booleans into a method without confusing them.

WriterToolAdapterFactory.java and WriterToolSwitch.java

We'll come to these later.

Adding Chapters to the Book

First, we'll need a new Chapter EClass. Create one as a child of "model".

The first version chapter will be pretty simple: Just add one attribute "text", set EType to EString.

The interesting part comes with the recursive nature of the book content.

Also, we should think about setting limits for the chapters in a book.

First, well tell EMF that a book can contain chapters. To do that, select the book and "New Child / EReference" from the context menu.

Unlike an attribute, an EReference allows you to use other model objects as "value". Try it: Create another attribute and try to set the type to Chapter. EMF won't let you.

But with references, you can do just that: Set the name of the new reference to "chapters" and the EType to Chapter.

Setting Limits