GenerateValidationsVisitor { {{ private Hashtable allTypes; private Formatter f; private String reportedPartName = ""; private String reportedAttrElementFlag = ""; private String accessor = ""; private String checks = ""; }} before CDDef {{ // Generate a list of all "type"s defined in the schema, for later use allTypes = new Hashtable(); for (Enumeration types = host.get_types().elements(); types.hasMoreElements(); ) { DataClass c = (DataClass) types.nextElement(); allTypes.put(c.get_name(), c); } // Initialize the output formatter to create the behavior file for the // validating visitor f = new Formatter(filename, "Beh", behCodeType); f.open(); f.startClass("ValidateVisitor"); f.skipLine(); f.declareVariable("private String errors = \"\";"); f.declareVariable("private Hashtable ids = null;"); f.skipLine(); f.startVisitorMethod("before", "XMLDoc"); f.writeTextLine("ids = host.get_element_ids();"); f.endMethod(); }} after CDDef {{ f.skipLine(); f.startMethod("public String get_return_val()"); f.writeTextLine("return errors;"); f.endMethod(); f.startMethod("private void addError(String errorText)"); f.writeTextLine("errors = errors + \"\\n\" + errorText;"); f.endMethod(); f.endClass("ValidateVisitor"); // Close the output formatter f.close(); }} around DataClass {{ // An abstract class is assumed to have no parts, and so do not process // the underlying class-parts if (host.get_abstract_ind()) return; if (host.get_category().equals("SIMPLE")) { if (reportedPartName.equals("")) return; // If it comes here, it must be from the forced traversal in the // getPartValidations method subtraversal.apply(); } else { checks = ""; accessor = ""; reportedPartName = ""; reportedAttrElementFlag = ""; f.changeIndent('+'); subtraversal.apply(); f.changeIndent('-'); if (!checks.equals("")) { f.startVisitorMethod("before", host.get_name()); f.writeWithNoIndent(checks); f.endMethod(); } } }} before ClassPart {{ String attrElementFlag; if (host.getClass().getName().startsWith("Attr")) attrElementFlag = "attribute"; else attrElementFlag = "element"; String partName = host.get_name(); String partType = host.get_type(); String partSpec; if (accessor.equals("")) partSpec = "host.get_" + partName + "()"; else { partSpec = accessor + ".get_" + partName + "()"; partName = reportedPartName; attrElementFlag = reportedAttrElementFlag; } // Get the accessor that would get us to the innermost primitive Java // data type or class, so that basic operations (like ==, <, >, // compareTo(), etc.) can be applied to them. // These might be needed if the constraining facets are defined on a // part with a user-defined simpleType or with a XML built-in data type // that has actually been implemented using a special class (e.g. // XMLpositiveInteger) on which we cannot use "<", for example. String finalPartType = partType; String finalPartSpec = partSpec; String partCategory = ""; DataClass partClass = null; if (! Utils.isPrimitiveDataType(partType)) { partClass = (DataClass) allTypes.get(partType); if (partClass != null) partCategory = partClass.get_category(); if (partCategory.equals("SIMPLE")) { String[] finalModifiers = getFinalModifiers(partType); finalPartType = finalModifiers[0]; finalPartSpec = partSpec + finalModifiers[1]; } } Constraint c = host.get_constraint(); if ((c.idref_flag != null) && (c.idref_flag.booleanValue())) { checks = checks + "\n" + f.indent() + "if (! ids.containsKey(" + finalPartSpec + "))\n"; f.changeIndent('+'); checks = checks + f.indent() + "addError(\"IDREF (\" + " + finalPartSpec + " + \") in attribute '" + partName + "' doesn't correspond to a valid ID\");\n"; f.changeIndent('-'); } if (c.fixed_value != null) { String compare = ""; if (Utils.isPrimitiveDataType(finalPartType)) { if (finalPartType.equals("char")) compare = finalPartSpec + " != '" + c.fixed_value + "'"; else compare = finalPartSpec + " != " + c.fixed_value; } else { compare = finalPartSpec + ".compareTo(new " + finalPartType + "(\"" + c.fixed_value + "\")) != 0"; } checks = checks + "\n" + f.indent() + "if (" + compare + ")\n"; f.changeIndent('+'); checks = checks + f.indent() + "addError(\"Value (\" + " + finalPartSpec + " + \") in " + attrElementFlag + " '" + partName + "' cannot be anything other than " + c.fixed_value + "\");\n"; f.changeIndent('-'); } if ((c.max_occurs > 1) && (c.max_occurs < Integer.MAX_VALUE)) { checks = checks + "\n" + f.indent() + "if (" + partSpec + ".size() > " + c.max_occurs + ")\n"; f.changeIndent('+'); checks = checks + f.indent() + "addError(\"Size (\" + " + partSpec + ".size() + \") of '" + partName + "' list cannot be greater than " + c.max_occurs + "\");\n"; f.changeIndent('-'); } if ((c.min_occurs > 0) && (c.max_occurs > 1)) { checks = checks + "\n" + f.indent() + "if (" + partSpec + ".size() < " + c.min_occurs + ")\n"; f.changeIndent('+'); checks = checks + f.indent() + "addError(\"Size (\" + " + partSpec + ".size() + \") of '" + partName + "' list cannot be less than " + c.min_occurs + "\");\n"; f.changeIndent('-'); } if (c.enum_values != null) { String compare = ""; compare = "new String(\"" + c.enum_values + "\").indexOf(" + "\"\" + " + finalPartSpec + " + \"\") < 0"; checks = checks + "\n" + f.indent() + "if (" + compare + ")\n"; f.changeIndent('+'); checks = checks + f.indent() + "addError(\"Value (\" + " + finalPartSpec + " + \") in " + attrElementFlag + " '" + partName + "' is not one of the enumerated values\");\n"; f.changeIndent('-'); } // For a list part, the "length", "maxLength" and "minLength" facets // restrict the length of the list; otherwise they restrict the length // of the value String valueSpec ; if (c.max_occurs > 1) // it's a list type valueSpec = partSpec + ".size()"; else valueSpec = finalPartSpec + ".length()"; if (c.length != null) { String compare = valueSpec + " != " + c.length.intValue(); checks = checks + "\n" + f.indent() + "if (" + compare + ")\n"; f.changeIndent('+'); checks = checks + f.indent() + "addError(\"Length (\" + " + valueSpec + " + \") of " + attrElementFlag + " '" + partName + "' must be equal to " + c.length.intValue() + "\");\n"; f.changeIndent('-'); } if (c.max_length != null) { String op = ">"; String compare = valueSpec + " " + op + " " + c.max_length.intValue(); checks = checks + "\n" + f.indent() + "if (" + compare + ")\n"; f.changeIndent('+'); checks = checks + f.indent() + "addError(\"Length (\" + " + valueSpec + " + \") of " + attrElementFlag + " '" + partName + "' cannot be " + op + " " + c.max_length.intValue() + "\");\n"; f.changeIndent('-'); } if (c.min_length != null) { String op = "<"; String compare = valueSpec + " " + op + " " + c.min_length.intValue(); checks = checks + "\n" + f.indent() + "if (" + compare + ")\n"; f.changeIndent('+'); checks = checks + f.indent() + "addError(\"Length (\" + " + valueSpec + " + \") of " + attrElementFlag + " '" + partName + "' cannot be " + op + " " + c.min_length.intValue() + "\");\n"; f.changeIndent('-'); } if (c.max_value != null) { String op = ">"; if ((c.max_included == null) || (!c.max_included.booleanValue())) op = op + "="; String compare = ""; if (Utils.isPrimitiveDataType(finalPartType)) { if (finalPartType.equals("char")) compare = finalPartSpec + " " + op + " '" + c.max_value + "'"; else compare = finalPartSpec + " " + op + " " + c.max_value; } else { compare = finalPartSpec + ".compareTo(new " + finalPartType + "(\"" + c.max_value + "\")) " + op + " 0"; } checks = checks + "\n" + f.indent() + "if (" + compare + ")\n"; f.changeIndent('+'); checks = checks + f.indent() + "addError(\"Value (\" + " + finalPartSpec + " + \") in " + attrElementFlag + " '" + partName + "' cannot be " + op + " " + c.max_value + "\");\n"; f.changeIndent('-'); } if (c.min_value != null) { String op = "<"; if ((c.min_included == null) || (!c.min_included.booleanValue())) op = op + "="; String compare = ""; if (Utils.isPrimitiveDataType(finalPartType)) { if (finalPartType.equals("char")) compare = finalPartSpec + " " + op + " '" + c.min_value + "'"; else compare = finalPartSpec + " " + op + " " + c.min_value; } else { compare = finalPartSpec + ".compareTo(new " + finalPartType + "(\"" + c.min_value + "\")) " + op + " 0"; } checks = checks + "\n" + f.indent() + "if (" + compare + ")\n"; f.changeIndent('+'); checks = checks + f.indent() + "addError(\"Value (\" + " + finalPartSpec + " + \") in " + attrElementFlag + " '" + partName + "' cannot be " + op + " " + c.min_value + "\");\n"; f.changeIndent('-'); } // if it's a user-defined simple type or a non-Java built-in XML data // type (which is also going to be a simpleType), then locate the // class corresponding to this type and explicitly traverse it here. // This will get the "checks" for the elements in the underlying classes // added as part of the **current** class. if (!Utils.isPrimitiveDataType(partType)) { if (partCategory.equals("SIMPLE")) { boolean firstSimpleType = true; if (accessor.equals("")) { reportedPartName = partName; reportedAttrElementFlag = attrElementFlag; } else firstSimpleType = false; String saveChecks = checks; // For a list part, we need to iterate over the items and so set // the "accessor" to a local iterator variable name; otherwise // set the "accessor" to the current part-specification // This "accccessor" is used in the subsequent sub-traversal to // represent the data item being validated. if (c.max_occurs > 1) accessor = "e"; else accessor = partSpec; // For a list part (as explained above) we need a "for" loop to // enclose the validations from the sub-traversal; and for an // optional part, we need an "if" statement (checking if the // part is actually present) to enclose the validations. // Accordingly, in these two cases, the indent is to be changed. if ((c.max_occurs > 1) || // if it's a list part (c.min_occurs == 0)) // if it's an optional part f.changeIndent('+'); checks = ""; partClass.getPartValidations(this); // If the indent was increased before, reduce it back if ((c.max_occurs > 1) || (c.min_occurs == 0)) f.changeIndent('-'); if (firstSimpleType) // reset the subtraversal indicators { accessor = ""; reportedPartName = ""; reportedAttrElementFlag = ""; } if (!checks.equals("")) { String newChecks = saveChecks; if (c.max_occurs > 1) // it is a list part { newChecks = newChecks + "\n" + f.indent() + "for (Enumeration en = " + partSpec + ".elements();\n" + f.indent() + " en.hasMoreElements(); )\n" + f.indent() + "{\n"; f.changeIndent('+'); newChecks = newChecks + f.indent() + partClass.get_name() + " e = (" + partClass.get_name() + ") en.nextElement();"; f.changeIndent('-'); newChecks = newChecks + checks + f.indent() + "}\n"; } else if (c.min_occurs == 0) // it is an optional part newChecks = newChecks + "\n" + f.indent() + "if (" + partSpec + " != null)\n" + f.indent() + "{" + checks + f.indent() + "}\n"; else newChecks = newChecks + checks; checks = newChecks; } else checks = saveChecks; } } }} public String getErrors() {{ if (f == null) return "\nValidations Print visitor action not yet initiated"; else return f.getErrors(); }} private String[] getFinalModifiers(String typeName) {{ String finalType = typeName; String partSpecModifier = ""; while (true) { DataClass partClass = (DataClass) allTypes.get(finalType); if (partClass == null) break; for (Enumeration parts = partClass.get_element_parts().elements(); parts.hasMoreElements(); ) { ElementClassPart e = (ElementClassPart) parts.nextElement(); finalType = e.get_type(); partSpecModifier = partSpecModifier + ".get_" + e.get_name() + "()"; break; } } return new String[] {finalType, partSpecModifier}; }} } // This traversal method in DataClass is used to do a forced traversal on the // data-class that implements the type of a given class-part. // This separate forced traversal is needed because the current class-part just // contains the name (a string) of the data-class corresponding to its type - it // doesn't have an actual reference to it. // Please note that this data-class would have gotten traversed at some other // time anyway (because the root CDDef has a reference to it in the "types" // list); but this sub-traversal is needed right in the middle of traversing the // class-part, for certain special cases. DataClass { traversal getPartValidations(GenerateValidationsVisitor) { to *; } }