Skip to content
Roman Schulte edited this page Aug 19, 2016 · 23 revisions

#JSBML Developer Wiki

##Offline Validation

The offline validator is a alternative way to validate a document against the specifications of SBML. As the name will suggest it doesn't need internet connection.

###How it's work One of the most important classes of the offline validator is the ValidationContext. In the best case, this should be the only class the user will ever need. To reduce overhead, this class is also designed to be reusable.

This his how you setup a validation context and perform a simple validation:

// 1. Obtain a new instance
ValidationContext ctx = new ValidationContext();

// 2. Loading constraints to the context
ctx.loadConstraints(MyClass.class);

// 3. Perform validation
MyClass myObject = new MyClass();
boolean isValid = ctx.validate(myObject);

Notice that the ValidationContext is capable to validate EVERY class, as long as at least one motherclass or interface provides constraints. Let's see what these lines of code really do:

  1. This is a simple constructor call, nothing special here. The result is a default ValidationContext with has recursive validation turned on and only loads the constraints for the General SBML Consistency Issues
  2. Here's the real magic behind the validator. The first thing the context does is to obtain the shared instance of the ConstraintFactory. The factory now checks every superclass and interface of the given class and tries to find a ConstraintDeclaration. To avoid double checking, the factory remembers already visited classes. Be aware, that multiple calls of loadConstraint(*) or loadConstraintForAttribute(*) will override each other. There can always be only one set of constraints in a context.
  3. This function call triggers the validation. While loading constraints, the context remembers the root class for which the constraints were loaded. Before the validation starts, the context checks if the given object is assignable to the constraint type, if not the validation will return false and print a message to the console. If this test is passed, the HashMap of the context will be cleared and context calls the check(*) method of the root constraint after checking for null. If the root constraint is null, no rules exist and therefore the object must be valid.

Take control of the validation

The steps above perform a really simple validation, which only gives a quick result. If the validate(*) method returns false you couldn't say how many or which constraints are broken. If you want to have more informations about the validation process you could add a ValidationListener to a context. A context can have unlimited amount of these listeners. Each of them has two methods, one of them will be triggered before a constraint will be validated and one afterwards. The second method also gets the result of this constraint (true if everything is fine, false otherwise). This informations in combination with the error codes of the constraints could be used to retrieve more information about a broken constraint. Notice that a ConstraintGroup will return false if at least one of their child constraints is broken. You can recognize a ConstraintGroup either by checking the class of the constraint or by comparing the error code:

class ValidationLogger implements ValidationListener {

   public void willValidate(ValidationContext ctx, AnyConstraint<?> c, Object o) {
      
      // using the instanceof operator to filter out groups
      if (c instanceof ConstraintGroup){
         system.out.println("enter group");
      }
   }

   public void didValidate(ValidationContext ctx, AnyConstraint<?> c, Object o, boolean success) {
      if (c.getErrorCode == CoreSpecialErrorCodes.ID_GROUP) {
          system.out.println("leave group");
      }
      else 
      {
          if (!success)
          {
             system.out.println("constraint " + c.getErrorCode() + " was broken!");
          }
      }
   }
}

How constraints are loaded

  • ValidationContext:

    • Most important class for user
    • Should do all the work and easy to use
    • Should be reusable
    • Has a HashMap to store additional data
      • Cleared before validation starts
  • Context calls the factory to get constraints

    • Factory checks class hierarchy and is looking for ConstraintDeclarations
    • Remember already visited classes to avoid double constraints
  • Looking for ConstraintDeclaration

    • Must be in package
    • Must be named like class and ends with 'Constraints'
    • Must be assignable to ConstraintDeclaration
  • AbstractConstraintDeclaration:

    • provides most functions
      • You only have to take care about which constraints should be loaded
      • and how each of the constraints will work
      • Creates automatically ConstraintGroups and ignores non existing constraints
      • Groups have always at least one member
    • uses Reflection to find constraint declaration
      • Caches found classes and remembers not existing classes
    • Caches constraints
      • Key = className + ErrorCode
      • Two constraints with same ErrorCode but different targets are possible
  • TreeNodeConstraints

    • Has special constraint which points the context to every child
    • The constraint does nothing if recursive validation is disabled

###Add Constraints

  • Create ConstraintDeclaration
    • Remember naming convention and package
    • Should extending AbstractConstraintDeclaration
  • Collect ErrorCodes
  • Provide ValidationFunctions
  • Use level/version of context
  • Remember that's cached

###Adding Error Objects

  • Stored in JSON
  • One big Dictionary/HashMap. ErrorCode as String is key for Entry
  • Entry is again Dic/HashMap. Has following attributes:
    • "NAME" =
  • Example Structure
Clone this wiki locally