The following example demonstrates how to validate a model exported by your CASE tool. You can find the templates and the model for this example at: ../samples/getting_started/validation. The templates use the Model Validation feature and will generate an HTML report for the underlying UML model.
Here is a complete list of files of this example:
Table 2.5. Files for the Model Validation example
| Directory | File | Comment |
|---|---|---|
| ./ | validation.iqp | Project file for this example |
| ./ | build.xml | Ant Script for generation |
| model | valid_model.xmi | Valid model XMI file |
| model | invalid_model.xmi | Invalid model XMI file |
| model | model.zargo | ArgoUML model file |
| templates | main.jsp | Model element to template mapping, Validation |
| templates | report.jsp | Creates the HTML output |
The UML profile in this case is very simple. What we want to show is how to use model validation. Model validation answers the question: Does the UML model satisfy the UML profile?
The UML profile looks like this:
only Entity, Process and Enum are allowed stereotypes
every navigable association must be named with a rolename
a usage dependency of processes to classes shows implicitly a get method of the used class
First let us start with the invalid model. It looks as follows:
You can find the invalid model as XMI file in the folder validation/model. Please open the transformation samples/getting_started/validation/validation.iqp. When you start generation and change to the validation page you should see that the model has three validation errors:
| Unknown stereotype: | Class: User |
| Association [Document -> Version] is navigable but missing rolename. | Association: |
| Association [Version -> Label] is navigable but missing rolename. | Association: |
The Unknown stereotype error is a warning, the missing rolename exceptions are errors (different images in table!). If we used a fatal error the execution would have stopped. In our example the validation violation status are arbitrarily.
The next step is that we fix the model, remove the validation violations and re-generate. The correct model looks as follows:
You can load the model from the XMI file valid_model.xmi. Now re-generating will not produce errors anymore!
You can find both templates main.jsp and report.jsp in the templates directory of this example. Most of the used methods are explained in further examples so we can concentrate on the validation method (see main.jsp):
[...]
private static final List STEREOTYPES = Arrays.asList(
new String[] {"Entity", "Process", "Enum"}
);
[...]
public boolean canBeGenerated(MBase pElement) {
return super.canBeGenerated(pElement)
|| getMetaModel().isAssociation(pElement);
}
public ValidationMessage isValid(MBase pElement) {
String s = getMetaModel().getStereotype(pElement);
// valid stereotypes
if (getMetaModel().isClass(pElement) && !STEREOTYPES.contains(s)) {
return new ValidationMessage(ValidationMessage.WARN, "Unknown stereotype: " + s);
}
// navigable associations do have a rolename?
if (getMetaModel().isAssociation(pElement)) {
for (Iterator it = ((MAssociation)pElement).getConnections().iterator(); it.hasNext(); ) {
MAssociationEnd lAssocEnd = (MAssociationEnd)getMetaModel().getOppositeEnd((MBase)it.next());
if (getMetaModel().isNavigable(lAssocEnd)
&& getMetaModel().getName(lAssocEnd).length() == 0) {
StringBuffer sb = new StringBuffer();
sb.append("Association [");
sb.append(getMetaModel().getTypeName(lAssocEnd.getOppositeEnd()));
sb.append(" -> ");
sb.append(getMetaModel().getTypeName(lAssocEnd));
sb.append("] is navigable but lacks a rolename.");
return new ValidationMessage(ValidationMessage.ERROR, sb.toString());
}
}
}
return new ValidationMessage(ValidationMessage.OK, "No error found!");
}
[...] The model validation happens programmatically. For validation purpose you can use the hook method isValid. This method is called for every model element that is to be processed during generation. You can control which model element leads to the generation of an artifact by overwriting the method canBeGenerated. By default only classes and interfaces are generated. In our example we have added associations, because the UML profile demands a validation of associations, too. Our intention is to check navigable associations and their rolenames.
In our custom isValid method we check whether all classes have a valid stereotype - if not we return a ValidationMessage of type ValidationMessage.WARN. Then we check whether all navigable associations have rolenames.
To do this we check, if the element is an association, then we take a look at the association's ends. Every association consists of two association ends which you can get by calling the method getConnections. Finally we check the UML profile constraint ("Association is navigable and has a rolename?!?"). If this constraint is violated we return a ValidationMessage of type ValidationMessage.ERROR.
If the element is no association in the first place, there is nothing to test form, so we simply return a ValidationMessage of type ValidationMessage.OK.
In a real world application the model validation would more complex. Therefore we recommend to handle it in a separate class. Our experience is that it is also an incremental process to make your model validation more and more detailed. In a way, this is comparable with unit testing your model.