3. The Check Method

Any mODELcLASS has a class-level check method for validating all kinds of property constratints. Since this method can validate all properties of a model class, it takes as its first parameter the name of the property, and as its second parameter the value to be validated:

mODELcLASS.prototype.check = function (prop, val) {
  var propDeclParams = this.properties[prop],
      range = propDeclParams.range,
      min = propDeclParams.min,
      max = propDeclParams.max,
      minCard = propDeclParams.minCard,
      maxCard = propDeclParams.maxCard,
      pattern = propDeclParams.pattern,
      msg = propDeclParams.patternMessage,
      label = propDeclParams.label || prop;

Notice that the definition of a model class comes with a set of property declarations in properties. An example of such a poperty declaration is the declaration of the attribute title:

title: {range:"NonEmptyString", min: 2, max: 50, label:"Title"}, 

In this example we have the property declaration parameters range, min, max and label. In lines 2-10 above, all these property declaration parameters are copied to local variables for having convenient shortcuts.

In the check method, the first check is concerned with mandatory value constraints:

  if (!propDeclParams.optional && val === undefined) {
      return new MandatoryValueConstraintViolation("A value for "+ 
          label +" is required!");	  
  }

The next check is concerned with range constraints:

  switch (range) {
  case "String":
    if (typeof( val) !== "string") {
      return new RangeConstraintViolation("The "+ label +
          " must be a string!");
    } 
    break;
  case "NonEmptyString":
    if (typeof(val) !== "string" || val.trim() === "") {
      return new RangeConstraintViolation("The "+ label +
          " must be a non-empty string!");
    }
    break;
  ...  // other cases
  case "Boolean":
    if (typeof( val) !== "boolean") {
      return new RangeConstraintViolation("The value of "+ label +
          " must be either 'true' or 'false'!");
    }
    break;
  }

Then there are several range-specific checks concerning (1) string length constraints and pattern constraints:

  if (range === "String" || range === "NonEmptyString") {
    if (min !== undefined && val.length < min) {
      return new StringLengthConstraintViolation("The length of "+
          label + " must be greater than "+ min);
    } else if (max !== undefined && val.length > max) {
      return new StringLengthConstraintViolation("The length of "+
          label + " must be smaller than "+ max);  	
    } else if (pattern !== undefined && !pattern.test( val)) {
        return new PatternConstraintViolation( msg || val +
            "does not comply with the pattern defined for "+ label);  	
    }  
  }

and (2) interval constraints:

  if (range === "Integer" || range === "NonNegativeInteger" || 
      range === "PositiveInteger") {
    if (min !== undefined && val < min) {
      return new IntervalConstraintViolation( label +
          " must be greater than "+ min);
    } else if (max !== undefined && val > max) {
      return new IntervalConstraintViolation( label +
          " must be smaller than "+ max);  	
    }
  }

Then the next check is concerned with cardinality constraints, which may apply to list-valued or map-valued properties.

  if (minCard !== undefined && 
      (Array.isArray(val) && val.length < minCard || 
       typeof(val)==="object" && Object.keys(val).length < minCard)) {
    return new CardinalityConstraintViolation(
        "A set of at least "+ minCard +" values is required for "+ label);	  
  }
  if (maxCard !== undefined && 
      (Array.isArray(val) && val.length > maxCard || 
       typeof(val)==="object" && Object.keys(val).length > maxCard)) {
    return new CardinalityConstraintViolation("A value set for "+ label + 
        " must not have more than "+ maxCard +" members!");	  
  }

The next check is concerned with uniqueness constraints, which can only be checked by inspecting the entire population of the model class. Assuming that this population has been loaded into the main memory collection modelclass.instances, the following code is used:

  if (propDeclParams.unique && this.instances) {
    keys = Object.keys( this.instances);
    for (i=0; i < keys.length; i++) {
      if ( this.instances[keys[i]][prop] === val) {
        return new UniquenessConstraintViolation("There is already a "+
            this.typeName +" with a(n) "+ label +" value "+ val +"!");
      }
    }
  }

Finally, the mandatory value constraints and the uniqueness constraints implied by a standard identifier declaration are checked:

  if (propDeclParams.isStandardId) {
    if (val === undefined) {
      return new MandatoryValueConstraintViolation("A value for the " +
          "standard identifier attribute "+ label +" is required!");
    } else if (this.instances && this.instances[val]) {
      return new UniquenessConstraintViolation("There is already a "+
          this.typeName +" with a(n) "+ label +" value "+ val +"!");	  
    }
  }