OO Contraints Validator

This page gives some basic information on the Object Oriented (OO) Constraints Validator.

The FW Profile comes with FW Profile validator -- an Eclipse plug-in that checks the UML model against set of constraints defined in the FW Profile. The FW Profile assumes that the model on which it operates is designed following general guidelines on object-oriented design. To verify that this is indeed the case we have developed an additional checker that verifies constraints not verified by the Eclipse's org.eclipse.uml2.uml.util.UMLValidator. This checker is named OO Constraints Validator. Is is distributed with the FW Profile as the com.pnp.ooconstraints.validation.topcased plug-in.

Implementation

The OO Constraints Validator was implemented in early 2008 following the same design as the older FW Profile Validator. There are 7 constraints defined. Each constraint is specified using plain English and implemented in Java using Eclipse UML2 API to query the checked UML model. Internally the validator uses Topcased's Validation Framework which in turn uses Eclipse's EMF Validation Framework.

In the next section each constraint is detailed.

OO Constraints

  1. ChoiceStateRequiresElseBranchConstraint

    For each choice pseudostate there might be several outgoing transitions. The transitions have attached guard conditions that specifies on which condition the transitions can be used. According to the UML specification there should always be one such transitions where the guard condition says "else". This transition is taken if all no other transition guard is true.
    if (object.getKind() == PseudostateKind.CHOICE_LITERAL) {
      EList<Transition> list = object.getOutgoings();
      for (Transition transition : list) {
        if (transition.getGuard() != null) {
          if (g.getSpecification() != null) {
            String s = g.getSpecification().stringValue();
            if (s.equals("else")) return true;
          }
        }
      }
      return false;   
    }
    return true;   
  2. InitialAndHistoryStatesAreMutualyExclusiveConstraint

    In a region there might me at most one initial or history state.
    EList<Vertex> list = object.getSubvertices();
    int cnt = 0;
    for (Vertex vertex : list) {
      if (vertex instanceof Pseudostate) {
        Pseudostate ps = (Pseudostate) vertex;
        if (ps.getKind() == PseudostateKind.INITIAL_LITERAL) cnt++;
        if (ps.getKind() == PseudostateKind.SHALLOW_HISTORY_LITERAL) cnt++;
        if (ps.getKind() == PseudostateKind.DEEP_HISTORY_LITERAL) cnt++;
      }
    }
    if (cnt > 1) return false else return true;   
  3. AbstractMethodsRequiresAbstractClassConstraint

    If there is an abstract method in a class, then the class has to be defined as abstract too.
    Operation o = (Operation) object;
    if (o.isAbstract()) {
      Class c = o.getClass_(); 
      if (! c.isAbstract())return false;
    }
    return true;   
  4. FinalOperationCannotBeOverridenConstraint

    Final operation cannot be overriden in a subclass. In the FW Profile trigger operations are final. It is, however, possible to extend the state machine in a subclass and to use previously defined trigger operation to animate the estended (embedded) state machine.
    Operation o = (Operation) object;
    EList<Stereotype> sList = object.getAppliedStereotypes();
    for (Iterator iterator = sList.iterator(); iterator.hasNext();) {
      Stereotype s = (Stereotype) iterator.next(); 
      if (s.getName().equals("FwTrigger")) return true;
      // this constraint doesn't hold for triggers (triggers must be final)
    }
    Class c = o.getClass_();
    if (c == null) 
      return true; // operation may be in interface
    else
      c = c.getSuperClass(null); // get the first super class
    if (hasFinalAncestor(o, c)) {
      return false;
    }
    return true;   
  5. ConstrutorStereotypeConstraint

    Constructor needs to be tagged with <<create>> stereotype defined in Standard UML Profile.
    Operation o = (Operation) object;
    if (o.getClass_() == null)
      if (o.getInterface() != null) return true;
    
    if (o.getName().equals(o.getClass_().getName())) {
      EList<Stereotype> list = o.getAppliedStereotypes();
      for (Iterator<Stereotype> iterator = list.iterator(); iterator.hasNext();) {
        Stereotype s = (Stereotype) iterator.next();
        if (s.getName().equals("Create")) return true;
      }
      return false;
    } 
    return true;   
  6. ConstrutorReturnTypeConstraint

    Constructor has no return type.
    Operation o = (Operation) object;
    String className = "";
    String interfaceName = "";
    if (o.getClass_() != null) className = o.getClass_().getName();
    if (o.getInterface() != null) interfaceName = o.getInterface().getName();
    if (o.getName().equals(className.concat(interfaceName))) {
      Type t = o.getType();
      if (t == null) return true;
      if (t.getName().equals("null")) return true;
      return false;
    }
    return true;   
  7. AllMethodsImplementedInNonAbstractClassConstraint

    In a non-abstract class all operation must be implemented. Otherwise the class has to be abstract.
    if (object.isAbstract()) return true;
    EList<Operation> allOps = object.getAllOperations();
    for (Operation o : allOps) {
      if (o.isAbstract()) {
        //then search for non-abstract operation of that implements it (same signature), returns false if not found
        boolean found = false;
        for (Operation o2 : allOps)
          if (o2.getName().equals(o.getName())) 
            if (!o2.isAbstract())
              found = true;
        if (!found) return false;
      }
    }
    return true;   

Using OO Constraints Validator

The Validator is used along with the FW Profile Validator as described in user manual. The only difference between the two validators is, that the error messages is reported via the Problems view with [OO] prefix instead of [FW Profile] prefix.