The Unified Modeling Language (UML) is a language for specifying, visualizing, constructing, and documenting the artifacts of software systems, as well as for business modeling and other non-software systems. The UML represents a collection of the best engineering practices that have proven successful in the modeling of large and complex systems. The main creator and developer of UML is OMG.
Novosoft UML API is a modern, high-quality Java-based software, supporting user's UML models. It organizes compact storage of model, provides fast and convenient access to its features (attributes and opposite roles in associations). It allows all conceivable kind of work with models, such as generating and serialization of UML models, organizing of access to model elements, modifying, adding and deleting of features. Novosoft UML API supports integrity and reliability of the model.
Novosoft UML API satisfies all of specifications of OMG/UML, it implements all elements of UML : packages, datatypes, classes, their methods and associations. Besides, Novosoft UML API contains many other useful methods not specified by OMG.
Novosoft UML API contains Reflective API. Together with standard access methods, it gives to the developer alternative orthogonal possibilities for accessing to features of UML model elements. Sometimes this significantly simplifies work with UML model.
Novosoft UML API has its own Event Notification Support. Novosoft UML API realizes supporting of undo/redo policy. This is made to simplify the work of a programmer, who needs such possibilities.
Novosoft UML API supports XMI standard. It can read and write model according to XMI format.
Novosoft UML API is widely used inside NOVOSOFT for constructing code generators, mapped various UML models to programming languages: Java, C++, SQL, etc. Also it can be very useful for tool builders, developing facilities of visual work with models of object-oriented program software.
Novosoft UML API is an implementation of UML metamodel, based of Java language. It consists of interfaces, classes, attributes and methods, which supports notions of UML. There exist a simple correspondence between the names of UML metaobjects, types and roles and the names of NSUML Java types, interfaces, classes and methods.
The last official version of UML metamodel is UML 1.3 metamodel. Its conceptions and semantic constructions are described in Chapter 2 "UML semantics" of the following document:
OMG Unified Modeling Language Specification, Version 1.3, June 1999
In addition to UML metamodel OMG proposed UML 1.3 physical metamodel, which is more clear for realization and more practical. The specifications of the physical metamodel are described in Chapter 6 "UML XMI DTD" of the above mentioned document "OMG Unified Modeling Language Specification, Version 1.3, June 1999".
UML 1.3 physical metamodel, simplifies UML 1.3 metamodel. It contains only bidirectional associations, it does not contain association classes, and is more closely aligned to XMI. The most part of the Chapter 6 contains the text of the document "UML XMI DTD".
We remind some of the distinctions of UML 1.3 physical metamodel from UML 1.3 metamodel.
Names:
Additions:
Association Classes:
Novosoft UML metamodel has minor differences from UML 1.3 physical metamodel. We produced such modification to allow the model to be mapped unambiguously to Java language. We follow UML physical metamodel very closely. There are five exceptions that are introduced to fix problems with metamodel. We:
Primitives have stereotype primitive. Enumerations have stereotype enumeration. Datatypes and elements have no stereotypes. Distinction between datatypes and elements is concluded in two things. First, any datatype is mapped only to one Java class of NSUML , any element is mapped to one Java class and one Java interface. Secondly, datatype classes are created manually, but element classes and interfaces are created with the help of a generator program.
Novosoft UML API contains also auxiliary classes, which provide additional possibilities (events, undo/redo). All auxiliary interfaces and classes are created manually.
UML primitives | Java types |
Boolean | boolean |
Name | String |
Integer | int |
UnlimitedInteger | int |
LocationReference | String |
Geometry | String |
UML enumerations | NSUML Java classes |
AggregationKind | MAggregationKind |
CallConcurrencyKind | MCallConcurrencyKind |
ChangeableKind | MChangeableKind |
MessageDirectionKind | MMessageDirectionKind |
OperationDirectionKind | MOperationDirectionKind |
OrderingKind | MOrderingKind |
ParameterDirectionKind | MParameterDirectionKind |
PseudostateKind | MPseudostateKind |
ScopeKind | MScopeKind |
VisibilityKind | MVisibilityKind |
During initialization of enumeration classes there are constructed several predefined final static public instances in according with UML specifications. For example, there are 3 available instances of the class MVisibilityKind: MVisibilityKind.PRIVATE, MVisibilityKind.PROTECTED, MVisibilityKind.PUBLIC. In addition, the class contains 3 integer attributes which correspond to the above-mentioned instances (see the following table).
MVisibilityKind | |
Predefined Instances | Corresponding class attributes |
MVisibilityKind.PRIVATE | MVisibilityKind._PRIVATE |
MVisibilityKind.PROTECTED | MVisibilityKind._PROTECTED |
MVisibilityKind.PUBLIC | MVisibilityKind._PUBLIC |
The instances are created in according with UML specifications, the corresponding integer class attributes are created for simpler organization of Java switches.
NSUML datatype classes | |
MExpression | MMultiplicity |
MActionExpression | MMultiplicityRange |
MArgListsExpression | |
MBooleanExpression | |
MIterationExpression | |
MMappingExpression | |
MProcedureExpression | |
MTimeExpression | |
MTypeExpression | |
Class MExpression is the superclass for all the group of Expression classes.
Class MMultiplicity is intended for description of role multiplicities. There are four predefined instances of this class. They correspond to the most widespread types of UML multiplicities:
MMultiplicity | |
Predefined Instances | Corresponding UML multiplicities |
MMultiplicity.M0_1 | 0..1 |
MMultiplicity.M1_1 | 1 |
MMultiplicity.M0_N | * |
MMultiplicity.M1_N | 1..n |
With the help of this class you can create arbitrary possible UML multiplicitities, invoking the constructor with the string parameter like this "1,3..5,7,9,10..n".
All enumeration and datatype classes are contained in the package ru/novosoft/uml/foundation/datatypes.
Names' correspondence | ||
UML element | NSUML interface | NSUML class |
Package | MPackage | MPackageImpl |
Classifier | Mclassifier | MClassifierImpl |
... | ... | ... |
Novosoft UML API contains one important class which is the superclass for all the element Java classes. This is the class MBaseImpl, which implements interface MBase. Many interesting additional possibilities of NSUML elements are realized due to the methods defined in the base class. It is supposed you need not in creation of instances of this class, but you will create instances of its successors. For example, NSUML Java class MClassImpl implementing UML metaclass Class is a successor of the base class. You can create new instances of the metaclass Class, choosing any variant from the following
MBase cls0 = new MClassImpl();
MClass cls1 = new MClassImpl();
We remark, that the user have to operate with interface references only, and using the following code
MClassImpl cls = new MClassImpl(); // Error! Do not use similar references
is not allowed and can further lead to mistakes.
Note also, that interface MBase contains overridden in all successors
method getUMLClassName(), returning real UML name of the metaclass,
which is implemented in NSUML . See the following fragment:
public class MStereotypeImpl extends MGeneralizableElementImpl
implements MStereotype
{
// ------------ code for class Stereotype -----------------
...
public String getUMLClassName()
{
return "Stereotype";
}
...
}
So, if you have a reference to interface MBase, you can easy recognize which UML metaobject corresponds to this reference.
Each attribute and role (in association) is mapped to a set of NSUML public user methods of element interfaces (and classes). These methods support access to an attribute or role, possibilities of modifications of their values. There is a simple correspondence between the names of attributes or roles in UML and the names of NSUML methods. This section contains classification of the user methods for accessing to attributes and associations, description of rules for naming of the methods and semantic of these methods.
Attributes are divided on two types: boolean and non-boolean. A set of methods for access to boolean and non-boolean attributes is the same, but there exists a minor difference in naming the methods.
Roles in associations are divided on three types: reference, bag and list roles. Reference roles have multiplicities 1 or 0..1. Roles with other multiplicities belong to bag (unordered multiplicities) or list roles (ordered multiplicities).
Some methods of NSUML element interfaces are public, but designed for internal use only. You are not allowed to invoke them. The names of all of them begin with the word internal.
NSUML interface MAbstraction corresponding to the metaclass has the next form:
public interface MAbstraction extends MDependency {
// generating attributes
// attribute: mapping
MMappingExpression getMapping();
void setMapping(MMappingExpression __arg);
...
// generating associations
...
}
Method getMapping() is the Getter method, method setMapping(...) is the Setter method. These methods are implemented in class MAbstractionImpl. The rule for naming of Getter and Setter methods for non-boolean attribute is generally used: name of the Getter is created by capitalization of first letter of attribute name with addition to it the prefix get, name of the Setter is formed in the same manner with addition to the attribute name the prefix set.
A distinction arises for boolean attributes. In this case an interface looks like the following:
public interface MAssociationEnd extends MModelElement {
// generating attributes
...
// attribute: isNavigable
boolean isNavigable();
void setNavigable(boolean __arg);
...
}
Here the name of the UML attribute is isNavigable. To form the names for Getter and Setter method NSUML deletes the prefix is from the attribute's name. Then it uses the custom rule for forming name of Setter method, but Getters begins with the prefix is.
Novosoft UML API has not special objects for association. It maps association to fields and methods of element classes. Primitively, you can consider, that class contains and treats information about the opposite association end of the association to which it belongs.
Here your can see two UML metaclasses: Feature, Classifier and an unnamed association between them.
Role owner is a reference role, role feature is a list role, because it is ordered. NSUML maps this roles to next private fields:
Additional field | Java Class |
MClassifier _owner; | MFeatureImpl |
List _feature; | MClassifierImpl |
Now, it is provided that Feature contains the information about it owner Classifier in the field _owner, Classifier contains the list of Feature in the field _feature. You have not direct access to these fields, this is provided through Getter, Setter and other methods.
public interface MFeature extends MModelElement
{
...
// opposite role: owner this role: feature
MClassifier getOwner();
void setOwner(MClassifier __arg);
...
}
Analyzing interface you can see that the reference role generates the same Getter and Setter methods, as we saw any attribute generated. The difference is in semantic of Setter method in MFeatureImpl. In the case of attribute access a Setter simply refreshes private field with a new value of the attribute. Now Setter have to support correctness of the model, that is bilateral UML semantic of the association. So, it
Now we consider again our example, the list role feature. This
role is opposite to metaclass Classifier. See the code from Java
interface, which supports operation with this role.
public interface MClassifier extends MNamespace, MGeneralizableElement
{
...
// opposite role: feature this role: owner
List getFeatures();
void setFeatures(List __arg);
void addFeature(MFeature __arg);
void removeFeature(MFeature __arg);
void addFeature(int __pos, MFeature __arg);
void removeFeature(int __pos);
void setFeature(int __pos, MFeature __arg);
MFeature getFeature(int __pos);
...
}
Again you can see the well-known Setter and Getter and additional six methods too. Setter and Getter have semantics different from previous cases.
Semantics of rest six methods are clear from their syntax. They present few functions, that are allowed for the Java interface List.
Now we consider again our example 4.2.1, but suppose that the role feature is not ordered, and change the name of metaclass Classifier to abstract name Something. Then NSUML classes have to contain the following additional fields.
Additional field | NSUML Java Classes |
Something _owner; | MFeatureImpl |
Collection _feature; | MSomethingImpl |
See how could be looked Java interface, supporting operations with the bag role feature.
public interface MSomething ...
{
...
Collection getFeatures();
void setFeatures(Collection __arg);
void addFeature(MFeature __arg);
void removeFeature(MFeature __arg);
...
}
We can see only four methods instead of eight ones, which we saw for list role. Their semantic is similar to previous, but interface List is changed to interface Collection.
Role classification | ||
NS role names | Base multiplicity | Supported Multiplicities |
Reference | (0..1) | (0..1), (1) |
Bag | (0..*) | (2..*), (1..*), (0..*), (*) |
List | (0..*)ordered | all previous ordered |
The third column indicate the types of multiplicities existing in NOVOSOFT UML metamodel. It easy to understand that reference, bag, and list roles is a sufficient set for supporting all kinds of role multiplicities. If your association role have complicated multiplicity, for example, (1,4..6,8..*), you can refer it to bag roles, because it is unordered and contain more than one element. On the finish step, after modifying your model, you can simply verify if the collection's (bag's) size keeps within limits (1,4..6,8..*).
In conclusion remember main methods for accessing associations.
If a metaclass has opposite reference role, then corresponding NSUML interface(class) has only two users methods (Getter and Setter), whose names begin from get and set.
If a metaclass has opposite bag role, then corresponding NSUML interface(class) has four users methods, Getter, Setter, Adder and Remover methods, whose names begin from get, set, add, remove.
If a metaclass has opposite list role, then corresponding NSUML interface(class) has eight users methods, two Getter, two Setter, two Adder and two Remover methods, whose names begin from get, set, add, remove.
public interface MBase {
...
// Reflective API
public Object reflectiveGetValue(String feature);
public void reflectiveSetValue(String feature, Object obj);
public void reflectiveAddValue(String feature, Object obj);
public void reflectiveRemoveValue(String feature, Object obj);
public Object reflectiveGetValue(String feature, int pos);
public void reflectiveSetValue(String feature, int pos, Object obj);
public void reflectiveAddValue(String feature, int pos, Object obj);
public void reflectiveRemoveValue(String feature, int pos);
...
}
Reflective methods begins with the prefix reflective. These methods are overridden in all successors of NSUML class MBaseImpl. Argument feature is an UML name of attribute, opposite reference, bag or list role. The main sense of reflective methods is the access to features by their names, instead of invocation of explicit Setter, Getter, Adder or Remover methods.
As you can easy to see it, there exists a strict correspondence of reflective methods to the Setter, Getter, Adder or Remover methods, described earlier. First two methods provide access to attribute or opposite reference role. Next two methods are possible for feature, which is the name of an opposite bag role. Last four methods is intended for access to list roles.
To clear the semantic of the methods, consider, for example, the implementation of the method reflectiveGetValue in the class MElementResidenceImpl:
public class MElementResidenceImpl extends MBaseImpl
implements MElementResidence
{
...
// Reflective API
public Object reflectiveGetValue(String feature)
{
if ("visibility".equals(feature))
{
return getVisibility();
}
if ("resident".equals(feature))
{
return getResident();
}
if ("implementationLocation".equals(feature))
{
return getImplementationLocation();
}
return super.reflectiveGetValue(feature);
}
...
}
You can see that the class MElementResidenceImpl owns 3 features, not inherited from superclass. This 3 features: visibility, resident, implementationLocation. The object visibility is an attribute of the metaclass ElementResidence, and resident, implementationLocation are opposite roles of this metaclass. The method reflectiveGetValue() verifies, that input field feature coincides with one of the existing features and invokes corresponding Getter method. If this feature does not belong to features' set of this class, then the method invokes the method with the same name from the superclass. If feature does not belong any of element superclasses, then the base class MBaseImpl throws IllegalArgumentException.
This example has explained only the semantic of the first reflective method. Semantics of other methods can be easily guessed by a user with the help of NSUML Java codes for element classes.
Remark, that element can participate in different associations. So during the removing of element NSUML can invoke many methods of this class and many methods of opposite classes in associations to support correctness of the user's model.
public class MElementEvent extends java.util.EventObject
{
...
public final static int ELEMENT_REMOVED = 0;
public final static int ELEMENT_RECOVERED = 1;
public final static int ATTRIBUTE_SET = 2;
public final static int REFERENCE_SET = 3;
public final static int BAG_ROLE_SET = 4;
public final static int BAG_ROLE_ADDED = 5;
public final static int BAG_ROLE_REMOVED = 6;
public final static int LIST_ROLE_SET = 7;
public final static int LIST_ROLE_ADDED = 8;
public final static int LIST_ROLE_ITEM_SET = 9;
public final static int LIST_ROLE_REMOVED = 10;
...
}
Static fields describes types of situations generating events. Their meanings are clear from their names. First event reports, that user invoked method remove for some element. Second event reports, that API recovered previously removed element in the model.
If a user of NSUML want to listen events, going from model elements, he/she have to
Below we give description of the listener interface.
public interface MElementListener
{
void propertySet(MElementEvent e);
void roleAdded(MElementEvent e);
void roleRemoved(MElementEvent e);
void listRoleItemSet(MElementEvent e);
void removed(MElementEvent e);
void recovered(MElementEvent e);
}
Next example illustrate, how to create class EventTest implementing interface MElementListener (we omit implementation details). Method main() creates new listener instance listener and new model element Class, which registers the listener.
public class EventTest implements MElementListener
{
public static void main (String args[])
{
EventTest listener = new EventTest();
MClass cls = new MClassImpl();
cls.addMElementListener(listener);
}
...
}
static int event_policy = EVENT_POLICY_DISABLED;
public static final int EVENT_POLICY_DISABLED = 0;
public static final int EVENT_POLICY_IMMEDIATE = 1;
public static final int EVENT_POLICY_FLUSH = 2;
Default state of event_policy is disable, so listeners will not get events by default. To set the event policy you have to invoke its method setEventPolicy(...), for example
MFactory.setEventPolicy(MFactory.EVENT_POLICY_IMMEDIATE);
Explain the sense of these polices.
MElementEvent evt ; MElementListener l; ... switch (evt.getType()) { case MElementEvent.ELEMENT_REMOVED: l.removed(evt); break; case MElementEvent.ELEMENT_RECOVERED: l.recovered(evt); break; case MElementEvent.ATTRIBUTE_SET: case MElementEvent.REFERENCE_SET: case MElementEvent.BAG_ROLE_SET: case MElementEvent.LIST_ROLE_SET: l.propertySet(evt); break; case MElementEvent.BAG_ROLE_ADDED: case MElementEvent.LIST_ROLE_ADDED: l.roleAdded(evt); break; case MElementEvent.BAG_ROLE_REMOVED: case MElementEvent.LIST_ROLE_REMOVED: l.roleRemoved(evt); break; case MElementEvent.LIST_ROLE_ITEM_SET: l.listRoleItemSet(evt); break; default: System.err.println("bad event: "+evt.getType()); } ...
Class MCheckPointUndoManager has the next form:
public class MCheckPointUndoManager
{
...
static int undo_policy = UNDO_POLICY_DISABLED;
public static final int UNDO_POLICY_DISABLED = 0;
public static final int UNDO_POLICY_ENABLED = 1;
public static void setUndoPolicy(int policy)
...
public static MCheckPoint getCheckPoint()
...
public static void undo(MCheckPoint c)
...
public static void redo(MCheckPoint c)
...
}
Explain the sense of methods.
Below see a simple example of the program, illustrating work with undo/redo methods.
MCheckPointUndoManager.setUndoPolicy(MCheckPointUndoManager.UNDO_POLICY_ENABLED);
MCheckPoint c1 = MCheckPointUndoManager.getCheckPoint();
MClass cls = new MClassImpl();
cls.addMElementListener(l);
cls.setName("Test");
cls.setName("Test2");
MAttribute attr = new MAttributeImpl();
attr.setType(cls);
attr.setName("test");
cls.addFeature(attr);
cls.remove();
MCheckPoint c4 = MCheckPointUndoManager.getCheckPoint();
System.out.println("Doing full undo ...");
MCheckPointUndoManager.undo(c1);
System.out.println("Doing full redo ...");
MCheckPointUndoManager.redo(c4);
Following diagram illustrates possible variant of time evolution of some program.
The program
Copyright © 1999 by Novosoft Inc. All rights reserved