1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="stylesheet" href="images/../../../css/book.css" type="text/css"/>
<link rel="stylesheet" href="images/../../../css/emf-book.css" type="text/css"/>
<title>EMF.Edit Overview</title>
</head>
<body lang="EN-US" xml:lang="EN-US">
<h1>EMF.Edit Overview</h1>
<p>Last updated: June 1, 2004</p>
<p>This document assumes familiarity with basic EMF (Eclipse Modeling Framework)
concepts. For more information on EMF, refer to the
<a href="../../references/overview/EMF.html">EMF Overview</a>.</p>
<h2>Contents</h2>
<p><a href="#introduction">Introduction</a><br/>
<a href="#jface">Displaying EMF Objects in JFace Viewers</a><br/>
<a href="#providers">Item Provider Implementation Classes</a><br/>
<a href="#commands">Modifying EMF Models Using Commands</a><br/>
<a href="#generator">Using the EMF.Edit Code Generator</a></p>
<h2><a name="introduction">Introduction</a></h2>
<p>If you have an EMF-based model that you've generated code for using the EMF
generator, and you are now ready to add user interfaces to your model and code,
you can use the EMF.Edit framework to make your job easier.</p>
<p>EMF.Edit is an Eclipse framework that includes generic reusable classes for
building editors for EMF models. It provides:</p>
<ul>
<li>Content and label provider classes, property source support, and other
convenience classes that allow EMF models to be displayed using standard desktop
(JFace) viewers and property sheets.</li>
<li>A command framework, including a set of generic command implementation
classes for building editors that support fully automatic undo and redo.</li>
<li>A code generator capable of generating everything needed to build a complete
editor plug-in for your EMF model. It produces a properly structured editor that
conforms to the recommended style for Eclipse EMF model editors. You can then
customize the code however you like, without losing your connection to the
model.</li>
</ul>
<p>This paper gives an overview of the basic concepts of the EMF.Edit frameowrk
and generator tool. For more in-depth information, you can refer to the
documentation for the framework classes, which covers in detail their specific
behaviors and capabilities.</p>
<h2><a name="jface">Displaying EMF Objects in JFace Viewers</a></h2>
<h3>What's a content provider?</h3>
<p>The Eclipse user interface framework (JFace) includes a set of reusable
viewer classes (for example TreeViewer, TableViewer) for displaying structured
models. Rather than requiring the model objects being viewed to adhere to some
specific protocol (that is, implement some specific interface), JFace viewers
work with any kind of object (that is, any java.lang.Object subclass). This is
possible because the viewers, instead of navigating the model objects directly,
access the model objects through an adapter object called a <dfn>content
provider</dfn>.</p>
<p>Each viewer class uses a content provider that implements a specific provider
interface. For example, a TreeViewer uses a content provider that implements the
following interface:</p>
<pre>
public interface ITreeContentProvider ...
{
public Object[] getChildren(Object object);
public Object getParent(Object object);
...
}
</pre>
<p>The basic structure is shown in the following diagram:</p>
<img src="images/EMF.Edit/image001.gif" border="0" width="481" height="207" alt="JFace content provider"/>
<p>As far as the TreeViewer is concerned it is displaying a tree of objects (it
calls them items) on the screen, all of which, except the input (root) object,
are provided to it by calling getChildren() on its content provider.</p>
<p>Other kinds of viewers are handled similarly, although each viewer requires
the content provider to implement its specific interface. Although all of the
viewer interfaces are different, a content provider can often implement several
of them at once, allowing the same content provider class to be used for many
kinds of viewers.</p>
<h3>Providing content for an EMF model</h3>
<p>The EMF.Edit framework provides a generic content provider implementation
class that can be used to provide content for EMF models. Class
AdapterFactoryContentProvider implements the content provider interfaces by
delegating to EMF adapters that know how to navigate the model objects (items)
for the viewers. For example, the EMF adapter class used to support a tree
viewer would implement the following EMF.Edit interface:</p>
<pre>
public interface ITreeItemProvider
{
public Collection getChildren(Object object);
public Object getParent(Object object);
...
}
</pre>
<p>Notice the similarity of this interface to the content provider interface
ITreeContentProvider described above. The AdapterFactoryContentProvider
implements the content provider interface by locating and then delegating to an
adapter (implementing the item provider interface) for the required item. The
shift in terminology from objects to items is deliberate: from the viewer's
point of view, they are items, not objects.</p>
<p>The EMF picture looks like this:</p>
<img src="images/EMF.Edit/image002.gif" border="0" width="607" height="231" alt="AdapterFactoryContentProvider delegation to item providers"/>
<p class="Note">Note: you can automatically generate an
ItemProviderAdapterFactory and ItemProvider classes for a given EMF model using
the generator provided with the EMF.Edit framework. More about this later.</p>
<p>The AdapterFactoryContentProvider is constructed with an adapter factory
which, like any other EMF adapter factory, serves to create and locate adapters
of a specific type (ItemProviders in this case). The content provider services a
request like getChildren() by calling adapt(item) on the
ItemProviderAdapterFactory, which will create or return the ItemProvider
(adapter) for the specified item. It then simply delegates to the getChildren()
method of the required interface (in this case, ITreeItemProvider).</p>
<p>The getChildren() method in AdapterFactoryContentProvider looks something
like this:</p>
<pre>
public Object[] getChildren(Object object)
{
ITreeItemContentProvider adapter =
(ITreeItemContentProvider)
adapterFactory.adapt(object, ITreeItemContentProvider.class);
return adapter.getChildren(object).toArray();
}
</pre>
<p>This same pattern is used for all of the content provider methods. As stated
previously, the AdapterFactoryContentProvider does nothing more than simply
delegate content provider methods to the specific item provider (adapter) that
knows how to service the request.</p>
<p>In the getChildren() method above, the object being passed to
adapterFactory.adapt() is a simple java.lang.Object (not an
org.eclipse.emf.ecore.EObject). This is an important feature of the EMF.Edit
framework. The framework has been carefully designed to accommodate views on EMF
models that may be different from the model itself (that is, views that suppress
objects or include additional, phantom, objects). To allow this mixture of EMF
and non-EMF objects, the framework base class for adapter factories provides an
implementation of adapt() that works something like this:</p>
<pre>
public Object adapt(Object object, Object type)
{
if (object instanceof Notifier)
return this.adapt((Notifier)object, type);
else
return object;
}
</pre>
<p>If the specified object is not an EMF
Notifier<sup><a class="footnote" href="#fn1">[1]</a><a name="ref1"> </a></sup>,
it returns the object itself. With this design, an item provider that wants to
add non-EMF items to a view can simply return (for example, from its
getChildren() method) any non-EMF object it wants. As long as the returned
object implements the viewer's required item provider interface (for example,
ITreeItemProvider) it will be treated like all the other EMF items.</p>
<p>This aspect of the design highlights why we prefer to call the
provider/adapter classes item providers instead of adapters. In nontrivial
applications, the view model (that is, the one provided by the viewer's content
provider) will often be a mixture of "real" (EMF) model objects whose
item providers also happen to be (EMF) adapters, and "phantom" objects
whose item providers are the objects themselves. So, while all of the adapters
are also item providers, the reverse is not necessarily true.</p>
<h3>JFace label providers</h3>
<p>In the previous sections we described how JFace viewers use a content
provider to get their content items. A similar approach is used for getting the
label image and text for the items being displayed by a viewer. Instead of
asking the items themselves for their labels, a viewer uses another object
called a label provider (similar to a content provider). A TreeViewer, for
example, delegates to an object implementing the ILabelProvider interface to get
the labels of the items in the tree.</p>
<h3>Providing labels for EMF objects</h3>
<p>The EMF.Edit framework uses the same mechanism for implementing label
providers for EMF models as it uses for providing the content. A generic label
provider implementation class, AdapterFactoryLabelProvider (working exactly like
AdapterFactoryContentProvider), delegates the ILabelProvider interface to the
item providers for the model (the same item providers that provide the content).
The expanded picture looks like this:</p>
<img src="images/EMF.Edit/image003.gif" border="0" width="608" height="326" alt="AdapterFactoryLabelProvider delegation to item providers"/>
<p>The content and label provider can (and usually will) delegate to the same
adapter factory and, consequently, the same item providers. Just as for the
content provider, the item providers are where the work actually gets done.</p>
<p>Using the EMF.Edit provider classes, a user might construct a TreeViewer for
some EMF model as follows:</p>
<pre>
myAdapterFactory = ...
treeViewer = new TreeViewer();
treeViewer.setContentProvider(new AdapterFactoryContentProvider(myAdapterFactory));
treeViewer.setLabelProvider(new AdapterFactoryLabelProvider(myAdapterFactory));
</pre>
<p>This TreeViewer can then be displayed in, for example, an editor window in
the usual (JFace prescribed) way.</p>
<p>You might be thinking this all seems quite trivial so far, but only because
all we've shown is how to delegate to somebody else (that is, the adapter
factory). We haven't implemented any of the methods yet, we've just delegated
them away. However, the method implementation is supported by EMF.Edit, which
includes a code generator that generates most of the item provider and factory
code for you. Before we get into that, we'll first take a look at how item
providers do their job.</p>
<h2><a name="providers">Item Provider Implementation Classes</a></h2>
<p>As shown in the previous section, the real work of providing content for EMF
models is done by the item provider adapters attached to the model. The number
and types of ItemProvider adapters in the previous diagram was left
intentionally vague. This is because the EMF.Edit framework supports two
different patterns for the item provider adapters:</p>
<ol>
<li>Stateful adapters - one adapter object for every object in the model</li>
<li>Singleton adapters - one adapter object for every type of object in the
model (recommended when possible)</li>
</ol>
<p>The item providers for a given model can be implemented with either of these
patterns or with a mixture of them both.</p>
<h3>Stateful item provider adapters</h3>
<p>With the first pattern, every object in a model has a one-to-one
correspondence with its adapter. Each adapter has a pointer (called target) to
the one-and-only object it's adapting.</p>
<p>The picture looks like this:</p>
<img src="images/EMF.Edit/image004.gif" border="0" width="615" height="257" alt="Stateful item provider adapters"/>
<p>As shown, this pattern doubles the number of objects in the application, and
therefore only makes sense in applications where the instances are needed to
carry additional state. That's why we call it the Stateful pattern.</p>
<h3>Singleton item provider adapters</h3>
<p>A better approach, that avoids most of the extra objects, is the Singleton
pattern. With this pattern we use a single item provider adapter for all the
items of the same type. It looks like this:</p>
<img src="images/EMF.Edit/image005.gif" border="0" width="615" height="257" alt="Singleton item provider adapters"/>
<p>In this picture, the objects have adapter pointers, as usual, but the item
providers (which are shared) have no pointers back to the objects. If you
remember the tree item provider interface that we looked at previously in the
content provider section, you may have noticed that all the methods took an
extra argument (an object):</p>
<pre>
public interface ITreeItemProvider
{
public Collection getChildren(Object object);
public Object getParent(Object object);
...
}
</pre>
<p>The object argument was added to each item provider interface specifically to
support this pattern. In the Stateful case, this object will always be the same
as the adapter's target.</p>
<p>Another question you may be asking yourself, is why don't we support a
"true" Singleton adapter pattern - that is, exactly one adapter for
all the objects? The answer is simply that while it is another possible pattern
(compatible with the EMF.Edit
framework)<sup><a class="footnote" href="#fn2">[2]</a><a name="ref2"> </a></sup>,
we discourage it because a completely dynamic implementation, although simple,
is difficult to customize (without lots of messy instanceof() checks). The
alternative of having typed item provider classes, whose inheritance hierarchy
mirrors the model's, provides a convenient dispatch point for implementing nice
clean object-oriented view code for a model.</p>
<h2><a name="commands">Modifying EMF Models Using Commands</a></h2>
<p>So far we've only shown how to view EMF models using content and label
providers. Another feature of the EMF.Edit framework is its support for
command-based editing of a model. We use the term "editing" to mean
undoable modification, as opposed to simple "writing" of a model.</p>
<h3>Editing domains</h3>
<p>The EMF.Edit interface EditingDomain is used to provide editing access to an
EMF model. Another EMF.Edit implementation class, AdapterFactoryEditingDomain,
works like the content and label providers by delegating its implementation to
the item providers (through the ItemProviderAdapterFactory):</p>
<img src="images/EMF.Edit/image006.gif" border="0" width="608" height="249" alt="Editing domain"/>
<p>As shown, it also provides access to a command stack through which all
modifications to the model will be made. The editing domain provides two
fundamental services:</p>
<ul>
<li>A factory for commands</li>
<li>A provider of model-specific details for generic EMF.Edit commands (like
Add, Remove, Copy, and so on).</li>
</ul>
<p>The best way to think about an editing domain is as the modification or write
provider to the model, while the content and label providers are the viewing or
read provider. Here's the big picture:</p>
<img src="images/EMF.Edit/image007.gif" border="0" width="607" height="331" alt="Read and write providers"/>
<h3>Modifying a model</h3>
<p>Let's look at a simple example of modifying a model.</p>
<p>Assume class Company has a one-to-many reference, named departments, to class
Department. To remove a department from the company (to implement, for example,
an editor delete action) we could simply write the following code:</p>
<pre>
Department d = ...
Company c = ...
c.getDepartments().remove(d);
</pre>
<p>Although simple, this code does nothing more than simply make the
modification.</p>
<p>If instead, we use the EMF.Edit remove command
(org.eclipse.emf.edit.command.RemoveCommand) to remove the department, we would
write the following:</p>
<pre>
Department d = ...
Company c = ...
EditingDomain ed = ...
RemoveCommand cmd =
new RemoveCommand(ed, c, CompanyPackage.eINSTANCE.getCompany_Departments(), d);
ed.getCommandStack().execute(cmd);
</pre>
<p>Deleting the department this way has several advantages:</p>
<ol>
<li>The deletion can be undone by calling ed.getCommandStack().undo().</li>
<li>We can find out if a model is dirty (to enable a save menu, for example) by
checking if there are commands on the command stack, assuming all modifications
are done using commands (which is what the EMF.Edit framework advocates).
</li>
<li>We could have checked if the command is valid (to enable a delete menu, for
example) before executing it by calling cmd.canExecute();</li>
</ol>
<p>Using commands this way, pervasively, is the way to enable all kinds of
function that the EMF.Edit framework can give you.</p>
<h3>Creating commands using the editing domain</h3>
<p>In the previous example we created a RemoveCommand using a simple new call.
This worked fine, but is not very reusable; the code fragment is doing a very
specific thing, removing a department from a company. If instead we want to
write, for example, a reusable delete action capable of deleting any kind of
object, we can do that by using the EditingDomain to help with the job.</p>
<p>The EditingDomain interface contains (among other things) a command factory
method, createCommand(), that can be used, instead of new, to create
commands:</p>
<pre>
public interface EditingDomain
{
...
Command createCommand(Class commandClass, CommandParameter commandParameter);
...
}
</pre>
<p>To use this method to create a command you would first need to create a
CommandParameter object, set command parameters into it, and then call the
create method, passing it the desired command class (for example,
RemoveCommand.class) and the parameters.</p>
<p>Rather then making clients go through all that, we use a convention of
providing static convenience create() methods in every command class. Using the
static create() method, you can create and execute a RemoveCommand like this:
</p>
<pre>
Department d = ...
EditingDomain ed = ...
Command cmd = RemoveCommand.create(ed, d);
ed.getCommandStack().execute(cmd);
</pre>
<p>As you can see, this is just a small syntactic change (RemoveCommand.create()
instead of new RemoveCommand). But there are fundamental differences. We only
passed one argument (that is, the object being removed), aside from the editing
domain, instead of the three arguments previously. Notice how this piece of code
can now be used to remove any kind of object. By delegating the creation of the
command to the editing domain, we're letting it fill in the missing
arguments.</p>
<h3>How does an Editing Domain handle a createCommand() request?</h3>
<p>To understand how it all works, let's follow through with the
RemoveCommand.create() call. As stated previously, the static create() method is
just a convenience method that delegates to the editing domain something like
this:</p>
<pre>
public static Command create(EditingDomain domain, Object value)
{
return domain.createCommand(
RemoveCommand.class,
new CommandParameter(null, null, Collections.singleton(value)));
}
</pre>
<p>The AdapterFactoryEditingDomain then takes the request and passes it along to
an item provider using the standard delegation pattern (as
AdapterFactorContentProvider delegated getChildren() earlier):</p>
<pre>
public Command createCommand(Class commandClass, CommandParameter commandParameter)
{
Object owner = ... // get the owner object for the command
IEditingDomainItemProvider adapter =
(IEditingDomainItemProvider)
adapterFactory.adapt(owner, IEditingDomainItemProvider.class);
return adapter.createCommand(owner, this, commandClass, commandParameter);
}
</pre>
<p class="Note">Note: if you look at the actual createCommand() method you will
notice that it's actually considerably more complicated. That's because it's
designed to also handle, among other things, deleting collections of objects at
once. This is, nevertheless, all it's doing conceptually.</p>
<p>The createCommand() method uses an owner object to access the item provider
to delegate to (that is, the owner is used in the adapterFactory.adapt() call).
The owner in our example will be the company object (that is, the parent of the
department being removed). The editing domain determines the owner by calling
the getParent() method on the item provider of the object being deleted.</p>
<p>The effect of all this is that the method createCommand() is finally called
on the item provider of the parent of the object being removed (that is, the
CompanyItemProvider for company c in the original code fragment). So, the
CompanyItemProvider could implement createCommand() by doing something like
this:</p>
<pre>
public class CompanyItemProvider ...
{
...
public Command createCommand(final Object object, ..., Class commandClass, ...)
{
if (commandClass == RemoveCommand.class)
{
return new RemoveCommand(object,
CompanyPackage.eINSTANCE.getCompany_Departments(),
commandParameter.getCollection());
}
...
}
}
</pre>
<p>This would get the job done, but there is a better way.</p>
<p>Every item provider class (that is also an EMF adapter) extends from an
EMF.Edit convenience base class, ItemProviderAdapter, which provides a default
implementation of, among other things, createCommand(). It implements
createCommand() for all the standard commands provided by the EMF.Edit framework
by calling out to a few simple methods (which are used for more than just this
purpose) that are implemented in the item provider subclasses. This is an
example of the Template Method design pattern.</p>
<p>To make our RemoveCommand example work, CompanyItemProvider only needs
to implement the following method:</p>
<pre>
public Collection getChildrenFeatures(Object object)
{
return Collections.singleton(CompanyPackage.eINSTANCE.getCompany_Departments());
}
</pre>
<p>As shown, this method returns the one or more features (in this case, only
the deparments reference) that are used for referencing children of the object.
After calling this method, the default implementation of createCommand() will
then figure out which feature to use (if more than one is returned) and create
the RemoveCommand with the right one.</p>
<h3>Overriding commands</h3>
<p>Another advantage of creating commands through an editing domain is that it
allows us to plug in different subclasses or completely different
implementations of standard commands and have standard editors just pick them
up. For example, assume that we want to do some extra cleanup whenever we remove
a department from a company. The simplest way to achieve that might be to create
a subclassof RemoveCommand called RemoveDepartmentCommand like this:</p>
<pre>
public class RemoveDepartmentCommand extends RemoveCommand
{
public void execute()
{
super.execute();
// do extra stuff ...
}
}
</pre>
<p>That part's easy enough.</p>
<p>Now, if our editor uses the static RemoveCommand.create() method (which calls
editingDomain.createCommand()) instead of new RemoveCommand(), then we can
easily substitute our RemoveDepartmentCommand for the standard RemoveCommand by
overriding createCommand() in the item provider like this:</p>
<pre>
public class CompanyItemProvider ...
{
...
public Command createCommand(final Object object, ...)
{
if (commandClass == RemoveCommand.class)
{
return new RemoveDepartmentCommand(...);
}
return super.createCommand(...);
}
}
</pre>
<p>Actually, if the command we want to specialize is one of the predefined ones
(like RemoveCommand), the substitution is even easier because the default
implementation of createCommand() dispatches the creation of each command to
command-specific convenience methods, something like this:</p>
<pre>
public Command createCommand(final Object object, ...
{
...
if (commandClass == RemoveCommand.class)
return createRemoveCommand(...);
else if (commandClass == AddCommand.class)
return createAddCommand(...);
else ...
}
</pre>
<p>So, we could have created our RemoveDepartmentCommand more simply by just
overriding createRemoveCommand(), instead of the createCommand() method itself:
</p>
<pre>
protected Command createRemoveCommand(...)
{
return new RemoveDepartmentCommand(...);
}
</pre>
<p>To summarize, the key point is that the editing domain is our hook to adjust
command parameters, including the command class itself, so we can easily control
the behavior of any editing command on our model.</p>
<h3>Model change notification</h3>
<p>One thing we haven't talked about yet is change notification. How do we get
the viewers to refresh after a command changes something in the model? The
answer is that it works using a combination of standard EMF adapter notification
and a viewer refresh mechanism provided by EMF.Edit.</p>
<p>When constructed, an AdapterFactoryContentProvider registers itself as a
listener (i.e. org.eclipse.emf.edit.provider.INotifyChangedListener) of its
adapter factory (which implements the
org.eclipse.emf.edit.provider.IChangeNotifier interface). The adapter factory in
turn passes itself to every item provider it creates so it can be the central
change notifier for the model. The AdapterFactoryContentProvider also records
(in the inputChanged() method) the viewer for which it is providing content, so
that it can update its viewer when it receives a change notification.</p>
<p>The following diagram shows how a change in an EMF model object (for example,
changing a company name) will make its way, through the adapter factory, back to
the viewers of the model.</p>
<img src="images/EMF.Edit/image008.gif" border="0" width="607" height="251" alt="Model change notification"/>
<p>Whenever an EMF object changes state, the method notifyChanged() is called on
all the object's adapters, including the item providers (in this case,
CompanyItemProvider). The notifyChanged() method in the item provider is
responsible for determining whether each event notification should be passed on
to the viewer, and if so, what type of update it should result in.</p>
<p>To do so, it wraps interesting notifications in a ViewerNotification, a
simple implementation of interface IViewerNotification. This interface extends
the basic Notification interface like this:</p>
<pre>
public interface IViewerNotification extends Notification
{
Object getElement();
boolean isContentRefresh();
boolean isLabelUpdate();
}
</pre>
<p>These methods specify which item in the viewer to update, whether to refresh
the content under that element, and whether to update the label of that element.
Since the item provider determines the children and label for an object, it must
also determine how to efficiently update the viewer.</p>
<p>The notifyChanged() method in class CompanyItemProvider looks like this:</p>
<pre>
public void notifyChanged(Notification notification)
{
...
switch (notification.getFeatureID(Company.class))
{
case CompanyPackage.COMPANY__NAME:
fireNotifyChanged(new ViewerNotification(notification, ..., false, true));
return;
case CompanyPackage.COMPANY__DEPARTMENT:
fireNotifyChanged(new ViewerNotification(notification, ..., true, false));
return;
}
super.notifyChanged(notification);
}
</pre>
<p>In this implementation, a change to the name attribute results in a label
update, and a change to the department reference causes a content refresh. Any
other change notifications have no effect on the viewer.</p>
<p>The fireNotifyChanged() method is a convenience method in class
ItemProviderAdapter (the base class of all the item provider adapters) which
simply forwards the notification to the adapter
factory<sup><a class="footnote" href="#fn3">[3]</a><a name="ref3"> </a></sup>.
The adapter factory (change notifier) proceeds to dispatch the notification to
all its listeners (in this example, just the tree viewer's content provider).
Finally, the content provider updates the viewer, as directed by the
notification.</p>
<h3>Composed adapter factories</h3>
<p>EMF models are often tied together by cross-model references. Whenever you
need to build an application to edit or display objects that span more than one
EMF model, you'll need an adapter factory capable of adapting the union of the
objects from the two (or more) models.</p>
<p>Often, you already have adapter factories for the individual models and all
that you need is to glue them together. Another EMF.Edit convenience class,
ComposedAdapterFactory, can be used for this purpose:</p>
<img src="images/EMF.Edit/image009.gif" border="0" width="607" height="250" alt="Adapter factory composition"/>
<p>A ComposedAdapterFactory is used to provide a common interface to other
adapter factories, to which it simply delegates its implementation.</p>
<p>To set up a composed adapter factory, you would write something like this:
</p>
<pre>
model1AdapterFactory = ...
model2AdapterFactory = ...
ComposedAdapterFactory myAdapterFactory = new ComposedAdapterFactory();
myAdapterFactory.addAdapterFactory(model1AdapterFactory);
myAdapterFActory.addAdapterFActory(model2AdapterFactory);
myContentProvider = new AdapterFactoryContentProvider(myAdapterFactory);
...
</pre>
<h2><a name="generator">Using the EMF.Edit Code Generator</a></h2>
<p class="Note">Note: a step-by-step tutorial for generating an EMF model, as
well as an EMF.Edit editor, can be found in
<a href="../../tutorials/clibmod/clibmod.html">Tutorial: Generating an EMF
Model</a>.</p>
<p>Given an EMF model definition, the EMF.Edit code generator can produce a
fully functional editor tool that will allow you to view instances of the model
using several common viewers and to add, remove, cut, copy, and paste model
objects, or modify the objects in a standard property sheet, all with full
undo/redo support.</p>
<p>The EMF.Edit generator produces complete working plug-ins that include the
following:</p>
<ol>
<li>ItemProviderAdapterFactory</li>
<li>ItemProviders (one per model class)</li>
<li>Editor</li>
<li>ModelWizard</li>
<li>ActionBarContributor</li>
<li>Plugin</li>
<li>plugin.xml</li>
<li>Icons</li>
</ol>
<p>Once generated, the editor should run. It will come up, but it may or may not
be working as you expected (that is, the default choices that the generator made
may have been inappropriate for your model). It should, however, be pretty easy
to tweak the generated code in a few places to quickly get a basic working
editor up and running.</p>
<p>The following takes a closer look at some of the more interesting generated
classes.</p>
<h3>ItemProviderAdapterFactory</h3>
<p>The generated ItemProviderAdapterFactory is a simple subclass of the
generated AdapterFactory class that you got when you generated your EMF model.
</p>
<p class="Note">Note: the generated EMF adapter factory creates adapters by
dispatching to a type-specific create() method that subclasses (like
ItemProviderAdapterFactory) are required to override. The EMF adapter factory
(for example, ABCAdapterFactory) uses another generated class, (ABCSwitch), to
implement the dispatch efficiently.</p>
<p>When using the Stateful pattern, the adapter factory create methods simply
return a new object like this:</p>
<pre>
class ABCItemProviderAdapterFactory extends ABCAdapterFactoryImpl
{
...
public Adapter createCompanyAdapter()
{
return new CompanyItemProvider(this);
}
...
}
</pre>
<p>If, instead, the Singleton pattern is used, the adapter factory also keeps
track of the singleton instance and returns it for each call:</p>
<pre>
protected DepartmentItemProvider departmentItemProvider;
public Adapter createDepartmentAdapter()
{
if (departmentItemProvider == null)
{
departmentItemProvider = new DepartmentItemProvider(this);
}
return departmentItemProvider;
}
</pre>
<h3>The ItemProvider classes</h3>
<p>For each class in the model, a corresponding item provider class is
generated. The generated item providers mix in all the interfaces needed to
support the standard viewers, commands, and the property sheet:</p>
<pre>
public class DepartmentItemProvider extends ...
implements
IEditingDomainItemProvider,
IStructuredItemContentProvider,
ITreeItemContentProvider,
IItemLabelProvider,
IItemPropertySource
{
...
}
</pre>
<p>If a model class is a root (that is, it has no explicit base class) then the
generated item provider will extend from the EMF.Edit item provider base class,
ItemProviderAdapter:</p>
<pre>
public class EmployeeItemProvider extends ItemProviderAdapter ...
</pre>
<p>If, instead, the model class inherits from a base class, then the generated
item provider will extend from the base item provider like this:</p>
<pre>
public class EmployeeItemProvider extends PersonItemProvider ...
</pre>
<p>For a multiply inheriting class, the generated item provider will extend from
the first base class's item provider (as in the single inheritance case) and it
will implement the provider function for the rest of the base classes.</p>
<p>If you look at the generated item provider classes, you'll notice that a lot
of their function is actually implemented in the item provider base class. The
most important functions of the generated item provider subclasses are:</p>
<ol>
<li>Identifying the children and parent</li>
<li>Setting up the property descriptors</li>
<li>Passing on the "interesting" notification events.</li>
</ol>
<h3>Editor and ModelWizard</h3>
<p>The generated Editor and ModelWizard show how to put all the other generated
pieces together with standard JFace components to produce a working editor.</p>
<p>The ModelWizard can be used to create new resources of the model's type. If
instead, you already have a resource that was created some other way, you can
import it into your desktop workspace and launch the editor on it, bypassing the
ModelWizard completely.</p>
<br/><hr noshade="noshade" size="1"/>
<p><sup><a class="footnote" href="#ref1">[1]</a><a name="fn1"> </a></sup>Notifier
is the base interface in EMF for objects that can register adapters and send
notifications to them. It is extended by EObject, the base interface for all
model objects.</p>
<p><sup><a class="footnote" href="#ref2">[2]</a><a name="fn2"> </a></sup>Actually,
EMF adapter factories are inheritance driven, so you can choose to use a base
adapter to handle subclasses at any level in your model, EObject being the
extreme case.</p>
<p><sup><a class="footnote" href="#ref3">[3]</a><a name="fn3"> </a></sup>In
addition to the adapter factory, which acts as the change notifier for viewers,
an ItemProviderAdapter can also have other (direct) listeners which are also
called in ItemProviderAdapter.fireNotifyChanged().</p>
</body>
</html>
|