TCM Java Code Generator for Cayennesoft's ClassDesigner

Contents

1. Introduction

1.1 Prerequisites
1.2 Download information
1.3 Contact information

2. Usage

2.1 ClassDesigner Components as Java source modules
2.2 Binding UML Classes to source modules
2.3 Specifying source directories for Component packages
2.4 Selecting Components for code generation
2.5 Executing the code generator

3. UML model elements to Java source code

3.1 UML concepts vs. Java concepts
3.2 ClassDesigner properties
3.3 Classes and Interfaces
3.4 Attributes as Java fields
3.5 Operations as Java methods
3.6 Associations as references to Java objects
3.7 Java Bean properties
3.8 Components as Java source modules

4. Preservation of old code by the code generator

4.1 Signatures and field declarations of classes
4.2 Method declarations of classes

5. Source code templates

5.1 Purpose of templates
5.2 Coverage of default templates
5.3 Variables available for templates
5.4 Editing template files

APPENDIX: Error messages and warnings


1 Introduction

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.

1.1 Prerequisites

In order to make good use of this document, the reader should be reasonably familiar with the major features of: The document also briefly discusses Java exceptions, inner classes and Java Beans, but knowledge on these concepts is required only if the corresponding features in the code generator are used.

1.2 Download information

The latest version of the TCM Java Code Generator can be downloaded here: codegen.tar.gz

1.3 Contact information

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


2 Usage

2.1 ClassDesigner Components as Java source modules

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.

2.2 Binding UML Classes to source modules

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:

  1. Open a suitable Component diagram and create a new implementation Component in it.
  2. Name the Component according to the name of the source module to be generated (without the .java extension).
  3. Open the corresponding UML Class, select its 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.

2.3 Specifying source directories for Component packages

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.

2.4 Selecting Components for code generation

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.

2.5 Executing the code generator

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.


3 UML model elements to Java source code

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.

3.1 UML concepts vs. Java concepts

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.

3.2 ClassDesigner properties

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.

3.3 Classes and Interfaces

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

Name:
The name of the generated Java class.
Description:
Generated as a leading Javadoc comment for the Java class declaration.
Stereotype:
Ignored.

Detail settings

Parameters:
Ignored.
Arguments:
Ignored.
Root:
Ignored.
Leaf:
Ignored. Use property {final}.
Abstract:
Ignored. Use property {abstract}.
Visibility:
Generate either public, protected or private Java class or interface. If unspecified, use package visibility. This setting is overridden by the {visibility} property.
Primitive:
Ignored.

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

Specification:
Ignored.
Implementation:
Specifies the Component which defines the Java source module for the Class.

Properties settings

{abstract}:
Generate an abstract Java class. Makes no difference with Interfaces. If a Class has {abstract} Operations, the Class must have this property. A Class may not be both {abstract} and {final}.
{final}:
Generate a final Java class. Interfaces cannot be {final}. A Class may not be both {abstract} and {final}.
{innerclassof=[container]}:
Generate the Class as an inner class of [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}:
Generate a nested top-level Java class or interface.
{visibility=public|protected|private}:
Generate either public, protected or private Java class or interface. If omitted, use ClassDesigner's Visibility setting.

3.4 Attributes as Java fields

Attribute settings describe how to generate each field in the Java class.

General settings

Declaration:
The name of the Java field to be generated, as well as its visibility, scope (static or non-static) and initial value. See the documentation of ClassDesigner for details.
Description:
Generated as a leading Javadoc comment for the Java field declaration.
Implement as:
Ignored.
Access:
If readonly, generates the Java field as final. Else, ignored.

Properties settings

{beanprop=none|bound|constrained|abstract}:
Generate the Java field as a Bean property. See Section 3.7 for details.
{final}
:
Generate a final Java field. Attributes of an Interface are {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}:
Generate a transient Java field.
{volatile}:
Generate a volatile Java field.

3.5 Operations as Java methods

Operation settings define how to generate each method in a Java class.

General settings

Declaration:
The name of the Java method to be generated, as well as its visibility and scope (static or non-static). See the documentation of ClassDesigner for details.
Description:
Generated as a leading Javadoc comment for the Java method declaration.
Polymorphic:
Ignored. Use property {final}.
Abstract:
Ignored. Use property {abstract}.
Query:
Ignored.
Implement as:
Ignored.

Properties settings

{abstract}
:
Generate an abstract Java method. A non-interface Class containing an {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]}:
Generate a Java method with a throws clause. For example, the property {exceptions=myException1, myException2} generates a method declaration with the throws clause throws myException1, myException2.
{final}:
Generate a final Java method. An Operation may not be both {abstract} and {final}.
{native}:
Generate the method as a native method prototype.
{synchronized}:
Generate a synchronized Java method.

3.6 Associations as references to Java objects

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.

Init
is a private method which should be called from the constructor of the generated class. Obviously, init is responsible for initializing the association variable. The code generator does not modify constructors of classes, so the user should always remember to initialize all of the association variables of each class by entering appropriate calls to init methods into their constructors.
Add
is a method used for adding new elements to an association variable array.
Set
is a method used for changing the value of an existing association variable and elements in association variable arrays.
Get
is a getter for the association variable.
Update
is a special method which is only generated for Associations navigable in both directions. The purpose of update is to ensure that adding and setting association variables in one AssociationEnd keeps the other End up-to-date with these changes. The user's code should not explicitly call add and set methods for an association variable which also has an update method.

The existence, signature and implementation of association variable manipulators can vary a lot depending on the Association Specification.

Association Specification

Name:
Only affects the generated comment lines which indicate the beginning and end of the association code in a source module.
Description:
Generated as a leading Javadoc comment for the association Java code.

(The following settings are unique to each AssociationEnd.)

End name:
If the AssociationEnd is navigable, its end name will be generated as the name for an association variable on the other End.
Qualifier:
If the AssociationEnd has a Qualifier, the list of Attributes for that Qualifier are passed on to the AssociationEnd source code templates; see Section 5 for more information. The default templates ignore Qualifier attributes, however.
Multiplicity:
If the AssociationEnd is navigable, its multiplicity will affect the type and, for arrays, index bounds of the association variable on the other End. Each multiplicity setting maps to a multiplicity type. There are four multiplicity types *:
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
* Note: ClassDesigner also supports multiplicity types Zero and ExactNumberGreaterThanOne but the former is basically useless and thus not supported, and the latter is treated as Bounded multiplicity with the upper bound equal to the lower bound.

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
Multiplicities other than those listed above are invalid.

Visibility:
Sets the visibility of the manipulation methods for the association variable on the other End. The init method, however, is private regardless of this setting.
Access:
Prevents manipulator methods from being generated for the association variable on the other End:
Setting Excluded methods
readonly add, set, update
writeonly get
writeread -
The exclusive effects of this setting are cumulative with those of the Changeable setting and the property {disabled}.
Aggregation:
Ignored at the moment.
Changeable:
Prevents manipulator methods from being generated for the association variable on the other End:
Setting Excluded methods
none -
addOnly set
fixed add, set, update
The exclusive effects of this setting are cumulative with those of the Access setting and the property {disabled}.
Class scope:
Checking this box generates the association variable on the other End and its manipulators as static Java members.
Ordered:
Ignored at the moment.
pwdsdsds
Navigable:
If an AssociationEnd is navigable, an association variable with the name of that AssociationEnd is generated on the other End (along with manipulation methods for the variable). If both Ends of an Association are navigable, an extra manipulation method update is generated for both AssociationEnds.
Gen. operations:
Leaving this box unchecked disables code generation for the whole AssociationEnd.
Implement as:
Generate association code according to the source code template named after this field, as opposed to the default template which is resolved from other settings of the 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]}:
Disable code generation for items in [ItemList]. Valid item names are: 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.

3.7 Java Bean properties

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:

  1. Create a new Attribute into the Class and give that Attribute the property {beanprop}.
  2. Enter a value for the property according to what kind of a Bean property you want to create:
    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& and not get&.

Indexed Bean properties are not yet supported.

3.8 Components as Java source modules

Components are object representations of Java source modules, describing one or more Java classes or interfaces.

General settings

Name:
The name of the generated source module without the .java extension which is added automatically.
Description:
Generated as a leading Javadoc comment at the beginning of a source module.

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.


4 Preservation of old code by the code generator

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.

4.1 Signatures and field declarations of classes

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.

4.2 Method declarations of classes

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.


5 Source code templates

5.1 Purpose of templates

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.

5.2 Coverage of default templates

The features that can be configured by editing source code templates and the corresponding template directories are:

5.3 Variables available for templates

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

5.4 Editing template files

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.


APPENDIX: Error messages and warnings

The following lists the error messages and warnings of the code generator and gives general hints for correcting errors.

*** ERROR *** No active model
Ensure that the active window in ClassDesigner is either a diagram or a model view (and not e.g. the console) when you start code generation.

*** ERROR *** No components selected
Ensure that each of the Classes you want to generate code for is bound to a Component, and select one or more Components for code generation. See Section 2.2 and Section 2.4.

*** ERROR *** C: F is write protected
The source file F of Component C is read-only. Change the file permissions of F.

*** ERROR *** C: component has no namespace

*** ERROR *** C: component has no implementation

*** ERROR *** C: multiple superclasses
Open the Relationships dialog for Class C and check whether an obsolete superclass is haunting the Class.

*** ERROR *** C1: unable to resolve the container class ( C2 )
Verify that C2, the value of the {innerclassof} property of class C1, is correctly spelled. If so, verify that the model contains a Class named C2.

*** ERROR *** C: non-abstract class with an abstract method ( O )
Either give Class C the {abstract} property or remove the {abstract} property from Operation O.

*** WARNING *** C: abstract class with no abstract methods
Just a notification.

*** ERROR *** I: interface extending a class ( C )
Open the Relationships dialog for Interface I and ensure that I has superclass relationships exclusively with other Interfaces.

*** ERROR *** O: method declared both abstract and final
Remove either the property {abstract} or the property {final} from Operation O.

*** ERROR *** A: uninitialized final field
Either remove the property {final} from Attribute A or initialize A in its Declaration setting.


Last modified: Thu August 6 10:44:43 EET DST 1998