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 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616
|
<!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 Overview</title>
</head>
<body lang="EN-US" xml:lang="EN-US">
<h1>The Eclipse Modeling Framework (EMF) Overview</h1>
<p>
Last updated: June 16, 2005 (accessibility update)
</p>
<p>
This paper presents a basic overview of EMF and its code generator patterns. For
a more complete description of all the features of EMF, refer to
<a href="https://www.informit.com/title/9780321331885" target="_blank">EMF: Eclipse Modeling
Framework, Second Edition</a> (Addison-Wesley Professional, 2008) or to the
Javadoc for the framework classes themselves.
</p>
<h2>Contents</h2>
<p><a href="#introduction">Introduction</a><br/>
<a href="#definition">Defining an EMF Model</a><br/>
<a href="#generation">Generating a Java Implementation</a><br/>
<a href="#programming">Using the Generated EMF Classes</a><br/>
<a href="#advanced">Advanced Topics</a></p>
<h2><a name="introduction">Introduction</a></h2>
<p>
EMF is a Java framework and code generation facility for building tools and
other applications based on a structured model. For those of you that have
bought into the idea of object-oriented modeling, EMF helps you rapidly turn
your models into efficient, correct, and easily customizable Java code. For
those of you that aren't necessarily sold on the value of formal models, EMF is
intended to provide you with the same benefits and a very low cost of entry.
</p>
<p>
So, what do we mean when we say model? When talking about modeling, we generally
think about things like Class Diagrams, Collaboration Diagrams, State Diagrams,
and so on. UML (Unified Modeling Language) defines a (the) standard notation for
these kinds of diagrams. Using a combination of UML diagrams, a complete model
of an application can be specified. This model may be used purely for
documentation or, given appropriate tools, it can be used as the input from
which to generate part of or, in simple cases, all of an application.
</p>
<p>
Given that this kind of modeling typically requires expensive Object Oriented
Analysis and Design (OOA/D) tools, you might be questioning our assertion,
above, that EMF provides a low cost of entry. The reason we can say that is
because an EMF model requires just a small subset of the kinds of things that
you can model in UML, specifically simple definitions of the classes and their
attributes and relations, for which a full-scale graphical modeling tool is
unnecessary.
</p>
<p>
While EMF uses XMI (XML Metadata Interchange) as its canonical form of a model
definition<sup><a class="footnote" href="#fn1">[1]</a><a name="ref1"> </a></sup>,
you have several ways of getting your model into that form:
</p>
<ul><li>Create the XMI document directly, using an XML or text editor</li>
<li>Export the XMI document from a modeling tool such as Rational Rose</li>
<li>Annotate Java interfaces with model properties</li>
<li>Use XML Schema to describe the form of a serialization of the model</li></ul>
<p>
The first approach is the most direct, but generally only appeals to XML gurus.
The second choice is the most desirable if you are already using full-scale
modeling tools. The third approach provides pure Java programmers a low-cost way
to get the benefits of EMF and its code generator using just a basic Java
development environment (for example, Eclipse's Java Development Tools). The
last approach is most applicable in creating an application that must read or
write a particular XML file format.
</p>
<p>
Once you specify an EMF model, the EMF generator can create a corresponding set
of Java implementation classes. You can edit these generated classes to add
methods and instance variables and still regenerate from the model as needed:
your additions will be preserved during the regeneration. If the code you added
depends on something that you changed in the model, you will still need to
update the code to reflect those changes; otherwise, your code is completely
unaffected by model changes and regeneration.
</p>
<p>
In addition to simply increasing your productivity, building your application
using EMF provides several other benefits like model change notification,
persistence support including default XMI and schema-based XML serialization, a
framework for model validation, and a very efficient reflective API for
manipulating EMF objects generically. Most important of all, EMF provides the
foundation for interoperability with other EMF-based tools and applications.
</p>
<p>
EMF consists of two fundamental frameworks: the core framework and EMF.Edit. The
core framework provides basic generation and runtime support to create Java
implementation classes for a model. EMF.Edit extends and builds on the core
framework, adding support for generating adapter classes that enable viewing
and command-based (undoable) editing of a model, and even a basic working
model editor. The following sections describe the main features of the core
EMF framework. EMF.Edit is described in a separate paper,
<a href="../../references/overview/EMF.Edit.html">EMF.Edit Overview</a>. For instructions on how to run the EMF and EMF.Edit generator,
refer to <a href="../../tutorials/clibmod/clibmod.html">Tutorial: Generating an
EMF Model</a>.
</p>
<h3>EMF Relation to OMG MOF</h3>
<p>
For those of you that are familiar with OMG (Object Management Group) MOF (Meta
Object Facility), you may be wondering how EMF relates to it. Actually, EMF
started out as an implementation of the MOF specification but evolved from there
based on the experience we gained from implementing a large set of tools using
it. EMF can be thought of as a highly efficient Java implementation of a core
subset of the MOF API. However, to avoid any confusion, the MOF-like core meta
model in EMF is called Ecore.
</p>
<p>
In the current proposal for MOF 2.0, a similar subset of the MOF model, which it
calls EMOF (Essential MOF), is separated out. There are small, mostly naming
differences between Ecore and EMOF; however, EMF can transparently read and
write serializations of EMOF.
</p>
<h2><a name="definition">Defining an EMF Model</a></h2>
<p>
To help describe EMF, we'll start by assuming we have a trivial, one-class model
like this:
</p>
<img src="images/EMF/image001.gif" width="87" height="89" alt="One-class model: Book with title : String and pages : int"/>
<p>
The model shows a single class called Book with two attributes: title of type
String and pages of type int.
</p>
<p>
Our model definition, trivial as it is, can be provided to the EMF code
generator in a number of ways.
</p>
<h3>UML</h3>
<p>
If you have a modeling tool that works with
EMF<sup><a class="footnote" href="#fn2">[2]</a><a name="ref2"> </a></sup>,
you can simply draw the class diagram as shown above.
</p>
<h3>XMI</h3>
<p>
Alternatively, we could describe the model directly in an XMI document that
would look something like this:
</p>
<pre>
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
name="library "nsURI="http:///library.ecore" nsPrefix="library">
<eClassifiers xsi:type="ecore:EClass" name="Book">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="title"
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="pages"
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
</eClassifiers>
</ecore:EPackage></pre>
<p>
The XMI document contains all the same information as the class diagram, but a
little less compactly. Every class and attribute in a diagram has a
corresponding class or attribute definition in the XMI document.
</p>
<h3>Annotated Java</h3>
<p>
For those of you that have neither a graphical modeling tool nor an interest in
trying to enter all the XMI syntax by hand, a third option is available for
describing your model. Since the EMF generator is a code-merging generator,
by providing partial Java interfaces (annotated with model information)
ahead of time, the generator can use the interfaces as its generation
metadata and merge the generated code with the rest of the implementation.
</p>
<p>
We could have defined our Book model class in Java like this:
</p>
<pre>
/**
* @model
*/
public interface Book
{
/**
* @model
*/
String getTitle();
/**
* @model
*/
int getPages();
}</pre>
<p>
With this approach, we provide all the model information in the form of Java
interfaces with standard get
methods<sup><a class="footnote" href="#fn3">[3]</a><a name="ref3"> </a></sup>
to identify the attributes and references. The @model tag is used to identify to
the code generator which interfaces, and parts of those interfaces, correspond
to model elements and therefore require code generation.
</p>
<p>
For our simple example, all of our model information is actually available
though Java introspection of this interface, so no additional model information
is needed. In the general case, however, the @model tag may be followed by
additional details about the model element. If for example, we wanted the pages
attribute to be read-only (that is, no generation of a set method), we would
need to add the following to the annotation:
</p>
<pre>
/**
* @model changeable="false"
*/
int getPages();</pre>
<p>
Because only information that is different from the default needs to be
specified, annotations can be kept simple and concise.
</p>
<h3>XML Schema</h3>
<p>
Sometimes, you might want to describe a model with a schema that specifies how
instance serializations should look. This can be useful for writing an
application that must use XML to integrate with an existing application or to
comply with a standard. Here is how we would specify a schema that's equivalent
to our simple book model:
</p>
<pre>
<xsd:schema targetNamespace="http:///library.ecore"
xmlns="http:///library.ecore" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="Book">
<xsd:sequence>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="pages" type="xsd:integer"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema></pre>
<p>
This approach differs somewhat from the other three, mainly because EMF must
apply certain restrictions to the serialization that it eventually uses, to
ensure compliance with the schema. As a result, the model that is a created for
a schema looks slightly different from one specified in one of the other ways.
The details of these differences are beyond the scope of this overview.
</p>
<p>
In the remainder of this paper, we'll use UML diagrams for their clarity and
conciseness. All of the modeling concepts we'll illustrate could also be
expressed using annotated Java or directly with XMI, and most have XML Schema
equivalents. Regardless of how the information is provided, the code generated
by EMF will be the same.
</p>
<h2><a name="generation">Generating a Java Implementation</a></h2>
<p>
For each class in the model, a Java interface and corresponding implementation
class will be generated. In our example, the generated interface for Book
looks like this:
</p>
<pre>
public interface Book extends EObject
{
String getTitle();
void setTitle(String value);
int getPages();
void setPages(int value);
}</pre>
<p>
Each generated interface contains getter and setter methods for each attribute
and reference of the corresponding model class.
</p>
<p>
Interface Book extends the base interface EObject. EObject is the EMF equivalent
of java.lang.Object, that is, it is the base of every EMF class. EObject and its
corresponding implementation class EObjectImpl (which we will look at later)
provide a relatively lightweight base class that lets Book participate in the
EMF notification and persistence frameworks. Before we start looking at what
exactly EObject brings into the mix, let's continue looking at how EMF generates
Book.
</p>
<p>
Each generated implementation class includes implementations of the getters and
setters defined in the corresponding interface, plus some other methods required
by the EMF framework.
</p>
<p>
Class BookImpl will include, among other things, implementations of the title
and pages accessors. The pages attribute, for example, has the following
generated implementation:
</p>
<pre>
public class BookImpl extends EObjectImpl implements Book
{
...
protected static final int PAGES_EDEFAULT = 0;
protected int pages = PAGES_EDEFAULT;
public int getPages()
{
return pages;
}
public void setPages(int newPages)
{
int oldPages = pages;
pages = newPages;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET, ..., oldPages, pages));
}
...
}</pre>
<p>
The generated get method is optimally efficient. It simply returns an instance
variable representing the attribute.
</p>
<p>
The set method, although a little more complicated, is also quite efficient. In
addition to setting the instance variable pages, the set method needs to send
change notification to any observers that may be listening to the object by
calling the eNotify() method. To optimize the case where there are no observers
(for example, in a batch application), construction of the notification object
(ENotificationImpl) and the call to eNotify() are guarded by a call to
eNotificationRequired(). The default implementation of eNotificationRequired()
simply checks if there are any observers (adapters) attached to the object.
Therefore, when EMF objects are used without observers, the call to
eNotificationRequired() amounts to nothing more than an efficient null pointer
check, which is inlined when using a JIT compiler.
</p>
<p>
The generated accessor patterns for other types of attributes, like the
String-typed title attribute, have some minor differences but are fundamentally
the same as those shown for
pages<sup><a class="footnote" href="#fn4">[4]</a><a name="ref4"> </a></sup>.
</p>
<p>
The generated accessors for references, especially two-way ones, are a little
more complicated and start to show the real value of the EMF generator.
</p>
<h3>One-way references</h3>
<p>
Let's expand our example model with another class Writer that has an association
with class Book:
</p>
<img src="images/EMF/image002.gif" width="303" height="89" alt="One-way reference: Book has a single author of class Writer, which contains a name : String"/>
<p>
The association between a book and its writer is, in this example, a single
one-way reference. The reference (role) name used to access the Writer from
a Book is author.
</p>
<p>
Running this model through the EMF generator will, in addition to generating the
new interface Writer and implementation class WriterImpl, generate
additional get and set methods in interface Book:
</p>
<pre>
Writer getAuthor();
void setAuthor(Writer value);</pre>
<p>
Since the author reference is one-way, the implementation of the setAuthor()
method looks much like a simple data setter, like the earlier one for
setPages():
</p>
<pre>
public void setAuthor(Writer newAuthor)
{
Writer oldAuthor = author;
author = newAuthor;
if(eNotificationRequired())
eNotify(new ENotificationImpl(this, ...));
}</pre>
<p>
The only difference is that here we're setting an object pointer instead of just
a simple data field.
</p>
<p>
Because we're dealing with an object reference, however, the getAuthor() method
is a little more complicated. This is because the get method for some types of
references, including the type of author, needs to deal with the possibility
that the referenced object (in this case Writer) may persist in a different
resource (document) from the source object (in this case Book). Because the EMF
persistence framework uses a lazy loading scheme, an object pointer (in this
case author) may at some point in time be a proxy for the object, instead of the
actual referenced
object<sup><a class="footnote" href="#fn5">[5]</a><a name="ref5"> </a></sup>.
As a result, the getAuthor() method looks like this:
</p>
<pre>
public Writer getAuthor()
{
if (author != null && author.eIsProxy())
{
Writer oldAuthor = author;
author = (Writer)eResolveProxy((InternalEObject)author);
if (author != oldAuthor)
{
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.RESOLVE, ...));
}
}
return author;
}</pre>
<p>
Instead of simply returning the author instance variable, we first call the
inherited framework method eIsProxy() method to check if the reference is a
proxy, and then call eResolveProxy() if it is. The latter method calls
EcoreUtil.resolve(), a static utility method that attempts to load the target
object's document, and consequently the object, using the proxy's URI. If
successful, it will return the resolved object. If, however, the document fails
to load, it will just return the proxy
again<sup><a class="footnote" href="#fn6">[6]</a><a name="ref6"> </a></sup>.
</p>
<h3>Two-way references</h3>
<p>
Now that we understand how proxy resolution affects the get pattern for certain
types of references, we can look at how the set pattern changes when an
association is made two-way. Let's change our one-way author association to
this:
</p>
<img src="images/EMF/image003.gif" width="303" height="89" alt="Two-way reference: an author may have written 0 or more books, but a book can only have one author"/>
<p>
The association is now two-way, as indicated by the lack of an arrowhead on the
Writer end of the association line. The role name used to access Books from a
Writer is books.
</p>
<p>
If we regenerate our model, the getAuthor() method will be unaffected, but
setAuthor() will now look like this:
</p>
<pre>
public void setAuthor(Writer newAuthor)
{
if (newAuthor != author)
{
NotificationChain msgs = null;
if (author != null)
msgs = ((InternalEObject)author).eInverseRemove(this, ..., msgs);
if (newAuthor != null)
msgs = ((InternalEObject)newAuthor).eInverseAdd(this, ..., msgs);
msgs = basicSetAuthor(newAuthor, msgs);
if (msgs != null) msgs.dispatch();
}
else if (eNotificationRequired())
eNotify(new ENotificationImpl(this, ...)); // send "touch" notification
}</pre>
<p>
As you can see, when setting a two-way reference like author, the other end of
the reference needs to be set as well (by calling eInverseAdd()). We also need
to remove the inverse of any previous author (by calling eInverseRemove())
because in our model the author reference is singular (that is, a book can only
have one
author)<sup><a class="footnote" href="#fn7">[7]</a><a name="ref7"> </a></sup>
and therefore this book cannot be in more than one Writer's books reference.
Finally, we set the author reference by calling another generated method
(basicSetAuthor()) which looks like this:
</p>
<pre>
public NotificationChain basicSetAuthor(Writer newAuthor, NotificationChain msgs)
{
Writer oldAuthor = author;
author = newAuthor;
if (eNotificationRequired())
{
ENotificationImpl notification = new ENotificationImpl(this, ...);
if (msgs == null) msgs = notification; else msgs.add(notification);
}
return msgs;
}</pre>
<p>
This method looks very similar to the one-way reference set method, except that
if the msgs argument is non-null, the notification gets added to it, intsead of
being fired
directly<sup><a class="footnote" href="#fn8">[8]</a><a name="ref8"> </a></sup>.
Because of all the forward/reverse adding/removing during a two-way reference
set operation, as many as four (three in this particular example) different
notifications may be generated. A NotificationChain is used to collect all these
individual notifications so their firing can be deferred until after all the
state changes have been made. The queued-up notifications are sent by calling
msgs.dispatch(), as shown in the setAuthor() method, above.
</p>
<h3>Multiplicity-many references</h3>
<p>
You may have noticed in our example that the books association (from Writer to
Book) is multiplicity many (that is, 0..*). In other words, one writer may
have written many books. Multiplicity-many references (that is, any
reference where the upper bound is greater than 1) in EMF are manipulated
using a collection API, so only a get method is generated in the
interface:
</p>
<pre>
public interface Writer extends EObject
{
...
EList getBooks();
}</pre>
<p>
Notice that getBooks() returns an EList as opposed to a java.util.List.
Actually, they are almost the same. EList is an EMF subclass of
java.util.List that adds two move methods to the API. Other than that, from
a client perspective, you can consider it a standard Java List. For
example, to add a book to the books association, you can simply call:
</p>
<pre>
aWriter.getBooks().add(aBook);</pre>
<p>
or to iterate over them you would do something like this:
</p>
<pre>
for (Iterator iter = aWriter.getBooks().iterator(); iter.hasNext(); )
{
Book book = (Book)iter.next();
...
}</pre>
<p>
As you can see, from a client perspective, the API for manipulating
multiplicity-many references is nothing special. However, because the books
reference is part of a two-way association (it's the inverse of Book.author),
we still need to do all the fancy inverse handshaking that we showed for the
setAuthor() method. Looking at the implementation of the getBooks() method in
WriterImpl shows us how the multiplicity-many case gets handled:
</p>
<pre>
public EList getBooks()
{
if (books == null)
{
books = new EObjectWithInverseResolvingEList(Book.class, this,
LibraryPackage.WRITER__BOOKS, LibraryPackage.BOOK__AUTHOR);
}
return books;
}</pre>
<p>
The getBooks() method returns a special implementation class,
EObjectWithInverseResolvingEList, which is constructed with all the information
it needs to do the reverse handshaking during add and remove calls. EMF actually
provides 20 different specialized EList
implementations<sup><a class="footnote" href="#fn9">[9]</a><a name="ref9"> </a></sup>
to efficiently implement all types of multiplicity-many features.
For one-way associations (that is, those with no inverse) we use
EObjectResolvingEList. If the reference doesn't need proxy resolution we'd use
EObjectWithInverseEList or EObjectEList, and so on.
</p>
<p>
So for our example, the list used to implement the books reference is created
with the argument LibraryPackage.BOOK__AUTHOR (a generated static int constant
representing the reverse feature). This will be used during the add() call to
call eInverseAdd() on the Book, similar to the way eInverseAdd() was called on
the Writer during setAuthor(). Here's what eInverseAdd() looks like in class
BookImpl:
</p>
<pre>
public NotificationChain eInverseAdd(InternalEObject otherEnd, int featureID,
Class baseClass, NotificationChain msgs)
{
if (featureID >= 0)
{
switch (eDerivedStructuralFeatureID(featureID, baseClass))
{
case LibraryPackage.BOOK__AUTHOR:
if (author != null)
msgs = ((InternalEObject)author).eInverseRemove(this, .., msgs);
return basicSetAuthor((Writer)otherEnd, msgs);
default:
...
}
}
...
}</pre>
<p>
It first calls eInverseRemove() to remove any previous author (as we described
previously when we looked at the setAuthor() method), and then it calls
basicSetAuthor() to actually set the reference. Although our particular example
only has one two-way reference, eInverseAdd() uses a switch statement that
includes a case for every two-way reference available on class
Book<sup><a class="footnote" href="#fn10">[10]</a><a name="ref10"> </a></sup>.
</p>
<h3><a name="containment">Containment references</a></h3>
<p>
Let's add a new class, Library, which will act as the container for Books.
</p>
<img src="images/EMF/image004.gif" width="308" height="89" alt="Containment reference: a Library contains 0 or more books"/>
<p>
The containment reference is indicated by the black diamond on the Library end
of the association. In full, the association indicates that a Library
aggregates, by value, 0 or more Books. By-value aggregation (containment)
associations are particularly important because they identify the parent or
owner of a target instance, which implies the physical location of the object
when persisted.
</p>
<p>
Containment affects the generated code in several ways. First of all, because a
contained object is guaranteed to be in the same resource as its container,
proxy resolution isn't needed. Therefore, the generated get method in
LibraryImpl will use a non-resolving EList implementation class:
</p>
<pre>
public EList getBooks()
{
if (books == null)
{
books = new EObjectContainmentEList(Book.class, this, ...);
}
return books;
}</pre>
<p>
In addition to not performing proxy resolution, an EObjectContainmentEList also
implements the contains() operation very efficiently (that is, in a constant
time, vs. linear time in the general case). This is particularly important
because duplicate entries are not allowed in EMF reference lists, so contains()
is called during add() operations as well.
</p>
<p>
Because an object can only have one container, adding an object to a containment
association also means removing the object from any container it's currently in,
regardless of the actual association. For example, adding a Book to a Library's
books list may involve removing it from some other Library's books list. That's
no different than any other two-way association where the inverse has
multiplicity 1. Let's assume, however, that the Writer class also had a
containment association to Book, called ownedBooks. Then, if a given book
instance is in the ownedBooks list of some Writer, when we add it to a Library's
books reference, it would need to be removed from the Writer first.
</p>
<p>
To implement this kind of thing efficiently, the base class EObjectImpl has an
instance variable (eContainer) of type EObject that it uses to store the
container generically. As a result, containment references are always
implicitly two-way. To access the Library from a Book, you can write something
like this:
</p>
<pre>
EObject container = book.eContainer();
if (container instanceof Library)
library = (Library)container;</pre>
<p>
If you want to avoid the downcast, you can change the association to be
explicitly two-way instead:
</p>
<img src="images/EMF/image005.gif" width="308" height="89" alt="Two-way containment reference: a Library contains 0 or more books; books are contained by a Library"/>
<p>
and let EMF generate a nice typesafe get method for you:
</p>
<pre>
public Library getLibrary()
{
if (eContainerFeatureID != LibraryPackage.BOOK__LIBRARY) return null;
return (Library)eContainer;
}</pre>
<p>
Notice that the explicit get method uses the eContainer variable from
EObjectImpl instead of a generated instance variable as we saw previously
for non-container references (like getAuthor(),
above)<sup><a class="footnote" href="#fn11">[11]</a><a name="ref11"> </a></sup>.
</p>
<h3>Enumeration attributes</h3>
<p>
So far, we've looked at how EMF handles simple attributes and various types of
references. Another commonly used type of attribute is an enumeration.
Enumeration-type attributes are implemented using the Java typesafe enum
pattern<sup><a class="footnote" href="#fn12">[12]</a><a name="ref12"> </a></sup>.
</p>
<p>
If we add an enumeration attribute, category, to class Book:
</p>
<img src="images/EMF/image006.gif" width="354" height="108" alt="Enumeration attribute and definition: Book has a category of class BookCategory, which is an enumeration of Mystery, ScienceFiction, and Biography"/>
<p>
and regenerate the implementation classes, interface Book will now include a
getter and setter for category:
</p>
<pre>
BookCategory getCategory();
void setCategory(BookCategory value);</pre>
<p>
In the generated interface, the category methods use a typesafe enumeration
class called BookCategory. This class defines static constants for the
enumeration's values and other convenience methods, like this:
</p>
<pre>
public final class BookCategory extends AbstractEnumerator
{
public static final int MYSTERY = 0;
public static final int SCIENCE_FICTION = 1;
public static final int BIOGRAPHY = 2;
public static final BookCategory MYSTERY_LITERAL =
new BookCategory(MYSTERY, "Mystery");
public static final BookCategory SCIENCE_FICTION_LITERAL =
new BookCategory(SCIENCE_FICTION, "ScienceFiction");
public static final BookCategory BIOGRAPHY_LITERAL =
new BookCategory(BIOGRAPHY, "Biography");
public static final List VALUES = Collections.unmodifiableList(...));
public static BookCategory get(String name)
{
...
}
public static BookCategory get(int value)
{
...
}
private BookCategory(int value, String name)
{
super(value, name);
}
}</pre>
<p>
As shown, the enumeration class provides static int constants for the
enumerations's values as well as static constants for the enumeration's
singleton literal objects themselves. The int constants have the same names as
the model's literal names<sup><a class="footnote" href="#fn13">[13]</a><a name="ref13"> </a></sup>.
The literal constants have the same names with _LITERAL appended.
</p>
<p>
The constants provide convenient access to the literals when, for example,
setting the category of a book:
</p>
<pre>
book.setCategory(BookCategory.SCIENCE_FICTION_LITERAL);</pre>
<p>
The BookCategory constructor is private and therefore the only instances of the
enumeration class that will ever exist are the ones used for the statics
MYSTERY_LITERAL, SCIENCE_FICTION_LITERAL, and BIOGRAPHY_LITERAL. As a result,
equality comparisons (that is .equals() calls) are never needed. Literals can
always be reliably compared using the simpler and more efficient == operator,
like this:
</p>
<pre>
book.getCategory() == BookCategory.MYSTERY_LITERAL</pre>
<p>
When comparing against many values, a switch statement using the int values is
better yet:
</p>
<pre>
switch (book.getCategory().value()) {
case BookCategory.MYSTERY:
// do something ...
break;
case BookCategory.SCIENCE_FICTION:
...
}</pre>
<p>
For situations where only the literal name (String) or value (int) is available,
convenience get() methods, which can be used to retrieve the corresponding
literal object, are also generated in the enumeration class.
</p>
<h3>Factories and packages</h3>
<p>
In addition to the model interfaces and implementation classes, EMF generates
at least two more interfaces (and implementation classes): a factory and a
package.
</p>
<p>
The factory, as its name implies, is used for creating instances of your model
classes, while the package provides some static constants (for example, the
feature constants used by the generated methods) and convenience methods for
accessing your model's
metadata<sup><a class="footnote" href="#fn14">[14]</a><a name="ref14"> </a></sup>.
</p>
<p>
Here is the factory interface for the book example:
</p>
<pre>
public interface LibraryFactory extends EFactory
{
LibraryFactory eINSTANCE = new LibraryFactoryImpl();
Book createBook();
Writer createWriter();
Library createLibrary();
LibraryPackage getLibraryPackage();
}</pre>
<p>
As shown, the generated factory provides a factory method (create) for each
class defined in the model, an accessor for your model's package, and a static
constant reference (that is, eINSTANCE) to the factory singleton.
</p>
<p>
The LibraryPackage interface provides convenient access to all the metadata of
our model:
</p>
<pre>
public interface LibraryPackage extends EPackage
{
...
LibraryPackage eINSTANCE = LibraryPackageImpl.init();
static final int BOOK = 0;
static final int BOOK__TITLE = 0;
static final int BOOK__PAGES = 1;
static final int BOOK__CATEGORY = 2;
static final int BOOK__AUTHOR = 3;
...
static final int WRITER = 1;
static final int WRITER__NAME = 0;
...
EClass getBook();
EAttribute getBook_Title();
EAttribute getBook_Pages();
EAttribute getBook_Category();
EReference getBook_Author();
...
}</pre>
<p>
As you can see, the metadata is available in two forms: int constants and the
Ecore meta objects themselves. The int constants provide the most efficient way
to pass around meta information. You may have noticed that the generated methods
use these constants in their implementations. Later, when we look at how EMF
adapters can be implemented, you'll see that the constants also provide the most
efficient way to determine what has changed when handling notifications. Also,
just like the factory, the generated package interface provides a static
constant reference to its singleton implementation.
</p>
<h3>Generating classes with super classes</h3>
<p>
Let's say we want to create a subclass, SchoolBook, of our Book model class,
like this:
</p>
<img src="images/EMF/image007.gif" width="164" height="198" alt="Single inheritance: a SchoolBook extends Book"/>
<p>
The EMF generator handles single inheritance as you'd expect: the generated
interface extends the super interface:
</p>
<pre>
public interface SchoolBook extends Book</pre>
<p>
and the implementation class extends the super implementation class:
</p>
<pre>
public class SchoolBookImpl extends BookImpl implements SchoolBook</pre>
<p>
As in Java itself, multiple interface inheritance is supported, but each EMF
class can only extend one implementation base class. Therefore, when we have a
model with multiple inheritance, we need to identify which of the multiple bases
should be used as the implementation base class. The others will then be simply
treated as mixin interfaces, with their implementations generated into the
derived implementation class.
</p>
<p>
Consider the following example:
</p>
<img src="images/EMF/image008.gif" width="284" height="213" alt="Multiple inheritance: a SchoolBook extends Book and Asset (Asset has a value : float)"/>
<p>
Here we've made SchoolBook derive from two classes: Book and Asset. We've
identified Book as the implementation base (extended) class as
shown<sup><a class="footnote" href="#fn15">[15]</a><a name="ref15"> </a></sup>.
If we regenerate the model, interface SchoolBook will now extend the two
interfaces:
</p>
<pre>
public interface SchoolBook extends Book, Asset</pre>
<p>
The implementation class looks the same as before, only now it includes
implementations of the mixed-in methods getValue() and setValue():
</p>
<pre>
public class SchoolBookImpl extends BookImpl implements SchoolBook
{
public float getValue()
{
...
}
public void setValue(float newValue)
{
...
}
...
}</pre>
<h3>Customizing the generated implementation classes</h3>
<p>
You can add behavior (methods and instance variables) to the generated Java
classes without having to worry about losing your changes if you later decide to
modify the model and then regenerate. For example, let's add a method,
isRecommended(), to class Book. To do this you simply go ahead and add the new
method signature to the Java interface Book:
</p>
<pre>
public interface Book ...
{
boolean isRecommended();
...
}</pre>
<p>
and its implementation in class BookImpl:
</p>
<pre>
public boolean isRecommended()
{
return getAuthor().getName().equals("William Shakespeare");
}</pre>
<p>
The EMF generator won't wipe out this change because it isn't a generated method
to begin with. Every method generated by EMF includes a Javadoc comment that
contains an @generated tag, like this:
</p>
<pre>
/**
* ...
* @generated
*/
public String getTitle()
{
return title;
}</pre>
<p>
Any method in the file that doesn't contain this tag (like isRecommended()) will
be left untouched whenever we regenerate. In fact, if we want to change the
implementation of a generated method, we can do that by removing the @generated
tag from
it<sup><a class="footnote" href="#fn16">[16]</a><a name="ref16"> </a></sup>:
</p>
<pre>
/**
* ...
* <strike>@generated</strike> // (removed)
*/
public String getTitle()
{
// our custom implementation ...
}</pre>
<p>
Now, because of the missing @generated tag, the getTitle() method is considered
to be user code; if we regenerate the model, the generator will detect the
collision and simply discard the generated version of the method.
</p>
<p>
Actually, before discarding a generated method, the generator first checks if
there is another generated method in the file with the same name, but with Gen
appended. If it finds one, then instead of discarding the newly generated
version of the method it redirects the output to it. For example, if we want to
extend the generated getTitle() implementation, instead of completely discarding
it, then we can do that by simply renaming it like this:
</p>
<pre>
/**
* ...
* @generated
*/
public String getTitleGen()
{
return title;
}</pre>
<p>
and then adding our override as a user method that does whatever we want:
</p>
<pre>
public String getTitle()
{
String result = getTitleGen();
if (result == null)
result = ...
return result;
}</pre>
<p>
If we regenerate now, the generator will detect the collision with our user
version of getTitle(), but because we also have the @generated getTitleGen()
method in the class, it will redirect the newly generated implementation to it,
instead of discarding it.
</p>
<h3>Operations in EMF models</h3>
<p>
In addition to attributes and references, you can add operations to your model
classes. If you do, the EMF generator will generate their signature into the
interface and a method skeleton into the implementation class. EMF does not
model behavior, so the implementation must be provided by user-written Java
code.
</p>
<p>
This may be done by removing the @generated tag from the generated
implementation, as described above, and adding the code right there.
Alternatively, the Java code can be included right in the model. In Rose, you
can enter it in the text box on the Semantics tab of an Operation Specification
dialog. The code will then be stored in the EMF model as an annotation on the
operation<sup><a class="footnote" href="#fn17">[17]</a><a name="ref17"> </a></sup>,
and will be generated into its body.
</p>
<h2><a name="programming">Using the Generated EMF Classes</a></h2>
<h3>Creating and accessing instances</h3>
<p>
Using the generated classes, a client program can create and initialize a Book
with the following simple Java statements:
</p>
<pre>
LibraryFactory factory = LibraryFactory.eINSTANCE;
Book book = factory.createBook();
Writer writer = factory.createWriter();
writer.setName("William Shakespeare");
book.setTitle("King Lear");
book.setAuthor(writer);</pre>
<p>
Because the Book to Writer association (author) is two-way, the inverse
reference (books) is automatically initialized. We can verify this by iterating
over the books reference like this:
</p>
<pre>
System.out.println("Shakespeare books:");
for (Iterator iter = writer.getBooks().iterator(); iter.hasNext(); )
{
Book shakespeareBook = (Book)iter.next();
System.out.println(" title: " + shakespeareBook.getTitle());
}</pre>
<p>
Running this program would produce output something like this:
</p>
<pre>
Shakespeare books:
title: King Lear</pre>
<h3>Saving and loading resources</h3>
<p>
To create a document named mylibrary.xmi containing the above model, all we need
to do is create an EMF resource at the beginning of the program, put the book
and writer into the resource, and call save() at the end:
</p>
<pre>
// Create a resource set.
ResourceSet resourceSet = new ResourceSetImpl();
// Register the default resource factory -- only needed for stand-alone!
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl());
// Get the URI of the model file.
URI fileURI = URI.createFileURI(new File("mylibrary.xmi").getAbsolutePath());
// Create a resource for this file.
Resource resource = resourceSet.createResource(fileURI);
// Add the book and writer objects to the contents.
resource.getContents().add(book);
resource.getContents().add(writer);
// Save the contents of the resource to the file system.
try
{
resource.save(Collections.EMPTY_MAP);
}
catch (IOException e) {}</pre>
<p>
Notice that a resource set (interface ResourceSet) is used to create the EMF
resource. A resource set is used by the EMF framework to manage resources that
may have cross document references. Using a registry (interface
Resource.Factory.Registry), it creates the right type of resource for a given
URI based on its scheme, file extension, or other possible criteria. Here,
we register the XMI resource implementation as the default for this resource
set<sup><a class="footnote" href="#fn18">[18]</a><a name="ref18"> </a></sup>.
During load, the resource set also manages the demand-loading of cross document
references.
</p>
<p>
Running this program will produce the file mylibrary.xmi with contents something
like this:
</p>
<pre>
<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
xmlns:library="http:///library.ecore">
<library:Book title="King Lear" author="/1"/>
<library:Writer name="William Shakespeare" books="/0"/>
</xmi:XMI></pre>
<p>
To load the document mylibrary.xmi, as saved above, we set up a resource set,
and then simply demand-load the resource into it, as follows:
</p>
<pre>
// Create a resource set.
ResourceSet resourceSet = new ResourceSetImpl();
// Register the default resource factory -- only needed for stand-alone!
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl());
// Register the package -- only needed for stand-alone!
LibraryPackage libraryPackage = LibraryPackage.eINSTANCE;
// Get the URI of the model file.
URI fileURI = URI.createFileURI(new File("mylibrary.xmi").getAbsolutePath());
// Demand load the resource for this file.
Resource resource = resourceSet.getResource(fileURI, true);
// Print the contents of the resource to System.out.
try
{
resource.save(System.out, Collections.EMPTY_MAP);
}
catch (IOException e) {}</pre>
<p>
Again, we create a resource set and, for the stand-alone case, register a
default resource implementation. Also, we need to ensure that our package is
registered in the package registry, which the resource uses to obtain the
appropriate metadata and factory for the model it is loading. Simply
accessing the eINSTANCE field of a generated package interface is sufficient
to ensure that it is registered.
</p>
<p>
This example uses the second form of save(), which takes an OutputStream, to
print the serialization to the console.
</p>
<p>
Splitting a model into multiple documents, with cross references between them,
is simple. If we wanted to serialize the books and writers, in the save example
above, into separate documents, all we need to do is create a second resource:
</p>
<pre>
Resource anotherResource = resourceSet.createResource(anotherFileURI);</pre>
<p>
and add the writer to it, instead of the first:
</p>
<pre>
<strike>resource.getContents().add(writer);</strike> // (replaced)
anotherResource.getContents().add(writer);</pre>
<p>
This would produce two resources, each containing one object, with a cross
document reference to the other.
</p>
<p>
Note that a containment reference necessarily implies that the contained object
is in the same resource as its container. So, for example, suppose that we had
created an instance of Library containing our Book via the books containment
reference. That would have automatically removed the Book from the contents of
the resource, which in this sense, also behaves like a containment reference.
If we then added the Library to the resource, the book would implicitly belong
to the resource as well, and its details would again be serialized in it.
</p>
<p>
If you want to serialize your objects in a format other than XMI, that can be
arranged as well. You will need to supply your own serialization and parsing
code. Create your own resource class (as a subclass of ResourceImpl) that
implements your preferred serialization format, and then either register it
locally with your resource set, or with the global factory registry if you want
it to always be used with your model.
</p>
<h3>Observing (adapting) EMF objects</h3>
<p>
Previously, when we looked at set methods in generated EMF classes, we saw that
notifications are always sent when an attribute or reference is changed. For
example, the BookImpl.setPages() method included the following line:
</p>
<pre>
eNotify(newENotificationImpl(this, ..., oldPages, pages));</pre>
<p>
Every EObject can maintain a list of observers (also referred to as adapters),
which will be notified whenever a state change occurs. The framework eNotify()
method iterates through this list and forwards the notification to the
observers.
</p>
<p>
An observer can be attached to any EObject (for example, book) by adding to the
eAdapters list like this:
</p>
<pre>
Adapter bookObserver = ...
book.eAdapters().add(bookObserver);</pre>
<p>
More commonly, however, adapters are added to EObjects using an adapter factory.
In addition to their observer role, adapters are more generally used as a way
to extend the behavior of the object they're attached to. A client generally
attaches such extended behavior by asking an
adapter factory to adapt an object with an extension of the required type.
Typically it looks something like this:
</p>
<pre>
EObject someObject = ...;
AdapterFactory someAdapterFactory = ...;
Object requiredType = ...;
if(someAdapterFactory.isFactoryForType(requiredType))
{
Adapter theAdapter = someAdapterFactory.adapt(someObject, requiredType);
...
}</pre>
<p>
Usually, the requiredType represents some interface supported by the adapter.
For example, the argument might be the actual java.lang.Class for an interface
of the chosen adapter. The returned adapter could then be downcast to the
requested interface like this:
</p>
<pre>
MyAdapter theAdapter =
(MyAdapter)someAdapterFactory.adapt(someObject, MyAdapter.class);</pre>
<p>
Adapters are often used this way to extend the behavior of an object without
subclassing.
</p>
<p>
To handle notifications in an adapter we need to override the eNotifyChanged()
method, which is called on every registered adapter by eNotify(). A typical
adapter implements eNotifyChanged() to perform some action for some or all of
the notifications, based on the notification's type.
</p>
<p>
Sometimes adapters are designed to adapt a specific class (for example, Book).
In this case, the notifyChanged() method might look something like this:
</p>
<pre>
public void notifyChanged(Notification notification)
{
Book book = (Book)notification.getNotifier();
switch (notification.getFeatureID(Book.class))
{
case LibraryPackage.BOOK__TITLE:
// book title changed
doSomething();
break;
caseLibraryPackage.BOOK__CATEGORY:
// book category changed
...
case ...
}
}</pre>
<p>
The call to notification.getFeatureID() is passed the argument Book.class to
handle the possibility that the object being adapted is not an instance of
class BookImpl, but is instead an instance of a multiple-inheritance subclass
where Book is not the primary (first) interface. In that case, the feature ID
passed in the notification will be a number relative to the other class and
therefore needs to be adjusted before we can switch using the BOOK__ constants.
In single-inheritance situations, this argument is ignored.
</p>
<p>
Another common type of adapter is not bound to any specific class, but instead
uses the reflective EMF API to perform its function. Instead of calling
getFeatureID() on the notification, it might call getFeature() instead, which
returns the actual Ecore feature (that is, the object in the metamodel that
represents the feature).
</p>
<h3>Using the reflective API</h3>
<p>
Every generated model class can also be manipulated using the reflective API
defined in interface EObject:
</p>
<pre>
public interface EObject ...
{
..
Object eGet(EStructuralFeature feature);
void eSet(EStructuralFeature feature, Object newValue);
boolean eIsSet(EStructuralFeature feature);
void eUnset(EStructuralFeature feature);
}</pre>
<p>
Using the reflective API, we could set the name of an author like this:
</p>
<pre>
writer.eSet(LibraryPackage.eINSTANCE.getWriter_Name(), "William Shakespeare");</pre>
<p>
or get the name like this:
</p>
<pre>
String name = (String)writer.eGet(LibraryPackage.eINSTANCE.getWriter_Name());</pre>
<p>
Notice that the feature being accessed is identified by metadata obtained from
the singleton instance of the library package.
</p>
<p>
Using the reflective API is slightly less efficient then calling the generated
getName() and setName() methods
directly<sup><a class="footnote" href="#fn19">[19]</a><a name="ref19"> </a></sup>,
but opens up the model for completely generic access. For example, the
reflective methods are used by the EMF.Edit framework to implement a full set of
generic commands (for example, AddCommand, RemoveCommand, SetCommand) that can
be used with any model. See the <a href="../../references/overview/EMF.Edit.html">EMF.Edit
Overview</a> for details.
</p>
<p>
In addition to eGet() and eSet(), the reflective API includes two other related
methods: eIsSet() and eUnset(). The eIsSet() method can be used to find out if
an attribute is set or
not<sup><a class="footnote" href="#fn20">[20]</a><a name="ref20"> </a></sup>,
while eUnset() can be used to unset (or reset) it. The generic XMI serializer,
for example, uses eIsSet() to determine which attributes need to be serialized
during a resource save operation.
</p>
<h2><a name="advanced">Advanced Topics</a></h2>
<h3><a name="flags">Generation control flags</a></h3>
<p>
There are several flags that can be set on a model feature to control the
generated code pattern for that feature. Typically, the default settings of
these flags will be fine, so you shouldn't need to change them very often.
</p>
<ul>
<li><p><strong>Unsettable</strong> (default is false)</p>
<p>
A feature that is declared to be unsettable has a notion of an explicit unset or
no-value state. For example, a boolean attribute that is not unsettable can take
on one of two values: true or false. If, instead, the attribute is declared to
be unsettable, it can then have any of three values: true, false, or unset.
</p>
<p>
The get method on a feature that is not set will return its default value, but
for an unsettable feature, there is a distinction between this state and when
the feature has been explicitly set to the default value. Since the unset state
is outside of the set of allowed values, we need to generate additional methods
to put a feature in the unset state and to determine if it is in that state. For
example, if the pages attribute in class Book is declared to be unsettable, then
we'll get two more generated methods:
</p>
<pre>
boolean isSetPages();
void unsetPages();</pre>
<p>
in addition to the original two:
</p>
<pre>
int getPages();
void setPages(int value);</pre>
<p>
The isSet method returns true if the feature has been explicitly set. The unset
method changes an attribute that has been set back to its unset state.
</p>
<p>
When unsettable is false, we don't get the generated isSet or unset methods, but
we still get implementations of the reflective versions: eIsSet() and eUnset()
(which every EObject must implement). For non-unsettable attributes, eIsSet()
returns true if the current value is different from the default value, and
eUnset() sets the feature to the default value (more like a reset).
</p>
</li>
<li><p><strong>ResolveProxies</strong> (default is true)</p>
<p>
ResolveProxies only applies to non-containment references. ResolveProxies
implies that the reference may span documents, and therefore needs to
include proxy checking and resolution in the get method, as described earlier
in this paper.
</p>
<p>
You can optimize the generated get pattern for references that you know will
never be used in a cross document scenario by setting resolveProxies to false.
In that case, the generated get method will be optimally
efficient<sup><a class="footnote" href="#fn21">[21]</a><a name="ref21"> </a></sup>.
</p>
</li>
<li><p><strong>Unique</strong> (default is true)</p>
<p>
Unique only applies to multiplicity-many attributes, indicating that such an
attribute may not contain multiple equal objects. References are always treated
as unique.
</p>
</li>
<li><p><strong>Changeable</strong> (default is true)</p>
<p>
A feature that is not changeable will not include a generated set method, and
the reflective eSet() method will throw an exception if you try to set it.
Declaring one end of a bi-directional relationship to be not changeable is a
good way to force clients to always set the reference from the other end, but
still provide convenient navigation methods from either end. Declaring one-way
references or attributes to be not changeable usually implies that the feature
will be set or changed by some other (user-written) code.
</p>
</li>
<li><p><strong>Volatile</strong> (default is false)</p>
<p>
A feature that is declared volatile is generated without storage fields and with
empty implementation method bodies, which you are required to fill in. Volatile
is commonly used for a feature whose value is derived from some other feature,
or for a feature that is to be implemented by hand using a different storage
and implementation pattern.
</p>
</li>
<li><p><strong>Derived</strong> (default is false)</p>
<p>
The value of a derived feature is computed from other features, so it doesn't
represent any additional object state. Framework classes, such as
EcoreUtil.Copier, that copy model objects will not attempt to copy such
features. The generated code is unaffected by the value of the derived flag,
except for the package implementation class, which initializes the metadata for
the model. Derived features are typically also marked volatile and transient.
</p>
</li>
<li><p><strong>Transient</strong> (default is false)</p>
<p>
Transient features are used to declare (modeled) data whose lifetime never spans
application invocations and therefore doesn't need to be persisted. The (default
XMI) serializer will not save features that are declared to be transient. Like
derived, transient's only effect on the generated code is the metadata
initialization in the package implementation class.
</p>
</li>
</ul>
<h3>Data types</h3>
<p>
As mentioned previously, all the classes defined in a model (for example, Book,
Writer) implicitly derive from the EMF base class EObject. However, all the
classes that a model uses are not necessarily EObjects. For example, assume we
want to add an attribute of type java.util.Date to our model. Before we can do
so, we need to define an EMF DataType to represent the external type. In UML, we
use a class with the datatype stereotype for this purpose:
</p>
<img src="images/EMF/image009.gif" width="183" height="75" alt="Data type definition: datatype JavaDate is of javaclass java.util.Date"/>
<p>
As shown, a data type is simply a named element in the model that acts as a
proxy for some Java class. The actual Java class is provided as an attribute
with the javaclass stereotype, whose name is the fully qualified class being
represented. With this data type defined, we can now declare attributes of type
java.util.Date like this:
</p>
<img src="images/EMF/image010.gif" width="173" height="122" alt="Attribute with data type as attribute type: Book has a publicationDate : JavaDate"/>
<p>
If we regenerate, the publicationDate attribute will now appear in the Book
interface:
</p>
<pre>
import java.util.Date;
public interface Book extends EObject
{
...
Date getPublicationDate();
void setPublicationDate(Date value);
}</pre>
<p>
As you can see, this Date-typed attribute is handled pretty much like any other.
In fact, all attributes, including ones of type String, int, and so on, have a
data type as their type. The only thing special about the standard Java types is
that their corresponding data types are predefined in the Ecore model, so they
don't need to be redefined in every model that uses them.
</p>
<p>
A data type definition has one other effect on the generated model. Since data
types represent some arbitrary class, a generic serializer and parser (for
example, the default XMI serializer) has no way of knowing how to save the state
of an attribute of that type. Should it call toString()? That's a reasonable
default, but the EMF framework doesn't want to require that, so it generates two
more methods in the factory implementation class for every data type defined in
the model:
</p>
<pre>
/**
* @generated
*/
public Date createJavaDateFromString(EDataType eDataType, String initialValue)
{
return (Date)super.createFromString(eDataType, initialValue);
}
/**
* @generated
*/
public String convertJavaDateToString(EDataType eDataType, Object instanceValue)
{
return super.convertToString(eDataType, instanceValue);
}</pre>
<p>
By default, these methods simply invoke the superclass implementations, which
provide reasonable, but inefficient, defaults: convertToString() simply calls
toString() on the instanceValue, but createFromString() tries, using Java
reflection, to call a String constructor or, failing that, a static valueOf()
method, if one exists. Typically you should take over these methods (by
removing the @generated tags) and change them to appropriate custom
implementations:
</p>
<pre>
/**
* <strike>@generated</strike> // (removed)
*/
public String convertJavaDateToString(EDataType eDataType, Object instanceValue)
{
return instanceValue.toString();
)</pre>
<h3>Ecore Model</h3>
<p>
Here is the complete class hierarchy of the Ecore model (shaded boxes are
abstract classes):
</p>
<img src="images/EMF/image011.gif" width="611" height="456" alt="Ecore class hierarchy"/>
<p>
This hierarchy includes the classes that represent the EMF model elements
described in this paper: classes (and their attributes, references and
operations) data types, enumerations, packages and factories.
</p>
<p>
EMF's implementation of Ecore is itself generated using the EMF generator and as
such has the same lightweight and efficient implementation as described in the
previous sections of this paper.
</p>
<br/><hr noshade="noshade" size="1"/>
<p>
<sup><a class="footnote" href="#ref1">[1]</a><a name="fn1"> </a></sup>Actually,
the EMF meta model is itself an EMF model, the default serialized form of which
is XMI.</p>
<p>
<sup><a class="footnote" href="#ref2">[2]</a><a name="fn2"> </a></sup>Currently,
EMF supports import from Rational Rose, but the generator architecture can
easily accommodate other modeling tools as well.
</p>
<p>
<sup><a class="footnote" href="#ref3">[3]</a><a name="fn3"> </a></sup>EMF
uses a subset of the JavaBean simple property accessor naming patterns.
</p>
<p>
<sup><a class="footnote" href="#ref4">[4]</a><a name="fn4"> </a></sup>There
are several user-specifiable options that can be used to change the generated
patterns. We'll describe some of them later (see
<a href="#flags">Generation control flags</a>, later in this document).
</p>
<p>
<sup><a class="footnote" href="#ref5">[5]</a><a name="fn5"> </a></sup>Containment
references, which we'll describe later (see <a href="#containment">Containment
references</a>), cannot span documents. There is also a flag that users can set
in a reference's meta data to indicate that resolve does not need to be called
because the reference will never be used in a cross document scenario (see
<a href="#flags">Generation control flags</a>). In these cases, the
generated get method simply returns the pointer.
</p>
<p>
<sup><a class="footnote" href="#ref6">[6]</a><a name="fn6"> </a></sup>Applications
that need to deal with and handle broken links should call eIsProxy() on the
object returned by a get method to see if it is resolved or not (for example,
book.getAuthor().eIsProxy()).
</p>
<p>
<sup><a class="footnote" href="#ref7">[7]</a><a name="fn7"> </a></sup>This
clearly fails to allow for multiple authors, but it keeps the example model
simple.
</p>
<p>
<sup><a class="footnote" href="#ref8">[8]</a><a name="fn8"> </a></sup>The
reason we bother to delegate to a basicSet() method at all is because it's also
needed by the eInverseAdd() and eInverseRemove() methods, which we'll look at a
little later.
</p>
<p>
<sup><a class="footnote" href="#ref9">[9]</a><a name="fn9"> </a></sup>Actually,
all of the concrete EList implementations are simple subclasses of one very
functional and efficient base implementation class, EcoreEList.
</p>
<p>
<sup><a class="footnote" href="#ref10">[10]</a><a name="fn10"> </a></sup>In
eInverseAdd(), instead of simply switching on the supplied feature id, it first
calls eDerivedStructuralFeatureID(featureID, baseClass). For simple single
inheritance models, this method has a default implementation that ignores the
second argument and returns the featureID passed in. For models that use
multiple inheritance, eDerivedStructuralFeatureID() may have a generated
override that adjusts a feature ID relative to a mixin class (that is,
baseClass) to a feature ID relative to the concrete derived class of the
instance.
</p>
<p>
<sup><a class="footnote" href="#ref11">[11]</a><a name="fn11"> </a></sup>EObjectImpl
also has an int-typed eContainerFeatureID instance variable to keep track of
which reference is currently used for the eContainer.
</p>
<p>
<sup><a class="footnote" href="#ref12">[12]</a><a name="fn12"> </a></sup>See
<a href="https://www.oracle.com/technical-resources/articles/java/substitutes-missing-c-constructs.html" target="_blank">Replace Enums with Classes</a>.
</p>
<p>
<sup><a class="footnote" href="#ref13">[13]</a><a name="fn13"> </a></sup>To
conform to proper Java programming style, the static constant names are
converted to upper case if the modeled enumeration's literal names are not
already upper case.
</p>
<p>
<sup><a class="footnote" href="#ref14">[14]</a><a name="fn14"> </a></sup> While
your program isn't strictly required to use the Factory or Package interfaces,
EMF does encourage clients to use the factory to create instances by generating
protected constructors on the model classes, thereby preventing you from simply
calling new to create your instances. You can, however, change the access to
public in the generated classes manually, if that's what you really want. Your
preferences will not be overwritten if you later decide to regenerate the
classes.
</p>
<p>
<sup><a class="footnote" href="#ref15">[15]</a><a name="fn15"> </a></sup>Actually,
the first base class in the Ecore model is the one used as the implementation
base class. In the UML diagram, the <<extend>> stereotype is needed to
indicate that Book should be first in the Ecore representation.
</p>
<p>
<sup><a class="footnote" href="#ref16">[16]</a><a name="fn16"> </a></sup>If
you know ahead of time that you're going to want to provide your own custom
implementation for some feature, then a better way of doing this is to model the
attribute as volatile, which instructs the generator to only generate a skeleton
method body in the first place, which you are then expected to implement.
</p>
<p>
<sup><a class="footnote" href="#ref17">[17]</a><a name="fn17"> </a></sup>EMF
includes a generic mechanism for annotating metamodel objects with additional
information. This mechanism can also be used to attach user documentation to
elements of the model, and when a model is created from XML Schema, EMF
relies on it to capture serialization details that cannot be expressed
directly using Ecore.
</p>
<p>
<sup><a class="footnote" href="#ref18">[18]</a><a name="fn18"> </a></sup>The
second line of the above code is only required when run stand-alone (that is,
directly invoked in a JVM, with the required EMF JAR files on the class path).
The same registration is automatically made in the global resource factory
registry when EMF is run within Eclipse.
</p>
<p>
<sup><a class="footnote" href="#ref19">[19]</a><a name="fn19"> </a></sup>Implementations
of the reflective methods are also generated for each model class. They
switch on the feature type, and simply call the appropriate generated typesafe
methods.
</p>
<p>
<sup><a class="footnote" href="#ref20">[20]</a><a name="fn20"> </a></sup>See
the Unsettable flag under <a href="#flags">Generation control flags</a> for
what constitutes a set attribute.
</p>
<p>
<sup><a class="footnote" href="#ref21">[21]</a><a name="fn21"> </a></sup>Think
carefully before declaring a feature to not resolve proxies. Just because you
don't need to use the reference in a cross document situation doesn't mean that
someone else who wants to use your model may not. Declaring a feature to not
resolve proxies is kind of like declaring a Java class to be final.
</p>
</body>
</html>
|