This document describes the first publicly available version of the TCM Java Code Generator for ClassDesigner. The TCM Java Code Generator differs from the standard ClassDesigner code generator in several respects. The most important differences include the following:
Our future plans include still better support for Java Beans, and Design Patterns in general. However, we are eager to hear about all experiences and suggestions.
The latest version of the TCM Java Code Generator can be downloaded here: codegen.tar.gz
Esko Heimonen
Research Assistant
ehe@niksula.cs.hut.fi
Pekka Nikander
Researcher
pnr@tcm.hut.fi
Helsinki University of Technology
Telecommunications and Multimedia Laboratory
Metsäneidonkuja 6
Spektri/Kvintti
Espoo, Finland
As an object-oriented modeling language, UML describes systems as collections of Classes, each of which has given Attributes, behavior (Operations related to the Attributes), and Associations to other Classes of the system. In an UML model describing a Java implementation, obviously, each Class represents a Java class, each Attribute a Java field, etc. In addition to Classes, UML also supports Interface model elements - equivalent to Java interfaces. For convenience, we address both a Class and an Interface as a Class here.
For code generation purposes, each UML Class must be bound to a
source module in the case tool. ClassDesigner uses separate objects
for specifying a binding between a Class and the corresponding source
module. These objects are called Components. A Class is bound to a
Component, and the Component describes the name and other properties
of the source module to be generated. Although ClassDesigner supports
separate Components for specification (header) and implementation
(code) of a Class, since Java classes don't have header files, the
specification component
setting of a Class is ignored
by the Java code generator.
The designer should introduce one or more Component diagrams to each package of an UML model under development in order to be able to generate code for them. Refer to the documentation of ClassDesigner for details on how to create new models, packages and diagrams.
An UML Class is bound to a Java source module by following three easy steps:
Components
dialog and alter the implementation component
setting
to point to the newly created Component.
More than one Class may occupy a single source module. In such a case,
several Classes have their implementation component
setting
pointing to the same Component. The designer can't decide the order
in which the Java classes appear in the generated source module, however.
Also, changing the order by hand after code generation is not very practical,
because activating the code generator for the module for the next time will
again reorder the classes.
The location of the source modules generated from an UML model is
specified on package level in ClassDesigner. That is, each package of
an UML model has a source directory
setting which can be set to
refer any available directory. The directory must be created
beforehand. All components in a package behave as source modules
residing in the source directory selected for that package.
The source directory
setting of a package is accessed in
ClassDesigner by selecting the package in the model diagram, and then
choosing Object
from the main menu, followed by selections
Properties...
then Source Directory
.
The starting point of relative path names is ClassDesigner's working directory instead of e.g. the directory where the UML model files are stored.
The user may freely select objects in ClassDesigner from the model diagram (any number of packages or even the entire model) and from all diagrams contained by the packages of the model. Selecting a package covers all Components in the package, and selecting a Class also selects the corresponding implementation Component (if any). When the code generator is activated, source code will be gererated for all selected Components.
The Java code generator is executed for the selected UML model
components by choosing Actions
from the main menu, and then
selecting Execute script
. In the appearing dialog, one should open
the file Java.tcl which is in the directory Jacob3/codegen of the
Jacob v3 project. This will start the code generation.
One should not open any other Tcl scripts in the code generator directory, because they require initialization by the main script Java.tcl in order to work properly.
After executing the code generator, one may want to read the output on ClassDesigner's console to review the results of code generation, including issued warnings and error messages.
When generating code for an UML model element, the code generator inspects many of the settings available for each element in order to generate Java classes that are equivalent to Classes in the UML model.
The designer is offered a reasonable amount of flexibility in controlling various aspects of the classes to be generated. The following discusses how the settings for Classes, Attributes, Operations and Associations in ClassDesigner affect the corresponding generated Java classes.
UML | Java |
---|---|
+ | public * |
- | private * |
# | protected * |
$ | static * |
Attribute | field |
Class | class |
Interface | interface |
Operation | method |
* For the usage of +
, -
,
#
and $
to control the visibility and class scope of
Attributes and Operations, please refer to the
documentation of the case tool. "Package" visibility of Java is
indicated by leaving visibility unspecified.
Although many of the settings in ClassDesigner's dialog boxes for
model elements have semantics that are usable in Java code generation,
most of them are ignored by the code generator. Instead, the code
generator supports various properties that can be used to e.g. define
a class to be {abstract}
or {final}
.
Properties are very much like #define
settings in C.
The existence of a property in a model element and the value (if any)
assigned to the property affect the behavior of the code generator
on the model element. The braces enclosing defined properties which show up
in class diagrams are included by ClassDesigner.
The reasons for using properties, instead of model element settings with similar semantics, include:
Summary of supported properties:
Property setting | Applies to | Effect in Java |
---|---|---|
{abstract} | C, O | Declare "abstract" |
{beanprop=none} | A | Generate field as Bean property |
{beanprop=bound} | A | Generate field as bound Bean prop. |
{beanprop=constrained} | A | Generate field as constr. Bean prop. |
{beanprop=abstract} | A | Generate field as abstract Bean prop. |
{disabled=[ItemList]} | As | Disable code generation for [ItemList] |
{exceptions=[ExcList]} | O | Declare "throws [ExcList]" |
{final} | C, I, O, A | Declare "final" |
{native} | O | Generate native method prototype |
{static} | C, I | Declare "static" |
{synchronized} | O | Declare "synchronized" |
{transient} | A | Declare "transient" |
{visibility=public} | C, I | Declare "public" visibility * |
{visibility=protected} | C, I | Declare "protected" visibility * |
{visibility=private} | C, I | Declare "private" visibility * |
{volatile} | A | Declare "volatile" |
C=Class I=Interface O=Operation A=Attribute As=Association
* If the {visibility}
property is not defined,
the code generator defaults to ClassDesigner's
Visibility
setting
The semantics of each property when applied to different model elements is described in better detail in Sections 3.3 - 3.7.
Interfaces are Classes that have the identity Interface instead of
the identity Class of normal Classes. Otherwise, Interfaces in the
UML model are identical to Classes, ie. an Interface has exactly the
same settings in the case tool as any other Class. The code generator,
however, uses slightly different approach in generating code for
Interfaces. For example, all Operations of an Interface are by
default treated as abstract Java methods, and all Attributes are
treated as static final Java fields unless they have the property
{beanprop=abstract}
.
Inner classes, or Java classes which are contained by other classes,
are defined using the property {innerclassof}
.
General settings
Detail settings
{final}
.{abstract}
.{visibility}
property.
Relationships settings
These settings should be edited by drawing Associations between
Classes with a graphical toolkit in an UML class diagram. Deleting
obsolete Associations may be sometimes easier to do from within the
Relationships
settings of the involved classes.
Components settings
Properties settings
{abstract}
:{abstract}
Operations,
the Class must have this property. A Class may not be both
{abstract}
and {final}
.
{final}
:{final}
. A Class may not be both
{abstract}
and {final}
.
{innerclassof=[container]}
:[container]
.
For example, the property {innerclassof=outerClass}
for Class innerClass states that the
implementation specification
of innerClass
will be ignored by the code generator and, instead, the code for
innerClass will be embedded inside the code of (and into
the same source module as) outerClass.
{static}
:{visibility=public|protected|private}
:Visibility
setting.
Attribute settings describe how to generate each field in the Java class.
General settings
Properties settings
{beanprop=none|bound|constrained|abstract}
:{final}
{static} {final}
by default unless they also have
the property {beanprop=abstract}
. The effect of
this property can be achieved by setting Access
to readonly.
{transient}
:{volatile}
:Operation settings define how to generate each method in a Java class.
General settings
{final}
.{abstract}
.Properties settings
{abstract}
{abstract}
Operation must also
have this property. Operations of an Interface have the
{abstract}
property by default. An Operation
may not be both {abstract}
and {final}
.
{exceptions=[ExcList]}
:{exceptions=myException1, myException2}
generates a method declaration with the throws clause
throws myException1, myException2
.
{final}
:{abstract}
and {final}
.
{native}
:{synchronized}
:In Java terms, an association between two Classes in an UML model means that either one or both of the corresponding Java classes have a variable or an array that references one or more instances of the other class, and possibly methods for manipulating these references (such as getter and setter methods).
Code generation for Associations is a complicated issue affected by several settings each of which can produce a large variety of association implementations. Thus, understanding the principles of code generation for Associations requires some intuition from the user on how UML Associations map to Java code. The basic approach of the code generator is to use two kinds of association variables, simple fields and arrays, and five different manipulation methods: init, add, set, get, and update. Choosing the right type of association variable and selecting a suitable set of manipulator implementations at each AssociationEnd produces the correct implementation for the whole Association.
A short description of each type of manipulator method follows.
The existence, signature and implementation of association variable manipulators can vary a lot depending on the Association Specification.
Association Specification
(The following settings are unique to each AssociationEnd.)
Type | Effect |
---|---|
One | generate a simple association variable which must be initialized to a non-null value and never be set to null thereafter |
ZeroOrOne | generate a simple association variable which may optionally be null |
Bounded | generate an association variable array whose minimum and maximum number of elements are limited to fixed values |
Unbounded | generate an association variable array whose minimum number of elements is fixed but which may grow unlimitedly |
The multiplicity type of an AssociationEnd also affects both the signature (arguments) and implementation of the manipulation methods for the generated association variable.
Multiplicity types are resolved as follows:
Multiplicity | Multiplicity type |
---|---|
1 | One |
0..1 | ZeroOrOne |
m (m >= 0) | Bounded |
m..n (m >= 0, n >= m) | Bounded |
m..* (m >= 0) | Unbounded |
Setting | Excluded methods |
---|---|
readonly | add, set, update |
writeonly | get |
writeread | - |
Changeable
setting and the property
{disabled}
.
Setting | Excluded methods |
---|---|
none | - |
addOnly | set |
fixed | add, set, update |
Access
setting and the property
{disabled}
.
Association Specification
.
A set of source code templates with the given postfix
must be located in the template/assocend subdirectory
of the code generator. For more information on source code
templates, see Section 5.
Properties settings
{disabled=[ItemList]}
:attribute
, init
, add
,
set
, get
and update
.
For example, the property {disabled=attribute,add,update}
excludes the association variable declaration, and the methods
add and update from code generation.
This property is used when the user wants to provide a customized
implementation for some part of an AssocationEnd. The exclusive
effects of the property {disabled}
are cumulative with
those of the Access
setting and the
Changeable
setting.
Using Java Bean properties involves writing methods like getters, setters and notifiers (as described in the design pattern section of the Java Beans documentation). The code generator is able to recogize different types of Bean properties and automatically generate the methods for the corresponding patterns. This helps the user to avoid doing uncreative work and ensures that the rules of the patterns are followed to the letter.
Not surprisingly, support for Java Bean properties has been implemented by using ClassDesigner properties.
To enter a Bean property into a Class:
{beanprop}
.
Value | Property type |
---|---|
{beanprop=none}, {beanprop} | normal Bean property |
{beanprop=bound} | bound Bean property |
{beanprop=constrained} | constrained Bean property |
{beanprop=abstract} | abstract Bean property |
The next time you generate code for the Class, the field declaration for the new Bean property will appear in the source module, followed by the methods of the property's design patterns.
The code generator knows that the getter method for a Bean property of
the type boolean is called is&
Indexed Bean properties are not yet supported.
Components are object representations of Java source modules, describing one or more Java classes or interfaces.
General settings
Relationships settings
These settings should be edited by drawing Associations between components with a graphical toolkit in a component diagram. Deleting obsolete Associations may be sometimes easier to do from within the Relationships settings of the involved components.
Declarations settings
The text in this area follows the leading Javadoc comment in the source module. In Java code generation, it is typically used for listing the import statements of the source module. (The package statement is generated automatically using the name of the UML package.)
Properties settings
Ignored.
The following describes how the code generator preserves method bodies with the help of special "code section" markers and the cases where code generator does not preserve old code.
Signatures and field declarations for classes are always re-generated from the UML model.
Fields added to a class by hand after the last code generation and left out of the UML model are not preserved by the code generator! It is very advisable to always introduce new variables to a class by adding them to the UML model and then generating fresh code for the class.
Naturally, the statement above does not apply to local variables within method implementations.
The code generator does not preserve hand-written code when generating code into source modules that have been imported from another project and created a ClassDesigner component for.
ClassDesigner uses special markers to define, in bodies of methods, code sections which are preserved "as is" in code generation. The markers are included in the generated classes automatically by the generator and should not be edited or deleted by the user.
The marker indicating the beginning of a code section
(//+ ...
) should always immediately follow
the signature of a method, and the ending indicator
(//- ...
) should immediately precede the closing
brace of the method body:
public int myMethod(int a, myClass cl) {
//+ myClass::myMethod(int, myClass)
/*
* insert your code between the code section markers;
* code outside the markers is not preserved in code generation
*/
//- myClass::myMethod(int, myClass)
}
Code sections may not be nested or otherwise overlap.
If the signature of a method remains the same between two subsequent code generation sessions, the newly generated method will continue to have the body it had before the last session.
If the signature of a method changes, the code for that method is not included in the code section of the newly generated method declaration (with new code section markers). Instead, the code generator appends the old code section to the end of the source module, after a comment labeled "UNUSED CODE". The preserved code can be re-copied into the body of the new method declaration by hand, but the old code section markers must not be copied along with the code.
The user is free to delete obsolete code located in the "UNUSED CODE" block; the code generator never removes anything from this block.
The output format of the generated source module is largely controlled by separate template files which define, on ASCII character level, how to write a Java field declaration, how to write a Java implementation for a particular Java association etc.
The directories for template files are located in the template subdirectory of the code generator. The user is free to adjust the templates to suit her personal taste.
Besides modifying the existing templates, the user can also add new templates
to template directories. Self-tailored templates are made to override the
default templates in code generation by entering their name
(without the .java extension) to the implement as
setting of an UML model element in ClassDesigner.
The features that can be configured by editing source code templates and the corresponding template directories are:
The following Tcl string variables can be used in template files:
Variable | Description |
---|---|
${name} | name of the source code element (class, field, etc.) |
${Name} | as above, but with the first letter in uppercase |
${abstract} | "abstract " if element has the {abstract} property, else "" |
${beanprop} | value of the {beanprop} property |
${exceptions} | value of the {exceptions} property |
${final} | "final " if element has the {final} property, "" otherwise |
${hasbody} | 1 if an Operation has a non-empty body, 0 otherwise |
${increment} | symbolic name for a constant which has the value ${multincr} |
${initialvalue} | initialization string for an Attribute: " = |
${isOrdered} | 1 if an AssociationEnd is ordered, 0 otherwise |
${lowerbound} | symbolic name for a constant which has the value ${multmin} |
${multincr} | size of the increment used in expanding association variable arrays |
${multmax} | upper multiplicity bound for an AssociationEnd |
${multmin} | lower multiplicity bound for an AssociationEnd |
${native} | "native " if element has the {native} property |
${parameters} | argument list of an Operation ("arg1, arg2, ...") |
${qattr} | list of Attrbute names for a Qualifier of an AssociationEnd |
${static} | "static " if element has the {static} property or if an AssociationEnd has "class scope" setting checked |
${superclass} | name of the superclass of a Class |
${interfaces} | list of interfaces a Class or Interface implements |
${synchronized} | "synchronized " if an Operation has the {synchronized} prop. |
${transient} | "transient " if an Attribute has the {transient} property |
${type} | type of an Attribute or the return value of an Operation |
${Type} | reflected "object" type of an Attribute (for example, "Byte" is the object type for Attributes of type "byte") |
${upperbound} | symbolic name for a constant which has the value ${multmax} |
${visibility} | value of the {visibility} property, followed by a space |
${volatile} | "volatile " if an Attribute has the {volatile} property |
The contents of each Tcl file are treated as evaluated Tcl strings. (Evaluated means that variables within the strings are substituted with their corresponding values.)
Since brackets ([
and ]
)
have a special purpose in Tcl, they should be escaped
("\[
", "\]
") in template files when used in
e.g. Java array indexing. Besides variables, Tcl expressions
(for example, condition testing) can also be embedded in templates using
[
and ]
. Please see Tcl's documentation on
substitution and evaluation rules for more information.
The following lists the error messages and warnings of the code generator and gives general hints for correcting errors.
{innerclassof}
property of class C1, is correctly spelled. If so, verify
that the model contains a Class named C2.
{abstract}
property or remove the {abstract}
property from
Operation O.
Relationships
dialog for Interface
I and ensure that I has superclass relationships
exclusively with other Interfaces.
{final}
from Operation O.
{final}
from Attribute
A or initialize A in its
Declaration
setting.