Defining Custom Model Tasks

Reminders are hints to help guide the user as they develop their model. They are presented dynamically in the Eclipse tasks view and update themselves as the user makes changes to their model.

Reminders can be contributed such that they constrain elements from all open models, or only elements who have a specific stereotype applied. UML Modeler contributes predefined reminders that display hints when developing UML Models. These reminders are contributed such that they constrain all model elements regardless of their applied stereotype.

Contributing Reminders to All Models

Reminders can be defined such that they constrain all models. These reminders are contributed programmatically similar to validation constraints (see Constraining Models for information about how to define constraints. A reminder declared programmatically is defined within an Eclipse plug-in. When the plug-in is present in a workbench, all opens models are constrained against the reminders defined in the plug-in.

Contributing a reminder can be as simple as contributing a validation constraint with a severity of INFO. This mechanism should be used when the logic behind the reminder is simple and the only object affecting the outcome is the target of the reminder itself. An example would be when the reminder succeeds or fails based on a property of the target.

The following reminder constraint will provide a hint to the user when they have a UML class in their model whose visibility has not been set. For this reminder there is a single target (a UML Class) and the only changes to the model that could update the reminder involve changing the target itself (setting the visibility of that UML Class).

The constraint definition for the reminder is below and is placed in the plug-in's plugin.xml file. The reminder specific portion of the constraint definition is setting the severity of the constraint to INFO. For details on the other elements on the constraint contribution see Constraining Models.


   <extension
         point="org.eclipse.emf.validation.constraintProviders">
      <category
            id="com.ibm.constraintCategory"
            name="My Category"/>
      <constraintProvider cache="true">
         <package namespaceUri="http://www.eclipse.org/uml2/3.0.0/UML"/>
         <constraints categories="com.ibm.constraintCategory">
            <constraint
                  class="someconstraints.ClassVisibilitySetConstraint"
                  id="classVisibilityConstraint"
                  lang="Java="
                  name="Class Visibility should be set"
                  severity="INFO"
                  statusCode="1001">
               <target class="=Class=">
               </target>
               <message>
                  The Class with name {0} does not have its visibility set.
               </message>
               <description>
                  This reminder provides a hint to the user if a UML Class does not have its visibility set.
               </description>
            </constraint>
         </constraints>
      </constraintProvider>
   </extension>

The validation portion of this reminder is defined in Java, as detailed below. The validate method simply returns a failure if the class' visibility is not set, and returns a success otherwise.


public class ClassVisibilitySetConstraint extends AbstractModelConstraint {
	public IStatus validate(IValidationContext ctx) {
		Class element = (Class)ctx.getTarget();
		
		if (!element.isSetVisibility) {
			return ctx.createFailureStatus(new Object[] {element.getName()});
		}
		
		return ctx.createSuccessStatus();
	}
}

A validate method should always return one of three results: a success status, a failure status or null. All reminders must return a success status when the reminder should disappear and a failure status when the reminder should appear. If the event that triggered the reminder had no effect on the reminder, the validate method should return null.

The above example only involves a single target for the reminder. Reminders that involve multiple targets can be written by defining Constraint Targets. When using constraint targets, the validation logic is only written with the target in mind, not the other elements that are involved.

Consider a reminder that will inform the user if they have a class that is stereotyped 'Auxiliary' and it is not associated with a class stereotyped 'Focus'. In this case there are several events not involving the target class that could change the status of the reminder. For example, creating or removing the association between the 'Auxiliary' and 'Focus' class or removing or adding the 'Focus' stereotype to the associated class. In order to capture these events and ensure the reminder is run when these events take place, constraint targets are used.

There are two parts to writing a constraint target:

  1. Defining the constraint. This constraint is written with only the target in mind.
  2. Defining the objects which could alter cause the constraint to pass or fail.

A third, optional, step involves writing an optimizer which can improve the performance of the constraint.

Defining the Reminder

Writing the reminder is straightforward, as we only need to consider the validation of the 'Auxiliary' class. Once again, the constraint is defined with a severity of INFO to indicate that it is a reminder. The constraint definition is as follows:


   <extension
         point="org.eclipse.emf.validation.constraintProviders">
      <category
            id="com.ibm.constraintCategory"
            name="My Category"/>
      <constraintProvider cache="true">
         <package namespaceUri="http://www.eclipse.org/uml2/3.0.0/UML"/>
         <constraints categories="com.ibm.constraintCategory">
            <constraint
                  class="someconstraints.AuxiliaryClassAssociatedWithFocusClass"
                  id="AuxiliaryClassAssociatedWithFocusClassConstraint"
                  lang="Java"
                  name="Auxiliary Class Associated With Focus Class"
                  severity="INFO"
                  statusCode="1001">
               <target class="Class">
               </target>
               <message>
                  The Auxiliary Class named {0} should be associated with a Focus class.
               </message>
               <description>
                  The intent of an Auxiliary class is to support another class.  The support class should be a Focus class.
               </description>
            </constraint>
         </constraints>
      </constraintProvider>
   </extension>

The Java portion of the constraint is defined below:



public class AuxiliaryClassAssociatedWithFocusClassConstraint extends AbstractModelConstraint {

	private static final String AUXILIARY_STEREOTYPE = "Standard::Auxiliary"; //$NON-NLS-1$
	private static final String FOCUS_STEREOTYPE = "Standard::Focus"; //$NON-NLS-1$
	
	public IStatus validate(IValidationContext ctx) {

		org.eclipse.uml2.uml.Class clazz = (org.eclipse.uml2.uml.Class)ctx.getTarget();
		    	
    	if (clazz.getAppliedStereotype(AUXILIARY_STEREOTYPE) != null) {
    		for (Iterator it=clazz.getAssociations().iterator(); it.hasNext();) {
    			Association association = (Association)it.next();
    			for (Iterator it2=association.getEndTypes().iterator(); it2.hasNext();) {
    				Object end = it2.next();
    				if (end instanceof org.eclipse.uml2.uml.Class && end != clazz) {
    					org.eclipse.uml2.uml.Class associatedClass = (org.eclipse.uml2.uml.Class)end;
    					if (associatedClass.getAppliedStereotype(FOCUS_STEREOTYPE) != null) {
    						return ConstraintStatus.createSuccessStatus(ctx, clazz, null);
    					}
    				}
    			}
    		} 
    		return ctx.createFailureStatus(new Object[] {clazz});
    	}
    	return ConstraintStatus.createSuccessStatus(ctx, clazz, null);
	}
}


Once again, the validate method straightforward, and is only interested in the target of constraint. The method simply determines if the target class is 'Auxiliary' and if so, ensures that it is associated with a 'Focus' class. It returns success if the class is not 'Auxiliary' or if it is 'Auxiliary' and associated with a 'Focus' class. It returns a failure if the target class is an 'Auxiliary' class but it is not associated with a 'Focus' class.

Defining the Constraint Target

The constraint target identifies the types of objects that when changed could affect the outcome of the reminder. For the reminder we've detailed above, changes to Class objects (eg the Focus class) or Association objects (eg the link between a focus and an auxiliary class) could affect the outcome of the reminder.

Constraint targets are defined using the com.ibm.xtools.emf.validation.core.constraintTargets extension point as follows:



<extension
         point="com.ibm.xtools.emf.validation.core.constraintTargets">
      <constraintTargets>
      	 <namespace
               prefix="uml"
               uri="http://www.eclipse.org/uml2/3.0.0/UML">
         </namespace>
         <targets constraintId="plugin.id.AuxiliaryClassAssociatedWithFocusClassConstraint">
         	<target eClassName="uml:Class">
         		<trigger eClassName="uml:Class"/>
         		<trigger eClassName="uml:Association"/>
         	</target>
         </targets>
      </constraintTargets>
   </extension>


There are several components of the constraint targets definition. The namespace definition defines the namespaces involved in the constraint target. In this instance the namespace involved is the UML namespace. The targets definition defines the constraint ID for which this constraint target is defined. The constraint ID is the fully qualified ID, including the plug-in ID prefix. The target definition defines the target of the constraint. It is defined as the EClass of the object in question with its namespace prefix preceeding the EClass name. For this constraint the target of the constraint is Class. Finally, the trigger definition defines the EClass of the objects that act as triggers. For this constraint the triggers are Class and Association.

Improving the performance of a Constraint Target

Constraint targets provide the option of specifying an optimizer which can improve the performance of a constraint target. The optimizer establishes a link between the trigger and target and determines whether or not validation should be run. It improves performance by ensuring validation is only run when necessary.

The optimizer is defined in the form of a Java class that inherits from IConstraintTargetOptimizer. The constraint targets extension point is used to specify that an optimizer is available for a given constraint target, as detailed in bold in the definition below:



<extension
         point="com.ibm.xtools.emf.validation.core.constraintTargets">
      <constraintTargets>
      	 <namespace
               prefix="uml"
               uri="http://www.eclipse.org/uml2/3.0.0/UML">
         </namespace>
         <targets constraintId="plugin.id.AuxiliaryClassAssociatedWithFocusClassConstraint">
         	<target eClassName="uml:Class">
         		<trigger eClassName="uml:Class"/>
         		<trigger eClassName="uml:Association"/>
         		<optimizer class="someconstraints.AuxiliaryClassAssociatedWithFocusClassOptimizer" />
         	</target>
         </targets>
         
      </constraintTargets>
   </extension>


The optimizer for the constraint in question ignores targets who are not stereotyped with 'Auxiliary' and ignores triggers who are of type Class but are not associated with 'Auxiliary' classes.

public class AuxiliaryClassAssociatedWithFocusClassOptimizer implements IConstraintTargetOptimizer{

	public boolean isTargetApplicableForTrigger(EObject target, EObject trigger) {
		 org.eclipse.uml2.uml.Class clazz = (org.eclipse.uml2.uml.Class)target;
		    	
    	 if (clazz.getAppliedStereotype(AUXILIARY_STEREOTYPE) != null) {
    	 	 if (trigger instanceof org.eclipse.uml2.uml.Class) {
    	 	 	 org.eclipse.uml2.uml.Class triggerClass = (org.eclipse.uml2.uml.Class)trigger;
    	 	 	 
    	 	 	 // Trigger was deleted, cannot look at associations
    	 	 	 if (triggerClass.eResource() == null) {
    	 	 	 	return false;
    	 	 	 }
    	 	 	 
    	 	 	 // Check if trigger is associated with the target
    	 	 	 for (Iterator it=triggerClass.getAssociations().iterator(); it.hasNext();) {
    			 	Association association = (Association)it.next();
    			 	for (Iterator it2=association.getEndTypes().iterator(); it2.hasNext();) {
    					Object end = it2.next();
    					if (end == trigger) {
    						return true;
    					}
    				}
    			 }
    	 	 } else {
    	 	 	// Accept all Associations as triggers
    	 	 	return true;
    	 	 }
    	 }
    	 
    	 return false;
	}

}

Contributing Reminders for Stereotyped Model Elements

Similar to validation constraints, reminders can be contributed using a UML profile. These reminders are defined such that they target model elements stereotyped with a stereotype defined in the profile. When the profile containing the reminder is applied to a model and model elements are subsequently stereotyped, these models elements are constrained against the reminders defined in the profile.

Defining Reminders for Profile Stereotypes

UML profiles allow the definition of constraints on UML stereotypes. For more information visit Adding constraints to custom UML profiles. Reminders are contributed identically to other profile constraints with the exception that the severity of the constraint should be set to INFO.

Defining Constraint Targets for Profile Stereotypes

Similar to non-profile reminders, the logic behind the reminders defined in profiles might be complex and involve multiple objects. Reminders in profiles can define constraint targets in order to cover these complex rules. Constraint targets are added to constraint by applying the StructuredConstraint profile, as follows:

  1. In the Project Explorer view, select the profile which contains your constraint
  2. In the Properties view, click the Profile tab.
  3. On the Profile page, click "Add Profile..."
  4. From the list of Deployed Profiles, select "Structured Constraint" and press OK

Once the profile StructuredConstraint profile is applied to the constraint's profile, the triggers for the constraint target are specified by:

  1. In the Project Explorer view, select the constraint for which you want to apply constraint targets
  2. In the Properties view, click the Stereotype tab.
  3. On the Stereotype page, click "Apply Stereotype..."
  4. From the list of available stereotypes, choose "ConstraintTarget" and press OK.
  5. On the Stereotype page, in the Stereotype Properties section, click on triggers which appears under ConstraintTarget
  6. Click on the ellipsis
  7. The properties dialog that appears is used to add and remove triggers from the constraint target.

Note: If your reminder uses OCL to define its rule, when the ConstraintTarget stereotype is applied to your reminder the list of triggers will be prepopulated with the references in your OCL expression.


Legal notices