gt: 		    xptobj.fragment = clvp.replace("{value}", xptobj.fragment);
context: /**
   * ShapeChange - processing application schemas for geographic information
   *
   * This file is part of ShapeChange. ShapeChange takes a ISO 19109 
   * Application Schema from a UML model and translates it into a 
   * GML Application Schema or other implementation representations.
   *
   * Additional information about the software can be found at
   * http://shapechange.net/
   *
   * (c) 2002-2019 interactive instruments GmbH, Bonn, Germany
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   * 
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   * 
   * You should have received a copy of the GNU General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *
   * Contact:
   * interactive instruments GmbH
   * Trierer Strasse 70-72
   * 53115 Bonn
   * Germany
   */
  package de.interactive_instruments.ShapeChange.Target.XmlSchema;
  
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.GregorianCalendar;
  import java.util.SortedSet;
  import java.util.TreeSet;
  
  import org.apache.commons.lang3.StringUtils;
  import org.apache.commons.lang3.tuple.ImmutablePair;
  import org.apache.commons.lang3.tuple.Pair;
  
  import de.interactive_instruments.ShapeChange.MapEntry;
  import de.interactive_instruments.ShapeChange.Options;
  import de.interactive_instruments.ShapeChange.Type;
  import de.interactive_instruments.ShapeChange.Model.ClassInfo;
  import de.interactive_instruments.ShapeChange.Model.PropertyInfo;
  import de.interactive_instruments.ShapeChange.Ocl.OclNode;
  import de.interactive_instruments.ShapeChange.Ocl.OclNode.AttributeCallExp;
  import de.interactive_instruments.ShapeChange.Ocl.OclNode.DataType;
  import de.interactive_instruments.ShapeChange.Ocl.OclNode.MultiplicityMapping;
  
  /**
   * SchematronConstraintNodeXslt2 and its concrete derivations stand for a
   * representation of OCL contents, which are close to the capabilities of
   * Schematron and the logic that can be realized within Schematron rules, based
   * on the xslt2 query binding.
   *<br><br>
   * There are two basic patterns of use of these classes:
   *
   * <ul>
   * <li>Creation of SchematronConstraintNodeXslt2 objects while interpreting the
   * original OclConstraint objects.
   * <li>Translating Rules and Assert code fragments by calling the abstract
   * method <i>translate</i>.
   * </ul>
   * 
   * @author Johannes Echterhoff (echterhoff at interactive-instruments dot
   *         de)
   * @author Reinhard Erstling
   * 
   */
  public abstract class SchematronConstraintNodeXslt2 {
  
      /** The children of the SchematronConstraintNodeXslt2 */
      protected ArrayList<SchematronConstraintNodeXslt2> children = new ArrayList<SchematronConstraintNodeXslt2>();
  
      /** General negation flag for all nodes */
      protected boolean negated = false;
  
      /** The parent reference */
      protected SchematronConstraintNodeXslt2 parent = null;
  
      /** Link back to SchematronSchema object */
      protected AbstractSchematronSchema schemaObject = null;
  
      /**
       * Method to add children to a node and at the same time establish the node as
       * parent of the child to be added.
       *
       * @param child The Child node to be added
       */
      public void addChild(SchematronConstraintNodeXslt2 child) {
  	children.add(child);
  	child.parent = this;
      }
  
      /**
       * Method to inquire whether the the node inquired is a Logic node AND this
       * logic node has the same <i>isAnd</i> polarity as specified in the parameter.
       *<br><br>
       * This implementation installs the default for all derivations except Logic.
       *
       * @param isAnd Flag: Are we an AND? (not an OR)?
       * @return True if this is Logic with the same polarity
       */
      public boolean isAndOrLogic(boolean isAnd) {
  	return false;
      }
  
      /**
       * This method determines whether the given expression depends on the Variable
       * passed as argument.
       *<br><br>
       * This implementation defines the default behavior: Descend down and try to
       * find the variable somewhere.
       *
       * @param vardecl The Declaration of the variable
       * @return Flag indicating the inquired dependency
       */
      public boolean isDependentOn(OclNode.Declaration vardecl) {
  	for (SchematronConstraintNodeXslt2 scn : children)
  	    if (scn.isDependentOn(vardecl))
  		return true;
  	return false;
      }
  
      /**
       * This method determines whether the node binds the given variable declaration
       * (this can only happen for iterators) and if it does, which is the expression
       * the variable is bound to.
       *
       * @param vardecl The variable Declaration object
       * @return Node the variable is bound to or null
       */
      public SchematronConstraintNodeXslt2 nodeVariableIsBoundTo(OclNode.Declaration vardecl) {
  	return null;
      }
  
      /**
       * This method determines whether the given expression is a Variable or an
       * Attribute based on a Variable, which is identical to the one passed as
       * argument.
       *<br><br>
       * This implementation defines the default behavior.
       *
       * @param vardecl The Declaration of the variable
       * @return Flag indicating the inquired property
       */
      public boolean isVarOrAttribBased(OclNode.Declaration vardecl) {
  	return false;
      }
  
      /**
       * By means of this function you can inquire which Attribute node is generating
       * the objects represented by this node. Note that invocation is only sensible
       * for iterators and attributes.
       *
       * @return The retrieved Attribute node if there is such a thing
       */
      public Attribute generatingAttribute() {
  	return null;
      }
  
      /**
       * This predicate finds out whether the given node may produce a set.
       *<br><br>
       * This is the default implementation providing the value false.
       *
       * @return Flag indicating whether the node can return multiple values
       */
      public boolean isMultiple() {
  	return false;
      }
  
      /**
       * This predicate finds out whether the given node is realized by means of a
       * simple XML schema type.
       *
       * @return Flag indicating whether the node has a simple type
       */
      public boolean hasSimpleType() {
  	return true;
      }
  
      /**
       * This predicate finds out whether the given node is realized by means of a
       * class, which conceptually has identity.
       *
       * @return Flag indicating whether the node is an identity carrying type
       */
      public boolean hasIdentity() {
  	return false;
      }
  
      /**
       * Determine if the given class is instantiable, i.e. an XML element can be used
       * to encode an object of that class.
       *
       * @param ci
       * @return <code>false</code> if the class is abstract or suppressed (includes
       *         checking that rule-xsd-cls-suppress applies to the class), otherwise
       *         <code>true</code>
       */
      private static boolean isInstantiable(ClassInfo ci) {
  
  	if (ci.isAbstract() || (ci.suppressed() && ci.matches("rule-xsd-cls-suppress"))) {
  	    return false;
  	} else {
  	    return true;
  	}
      }
  
      protected boolean isAttributeWithVariableSelfAsSource(SchematronConstraintNodeXslt2 node) {
  
  	if (node instanceof Attribute) {
  	    Attribute att = (Attribute) node;
  	    SchematronConstraintNodeXslt2 source = att.children.get(0);
  	    if (source instanceof Variable && ((Variable) source).getName().equalsIgnoreCase("self")) {
  		return true;
  	    }
  	}
  
  	return false;
      }
  
      protected boolean isSimpleType(ClassInfo ci, String ciName) {
  
  	boolean isSimple = false;
  
  	if (ci != null) {
  	    Boolean indicatorSimpleType = XmlSchema.indicatorForObjectElementWithSimpleContent(ci);
  	    isSimple = !XmlSchema.classHasObjectElement(ci) || (indicatorSimpleType != null && indicatorSimpleType);
  	} else {
  	    String tname = ciName;
  	    String er = schemaObject.currentOclConstraintClass.encodingRule("xsd");
  	    MapEntry me = schemaObject.options.typeMapEntry(tname, er);
  	    if (me != null)
  		isSimple = me.p2.equalsIgnoreCase("simple/simple") || me.p2.equalsIgnoreCase("complex/simple");
  	}
  
  	return isSimple;
  
      }
  
      /**
       * @param ci
       * @return All direct and indirect subtypes of ci that are instantiable, i.e.
       *         not abstract or suppressed (includes checking that
       *         rule-xsd-cls-suppress applies to the class). Can be empty but not
       *         <code>null</code>.
       */
      private static SortedSet<ClassInfo> getInstantiableSubtypesInCompleteHierarchy(ClassInfo ci) {
  
  	SortedSet<ClassInfo> instantiableClasses = new TreeSet<>();
  
  	for (ClassInfo st : ci.subtypesInCompleteHierarchy()) {
  
  	    if (isInstantiable(st)) {
  		instantiableClasses.add(st);
  	    }
  	}
  
  	return instantiableClasses;
      }
  
      /**
       * An expression - including its XPath priority - which can be used to select
       * elements based upon the combination of local-names and namespace-uris of the
       * given classes. We differentiate three cases:
       *
       *
       * <ul>
       * <li>Case multiple classes: (local-name()='localName_ci_1' and
       * namespace-uri()='namespace_ci_1') or ... or (local-name()='localName_ci_n'
       * and namespace-uri()='namespace_ci_n')
       * <li>Case single class: local-name()='localName_ci' and
       * namespace-uri()='namespace_ci'
       * <li>Case no class: false()
       * </ul>
       *
       * @param cis
       * @return a pair, with left=expression and right=priority
       */
      private static Pair<String, Integer> getExpressionForTypeRestriction(SortedSet<ClassInfo> cis) {
  
  	String fragment = "";
  	Integer priority = -1;
  
  	/*
  	 * If we have more than one component, surround each part we add with
  	 * parentheses.
  	 */
  	boolean surroundWithBrackets = cis.size() > 1;
  
  	boolean first = true;
  
  	for (ClassInfo ci : cis) {
  
  	    if (!first) {
  		fragment += " or ";
  	    }
  
  	    first = false;
  
  	    String namespace = ci.pkg().targetNamespace();
  	    String localName = ci.name();
  
  	    MapEntry me = ci.options().elementMapEntry(ci.name(), ci.encodingRule("xsd"));
  
  	    if (me != null && StringUtils.isNotBlank(me.p1)) {
  
  		String xmlElement = me.p1;
  		String[] parts = xmlElement.split(":");
  
  		if (parts.length > 1) {
  		    String nspref = parts[0];
  		    String ns = ci.options().fullNamespace(nspref);
  		    if (ns != null) {
  			namespace = ns;
  			localName = parts[1];
  		    }
  		}
  	    }
  
  	    if (surroundWithBrackets) {
  		fragment += "(";
  	    }
  
  	    /*
  	     * 2018-02-06 JE: comparison based on QName as literal value (like 'ex:ClassX')
  	     * is dangerous, because it depends on a fixed namespace prefix. However, the
  	     * prefix of a namespace can vary. Therefore, I changed the logic to use
  	     * local-name() and namespace-uri().
  	     */
  	    fragment += "local-name()='" + localName + "' and namespace-uri()='" + namespace + "'";
  
  	    if (surroundWithBrackets) {
  		fragment += ")";
  	    }
  	}
  
  	// for case in which no relevant class was found
  	// 2019-09-10 JE: TBD - better to report an error here?
  	if (cis.isEmpty()) {
  	    fragment += "false()";
  	    priority = 20;
  	} else if (cis.size() == 1) {
  	    // expression consisting of a single 'and'
  	    priority = 5;
  	} else {
  	    // expression concatenated by one or more 'or'
  	    priority = 4;
  	}
  
  	return new ImmutablePair<String, Integer>(fragment, priority);
      }
  
      /**
       * Find out whether this construct contains a node of type
       * SchematronConstraintNodeXslt2.Error. In this case the whole tree is in error.
       *
       * @return Error flag
       */
      public boolean containsError() {
  	if (this instanceof Error)
  	    return true;
  	for (SchematronConstraintNodeXslt2 node : children)
  	    if (node.containsError())
  		return true;
  	return false;
      }
  
      /**
       * This abstract method compiles a node to an XPath expression fragment.
       *
       * @param ctx BindingContext this node shall be compiled in
       * @return Object containing the Xpath fragment
       */
      public abstract XpathFragment translate(BindingContext ctx);
  
      /**
       * This class stands for logical operations AND, OR, XOR and EQV. Which of these
       * is is coded in the state member logic.
       */
      public static class Logic extends SchematronConstraintNodeXslt2 {
  
  	protected enum LogicType {
  	    AND, OR, XOR, EQV
  	}
  
  	protected LogicType logic;
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 * @param logic        Flag to make this an AND (true) or an OR (false)
  	 */
  	public Logic(AbstractSchematronSchema schemaObject, LogicType logic) {
  	    this.schemaObject = schemaObject;
  	    this.logic = logic;
  	}
  
  	/**
  	 * Method to inquire whether the node inquired is a Logic node and this logic
  	 * node has the same <i>isAnd</i> polarity as specified in the parameter. XORs
  	 * and EQVs are ignored and yield false.
  	 *
  	 * 
  	 *
  	 * @param isAnd Flag: Are we an AND? (not an OR)?
  	 * @return True if this is Logic with the same AND/OR polarity
  	 */
  	public boolean isAndOrLogic(boolean isAnd) {
  
  	    if (logic == LogicType.XOR || logic == LogicType.EQV)
  		return false;
  	    return (this.logic == LogicType.AND) == isAnd;
  	}
  
  	/**
  	 * This compiles the node and its children to an Xpath predicate, which can be
  	 * inserted into a &lt;rule&gt;.
  	 *<br><br>
  	 * AND and OR are translated into their Xpath counterparts <i>and</i> and
  	 * <i>or</i>. XOR will be realized as a != operator, EQV by an = operator.
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 * @return Object containing the Xpath fragment and its operator priority
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    // Just one child? Pass through ...
  	    if (children.size() == 1)
  		return children.get(0).translate(ctx);
  
  	    // Which logic particle?
  	    String particle = null;
  	    int refprio;
  	    if (logic == LogicType.AND) {
  		particle = "and";
  		refprio = 5;
  	    } else if (logic == LogicType.OR) {
  		particle = "or";
  		refprio = 4;
  	    } else if (logic == LogicType.XOR) {
  		particle = "!=";
  		refprio = 6;
  	    } else { // EQV
  		particle = "=";
  		refprio = 6;
  	    }
  
  	    // In turn compile the node's children and connect them with logic
  	    // particles. Bracket the subexpression if necessary. When
  	    // processing a XOR/EQV (which is implemented as !=/= of booleans)
  	    // do the necessary conversion to boolean first.
  	    XpathFragment result = null;
  
  	    for (SchematronConstraintNodeXslt2 ocn : children) {
  
  		// Bracket or do the necessary boolean conversion
  		XpathFragment child_xpt = ocn.translate(ctx);
  
  		if ((logic == LogicType.XOR || logic == LogicType.EQV) && child_xpt.type != XpathType.BOOLEAN) {
  
  		    child_xpt.fragment = "boolean(" + child_xpt.fragment + ")";
  		    child_xpt.type = XpathType.BOOLEAN;
  		    child_xpt.priority = 20;
  
  		} else if (child_xpt.priority <= refprio) {
  
  		    child_xpt.bracket();
  		}
  
  		// Apply operation and merge
  		child_xpt.atEnd.setState(BindingContext.CtxState.NONE);
  		if (result == null) {
  		    result = child_xpt;
  		} else {
  		    String mrge = result.merge(child_xpt);
  		    result.fragment += " " + particle + " ";
  		    result.fragment += mrge;
  		}
  	    }
  
  	    // Correct priority and type
  	    result.priority = refprio;
  	    result.type = XpathType.BOOLEAN;
  	    result.atEnd = new BindingContext(BindingContext.CtxState.NONE);
  
  	    return result;
  	}
      }
  
      /**
       * This class stands for comparisons. The operator is given as a String, which
       * can take the values: =, &lt;&gt;, &lt;, &lt;=, &gt;, &gt;=.
       */
      public static class Comparison extends SchematronConstraintNodeXslt2 {
  
  	// The relational operator
  	String opname;
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 * @param name         One of =, &lt;&gt;, &lt;, &lt;=, &gt;, &gt;=
  	 */
  	public Comparison(AbstractSchematronSchema schemaObject, String name) {
  	    this.schemaObject = schemaObject;
  	    opname = name.equals("<>") ? "!=" : name;
  	}
  
  	/**
  	 * This compiles the node and its children to Xpath. Xpath can express all
  	 * required comparison operators.
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 * @return Object containing the XPath fragment and its operator priority
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    // Operator priority
  	    int refprio = 6;
  
  	    // Check and compile children
  	    XpathFragment[] child_xpt = new XpathFragment[2];
  
  	    for (int i = 0; i < 2; i++) {
  
  		SchematronConstraintNodeXslt2 child = children.get(i);
  
  		if (!child.hasSimpleType() && !child.hasIdentity()) {
  		    return new XpathFragment(20, "***ERROR[126]***");
  		}
  
  		child_xpt[i] = child.translate(ctx);
  		if (child.hasIdentity()) {
  		    child_xpt[i].fragment = "generate-id(" + child_xpt[i].fragment + ")";
  		    child_xpt[i].priority = 20;
  		}
  
  		if (child_xpt[i].fragment.length() == 0) {
  		    child_xpt[i].fragment = ".";
  		}
  		if (child_xpt[i].priority <= refprio) {
  		    child_xpt[i].bracket();
  		}
  		child_xpt[i].atEnd.setState(BindingContext.CtxState.NONE);
  	    }
  
  	    // Construct the result
  	    String op2 = child_xpt[0].merge(child_xpt[1]);
  	    child_xpt[0].fragment += " " + opname + " " + op2;
  	    child_xpt[0].type = XpathType.BOOLEAN;
  	    child_xpt[0].priority = refprio;
  
  	    return child_xpt[0];
  	}
      }
  
      /**
       * This one stands for the OCL <i>isEmpty()</i> and <i>notEmpty()</i> predicate
       * operations. Which of these is meant is expressed in the state variable
       * <i>negated</i>.
       */
      public static class Empty extends SchematronConstraintNodeXslt2 {
  
  	// negated means: isEmpty (false) or notEmpty (true)
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 * @param neg          Flag: isEmpty (false) and notEmpty (true)
  	 */
  	public Empty(AbstractSchematronSchema schemaObject, boolean neg) {
  	    this.schemaObject = schemaObject;
  	    negated = neg;
  	}
  
  	/**
  	 * This compiles the node and its children to an Xpath fragment. The translation
  	 * is essentially the nodeset derived from the object part of the expression,
  	 * because notEmpty() is fulfilled for a nodeset, which converts to a boolean
  	 * true. isEmpty() requires an additional not().
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 * @return Object containing the fragment and its operator priority
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    // Fetch and compile the object
  	    SchematronConstraintNodeXslt2 obj = children.get(0);
  	    XpathFragment xpt = obj.translate(ctx);
  	    if (xpt.fragment.length() == 0) {
  		xpt.fragment = ".";
  	    }
  
  	    /*
  	     * NOTE: In the case of notEmpty(), the XPath results in a node set. The default
  	     * type of the XpathFragment is therefore ok. In the negated form (isEmpty())
  	     * the addition of not(..) results in a type change to BOOLEAN.
  	     */
  
  	    // isEmpty() requires an additional not(...)
  	    if (!negated) {
  		xpt.fragment = "not(" + xpt.fragment + ")";
  		xpt.type = XpathType.BOOLEAN;
  		xpt.priority = 20;
  		xpt.atEnd.setState(BindingContext.CtxState.NONE);
  	    }
  
  	    return xpt;
  	}
      }
  
      /**
       * This class represents the Exists iterator predicate. Being part of the Logic
       * system Exists can be negated, which is realized by applying an additional
       * not().
       */
      public static class Exists extends SchematronConstraintNodeXslt2 {
  
  	// The variable declaration attached to the iterator
  	OclNode.Declaration vardecl;
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 * @param vardecl      OclNode.Declaration object
  	 * @param neg          Negation flag
  	 */
  	public Exists(AbstractSchematronSchema schemaObject, OclNode.Declaration vardecl, boolean neg) {
  	    this.schemaObject = schemaObject;
  	    this.negated = neg;
  	    this.vardecl = vardecl;
  	}
  
  	/**
  	 * This method determines whether the Exists binds the given variable
  	 * declaration and if it does, which is the expression the variable is bound to.
  	 *
  	 * @param vardecl The variable Declaration object
  	 * @return Node the variable is bound to or null
  	 */
  	public SchematronConstraintNodeXslt2 nodeVariableIsBoundTo(OclNode.Declaration vardecl) {
  	    if (this.vardecl == vardecl)
  		return children.get(0);
  	    return null;
  	}
  
  	/**
  	 * This compiles the node and its children to an Xpath expression fragment.
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 * @return Object containing the fragment and its operator priority
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    // Fetch and compile the object
  	    SchematronConstraintNodeXslt2 selection = children.get(0);
  	    XpathFragment xpt = selection.translate(ctx);
  
  	    // Prepare the binding context for compilation of the iterator
  	    // body. This is primarily the context at the end of the object
  	    // plus the variable.
  	    BindingContext bodyctx = xpt.atEnd.clone();
  	    bodyctx.pushDeclaration(vardecl);
  	    bodyctx.inPredicateExpression = false;
  
  	    // Compile the boolean expression in the iterator body
  	    SchematronConstraintNodeXslt2 condition = children.get(1);
  	    XpathFragment cond = condition.translate(bodyctx);
  	    cond.atEnd = null; // This suppresses the merging of ending contexts
  
  	    String filter = xpt.merge(cond);
  	    if (xpt.priority < 3) {
  		xpt.bracket();
  	    }
  	    xpt.fragment = "some $" + vardecl.name + " in " + xpt.fragment + " satisfies " + filter;
  	    xpt.priority = 3;
  	    xpt.type = XpathType.BOOLEAN;
  
  	    // Consider negated form
  	    if (negated) {
  		xpt.fragment = "not(" + xpt.fragment + ")";
  		xpt.priority = 20;
  		xpt.atEnd.setState(BindingContext.CtxState.NONE);
  	    }
  
  	    return xpt;
  	}
      }
  
      /** This class represents the ForAll iterator predicate. */
      public static class ForAll extends SchematronConstraintNodeXslt2 {
  
  	// The variable declaration attached to the iterator
  	OclNode.Declaration vardecl;
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 * @param vardecl      OclNode.Declaration object
  	 * @param neg          Negation flag
  	 */
  	public ForAll(AbstractSchematronSchema schemaObject, OclNode.Declaration vardecl, boolean neg) {
  	    this.schemaObject = schemaObject;
  	    this.negated = neg;
  	    this.vardecl = vardecl;
  	}
  
  	/**
  	 * This method determines whether the ForAll binds the given variable
  	 * declaration and if it does, which is the expression the variable is bound to.
  	 *
  	 * @param vardecl The variable Declaration object
  	 * @return Node the variable is bound to or null
  	 */
  	public SchematronConstraintNodeXslt2 nodeVariableIsBoundTo(OclNode.Declaration vardecl) {
  	    if (this.vardecl == vardecl)
  		return children.get(0);
  	    return null;
  	}
  
  	/**
  	 * This compiles the node and its children to an Xpath expression fragment.
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 * @return Object containing the fragment and its operator priority
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    // Fetch and compile the object
  	    SchematronConstraintNodeXslt2 selection = children.get(0);
  	    XpathFragment xpt = selection.translate(ctx);
  
  	    // Prepare the binding context for compilation of the iterator
  	    // body. This is primarily the context at the end of the object
  	    // plus the variable.
  	    BindingContext bodyctx = xpt.atEnd.clone();
  	    bodyctx.pushDeclaration(vardecl);
  	    bodyctx.inPredicateExpression = false;
  
  	    // Compile the boolean expression in the iterator body
  	    SchematronConstraintNodeXslt2 condition = children.get(1);
  	    XpathFragment cond = condition.translate(bodyctx);
  	    cond.atEnd = null; // This suppresses the merging of ending contexts
  
  	    String filter = xpt.merge(cond);
  	    if (xpt.priority < 3) {
  		xpt.bracket();
  	    }
  	    xpt.fragment = "every $" + vardecl.name + " in " + xpt.fragment + " satisfies " + filter;
  	    xpt.priority = 3;
  	    xpt.type = XpathType.BOOLEAN;
  
  	    // Consider negated form
  	    if (negated) {
  		xpt.fragment = "not(" + xpt.fragment + ")";
  		xpt.priority = 20;
  		xpt.atEnd.setState(BindingContext.CtxState.NONE);
  	    }
  
  	    return xpt;
  	}
      }
  
      /**
       * This class represents the isUnique iterator predicate. Being part of the
       * Logic system Unique can be negated, which is realized by applying an
       * additional not().
       */
      public static class Unique extends SchematronConstraintNodeXslt2 {
  
  	public static final String IS_UNIQUE_EMPTY_TOKEN = "SC_EMPTY_ISU_BODY";
  
  	// The variable declaration attached to the iterator
  	OclNode.Declaration vardecl;
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 * @param vardecl      OclNode.Declaration object
  	 * @param neg          Negation flag
  	 */
  	public Unique(AbstractSchematronSchema schemaObject, OclNode.Declaration vardecl, boolean neg) {
  	    this.schemaObject = schemaObject;
  	    this.negated = neg;
  	    this.vardecl = vardecl;
  	}
  
  	/**
  	 * This method determines whether the Unique binds the given variable
  	 * declaration and if it does, which is the expression the variable is bound to.
  	 *
  	 * @param vardecl The variable Declaration object
  	 * @return Node the variable is bound to or null
  	 */
  	public SchematronConstraintNodeXslt2 nodeVariableIsBoundTo(OclNode.Declaration vardecl) {
  	    if (this.vardecl == vardecl)
  		return children.get(0);
  	    return null;
  	}
  
  	/**
  	 * This compiles the node and its children to an Xpath expression fragment.
  	 *<br><br>
  	 * The object is translated in the given context. If negated an additional not()
  	 * is applied.
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 * @return Object containing the fragment and its operator priority
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    // Local negation variable allows to absorb negation en route
  	    boolean neg = negated;
  
  	    // Fetch and compile the object
  	    SchematronConstraintNodeXslt2 obj = children.get(0);
  	    XpathFragment xpt = obj.translate(ctx);
  
  	    // Get hold of the expression argument and take cases on its
  	    // nature ...
  	    SchematronConstraintNodeXslt2 body = children.get(1);
  
  	    if (!body.isDependentOn(vardecl)) {
  
  		// A constant expression can be unique only if the object
  		// is of cardinality zero or one.
  		xpt.fragment = "count(" + xpt.fragment + ") " + (neg ? ">" : "<=") + " 1";
  		neg = false;
  		xpt.priority = 6;
  
  	    } else if (body.isVarOrAttribBased(vardecl) && body instanceof Variable) {
  
  		/*
  		 * This is the identity case. Example: x->isUnique(y|y)
  		 */
  
  		boolean simple = false;
  		boolean hasIdentity = false;
  
  		// Determine the Attribute behind the object
  		Attribute obat = null;
  		// The type name is relevant in case of 'Boolean'
  		String objAttNameOfLastClass = null;
  
  		if (obj instanceof Attribute)
  		    obat = (Attribute) obj;
  		else if (obj instanceof Variable) {
  		    obat = obj.generatingAttribute();
  		}
  
  		// TODO: error if obat cannot be determined?
  
  		if (obat != null) {
  
  		    simple = obat.hasSimpleType();
  		    hasIdentity = obat.hasIdentity();
  
  		    objAttNameOfLastClass = obat.attributes[obat.attributes.length - 1].main.dataType.name;
  		}
  
  		String objXpt = xpt.fragment;
  
  		if (isAttributeWithVariableSelfAsSource(obj)) {
  		    objXpt = "$" + xpt.findOrAdd(xpt.fragment);
  		}
  
  		if (simple) {
  
  		    xpt.fragment = "count(" + objXpt + ") = count(distinct-values(" + objXpt;
  		    if ("Boolean".equalsIgnoreCase(objAttNameOfLastClass)) {
  			xpt.fragment += "/xs:boolean(.)";
  		    }
  		    xpt.fragment += "))";
  
  		    xpt.priority = 6;
  
  		} else if (hasIdentity) {
  
  		    // complex type with identity
  
  		    xpt.fragment = "count(" + objXpt + ") = count(distinct-values(" + objXpt + "/@*:id" + "))";
  
  		    xpt.priority = 6;
  
  		} else {
  
  		    // complex type without identity
  
  		    xpt.fragment = "for $COUNT1 in count(" + objXpt + "), $COUNT2 in " + "sum(for $ISUVAR1 in " + objXpt
  			    + ", $ISUVAR2 in " + objXpt + " return ("
  			    + "if(empty($ISUVAR1) and empty($ISUVAR2)) then 0 else "
  			    + "if ((empty($ISUVAR1) and not(empty($ISUVAR2))) or (not(empty($ISUVAR1)) and empty($ISUVAR2))) then 1 else "
  			    + "if (generate-id($ISUVAR1) = generate-id($ISUVAR2)) then 0 else "
  			    + "if (deep-equal($ISUVAR1,$ISUVAR2)) then 0 else 1)) "
  			    + "return $COUNT1 * ($COUNT1 - 1) = $COUNT2";
  
  		    xpt.priority = 3;
  		}
  
  	    } else if (body.isVarOrAttribBased(vardecl)) {
  
  		/*
  		 * If we have an attribute, which is based on the binding variable, we can try a
  		 * translation. Precondition, however, is that all selectors have an attached
  		 * cardinality of 1 or 0.
  		 */
  
  		if (body.isMultiple()) {
  
  		    // Multiple selectors encountered. Signal implementation
  		    // restriction ...
  		    return new XpathFragment(20, "***ERROR[121]***");
  
  		} else {
  
  		    // Non-multiple attribute access using the binding variable.
  		    Attribute bodyAtt = (Attribute) body;
  		    // The type name is relevant in case of 'Boolean'
  		    String objAttNameOfLastClass = bodyAtt.attributes[bodyAtt.attributes.length - 1].main.dataType.name;
  
  		    boolean simple = bodyAtt.hasSimpleType();
  		    boolean hasIdentity = bodyAtt.hasIdentity();
  
  		    String objXpt = xpt.fragment;
  
  		    if (isAttributeWithVariableSelfAsSource(obj)) {
  			objXpt = "$" + xpt.findOrAdd(xpt.fragment);
  		    }
  
  		    // Prepare the binding context for compilation of the iterator
  		    // body. This is primarily the context at the end of the object
  		    // plus the variable.
  		    BindingContext bodyctx = xpt.atEnd.clone();
  		    bodyctx.pushDeclaration(vardecl);
  		    bodyctx.inPredicateExpression = false;
  
  		    // Compile the expression in the iterator body
  		    XpathFragment bodyXpt = body.translate(bodyctx);
  		    bodyXpt.atEnd = null; // This suppresses the merging of ending contexts
  
  		    String iterVar = "$" + vardecl.name;
  
  		    if (simple) {
  
  			xpt.fragment = "count(" + objXpt + ") = count(distinct-values(";
  
  			xpt.fragment += "for " + iterVar + " in " + objXpt + " return (if (empty(" + bodyXpt.fragment
  				+ ")) then '" + IS_UNIQUE_EMPTY_TOKEN + "' else " + bodyXpt.fragment;
  
  			if ("Boolean".equalsIgnoreCase(objAttNameOfLastClass)) {
  			    xpt.fragment += "/xs:boolean(.)";
  			}
  			xpt.fragment += ")))";
  
  			xpt.priority = 6;
  
  		    } else if (hasIdentity) {
  
  			// complex type with identity
  
  			xpt.fragment = "count(" + objXpt + ") = count(distinct-values(" + "for " + iterVar + " in "
  				+ objXpt + " return (if (empty(" + bodyXpt.fragment + ")) then '"
  				+ IS_UNIQUE_EMPTY_TOKEN + "' else " + bodyXpt.fragment + "/@*:id" + ")))";
  
  			xpt.priority = 6;
  
  		    } else {
  
  			// complex type without identity
  
  			xpt.fragment = "for $COUNT1 in count(" + objXpt + "), $COUNT2 in " + "sum(" + "for " + iterVar
  				+ " in " + objXpt + ", $ISUVAR1 in " + bodyXpt.fragment + ", $ISUVAR2 in "
  				+ bodyXpt.fragment + " return ("
  				+ "if(empty($ISUVAR1) and empty($ISUVAR2)) then 0 else "
  				+ "if ((empty($ISUVAR1) and not(empty($ISUVAR2))) or (not(empty($ISUVAR1)) and empty($ISUVAR2))) then 1 else "
  				+ "if (generate-id($ISUVAR1) = generate-id($ISUVAR2)) then 0 else "
  				+ "if (deep-equal($ISUVAR1,$ISUVAR2)) then 0 else 1)) "
  				+ "return $COUNT1 * ($COUNT1 - 1) = $COUNT2";
  
  			xpt.priority = 3;
  		    }
  
  		}
  
  	    } else {
  
  		// Expression other than constant, identity of attribute
  		// access. We cannot express this in Xpath syntax
  		return new XpathFragment(20, "***ERROR[122]***");
  	    }
  
  	    xpt.type = XpathType.BOOLEAN;
  	    xpt.atEnd.setState(BindingContext.CtxState.NONE);
  
  	    // Consider negated form
  	    if (neg) {
  		xpt.fragment = "not(" + xpt.fragment + ")";
  		xpt.priority = 20;
  	    }
  
  	    return xpt;
  	}
      }
  
      /** This class represents the Select iterator filter. */
      public static class Select extends SchematronConstraintNodeXslt2 {
  
  	// The variable declaration attached to the iterator
  	protected OclNode.Declaration vardecl;
  
  	// Stored generator body
  	protected SchematronConstraintNodeXslt2 generatorBody = null;
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 * @param vardecl      OclNode.Declaration object
  	 */
  	public Select(AbstractSchematronSchema schemaObject, OclNode.Declaration vardecl) {
  	    this.schemaObject = schemaObject;
  	    this.vardecl = vardecl;
  	}
  
  	/**
  	 * This method determines whether the Select binds the given variable
  	 * declaration and if it does, which is the expression the variable is bound to.
  	 *
  	 * @param vardecl The variable Declaration object
  	 * @return Node the variable is bound to or null
  	 */
  	public SchematronConstraintNodeXslt2 nodeVariableIsBoundTo(OclNode.Declaration vardecl) {
  	    if (this.vardecl == vardecl)
  		return children.get(0);
  	    return null;
  	}
  
  	/**
  	 * By means of this function you can inquire which Attribute node is generating
  	 * the objects of this Select node if any.
  	 *
  	 * @return The retrieved Attribute node if there is such a thing
  	 */
  	public Attribute generatingAttribute() {
  	    return children.get(0).generatingAttribute();
  	}
  
  	/**
  	 * The value of Select is always a set.
  	 *
  	 * @return Flag indicating whether the node can return multiple values
  	 */
  	public boolean isMultiple() {
  	    return true;
  	}
  
  	/**
  	 * This predicate finds out whether the Select results in a simple XML schema
  	 * type.
  	 *
  	 * @return Flag indicating whether the node has a simple type
  	 */
  	public boolean hasSimpleType() {
  	    return children.get(0).hasSimpleType();
  	}
  
  	/**
  	 * This predicate finds out whether the Select results in a collection of
  	 * instances, which conceptually have identity.
  	 *
  	 * @return Flag indicating whether the node is an identity carrying type
  	 */
  	public boolean hasIdentity() {
  	    return children.get(0).hasIdentity();
  	}
  
  	/**
  	 * This compiles the Select node and its children to an Xpath expression
  	 * fragment.
  	 *<br><br>
  	 * The object is translated in the given context and its ending position,
  	 * supplemented by the binding variable, defines the context for the compilation
  	 * of the body, which is appended as a predicate bracket.
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 * @return Object containing the fragment and its operator priority
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    // Fetch and compile the object
  	    SchematronConstraintNodeXslt2 obj = children.get(0);
  	    XpathFragment xpt = obj.translate(ctx);
  
  	    // Prepare the binding context for compilation of the iterator
  	    // body. This is primarily the context at the end of the object
  	    // plus the variable.
  	    BindingContext bodyctx = xpt.atEnd.clone();
  	    bodyctx.pushDeclaration(vardecl);
  
  	    bodyctx.inPredicateExpression = true;
  
  	    // Compile the boolean expression in the iterator body
  	    SchematronConstraintNodeXslt2 pred = children.get(1);
  	    XpathFragment prd = pred.translate(bodyctx);
  	    prd.atEnd = null; // This suppresses the merging of ending contexts
  
  	    // Append the boolean expression as a predicate filter
  	    String filter = xpt.merge(prd);
  	    if (xpt.priority < 19) {
  		xpt.bracket();
  	    }
  
  	    xpt.fragment += "[";
  	    if (!vardecl.name.equalsIgnoreCase("(noname)")) {
  		xpt.fragment += "for $" + vardecl.name + " in . return ";
  	    }
  	    xpt.fragment += filter + "]";
  
  	    xpt.priority = 19;
  
  	    return xpt;
  	}
      }
  
      /**
       * This class represents the OCL operation allInstances(). AllInstances is based
       * on a class literal and represents all instances of that class.
       */
      public static class AllInstances extends SchematronConstraintNodeXslt2 {
  
  	protected ClassInfo objectClass;
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 * @param ci           ClassInfo of the class to enumerate
  	 * @param negated      May be negated if of type boolean
  	 */
  	public AllInstances(AbstractSchematronSchema schemaObject, ClassInfo ci, boolean negated) {
  	    this.schemaObject = schemaObject;
  	    this.objectClass = ci;
  	    this.negated = negated;
  	}
  
  	/**
  	 * Allinstances always produces a set.
  	 *
  	 * @return Flag indicating whether the node can return multiple values
  	 */
  	public boolean isMultiple() {
  	    return true;
  	}
  
  	/**
  	 * allInstances() is never simple.
  	 *
  	 * @return Flag indicating whether the node has a simple type
  	 */
  	public boolean hasSimpleType() {
  	    return false;
  	}
  
  	/**
  	 * This predicate finds out whether the allInstances results in a collection of
  	 * instances, which conceptually have identity.
  	 *
  	 * @return Flag indicating whether the node is an identity carrying type
  	 */
  	public boolean hasIdentity() {
  	    return XmlSchema.classCanBeReferenced(objectClass);
  	}
  
  	/**
  	 * allInstances() is translated to a search for the given type. The result is a
  	 * nodeset containing all the given features.
  	 *<br><br>
  	 * In compiling x.allInstances() we create an expression that selects all
  	 * elements based upon a predicate which uses a combination of local-names and
  	 * namespace-uris of the type and its subtypes (if they are instantiable, i.e.
  	 * not abstract and not suppressed). We differentiate three cases:
  	 *
  	 * <ul>
  	 * <li>Case multiple instantiable classes: //*[(local-name()='localName_class_1'
  	 * and namespace-uri()='namespace_class_1') or ... or
  	 * (local-name()='localName_class_n' and namespace-uri()='namespace_class_n')]
  	 * <li>Case single instantiable class: //*[local-name()='localName_class' and
  	 * namespace-uri()='namespace_class']
  	 * <li>Case no instantiable class: //*[false()]
  	 * </ul>
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    // 1st obtain the necessary classes from the model
  	    SortedSet<ClassInfo> relevantClasses = getInstantiableSubtypesInCompleteHierarchy(objectClass);
  
  	    // also take into account the object class itself
  	    if (isInstantiable(objectClass)) {
  		relevantClasses.add(objectClass);
  	    }
  
  	    // 2nd create the Xpath expression combining all those classes
  	    String fragment = "//*[";
  	    fragment += getExpressionForTypeRestriction(relevantClasses).getLeft();
  	    fragment += "]";
  
  	    // Wrap and provide proper priority. Set binding context.
  	    XpathFragment xpt = new XpathFragment(19, fragment);
  
  	    xpt.atEnd = new BindingContext(BindingContext.CtxState.OTHER);
  
  	    String var = xpt.findOrAdd(fragment);
  	    xpt.fragment = "$" + var;
  	    xpt.priority = 20;
  	    // xpt.type = NODESET - set by default; already correct
  
  	    return xpt;
  	}
      }
  
      /**
       * This class represents the operation propertyMetadata(). It selects the
       * metadata object associated with a property.
       */
      public static class PropertyMetadata extends SchematronConstraintNodeXslt2 {
  
  	protected ClassInfo metadataType;
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 * @param metadataType  tbd
  	 * @param negated  tbd
  	 */
  	public PropertyMetadata(AbstractSchematronSchema schemaObject, ClassInfo metadataType, boolean negated) {
  	    this.schemaObject = schemaObject;
  	    this.metadataType = metadataType;
  	    this.negated = negated;
  	}
  
  	/**
  	 * PropertyMetadata can produce a set. That depends on the collection (of
  	 * property values, taking into account multiple property steps, i.e. a sequence
  	 * of implicit collect operations) or variable it is operating on.
  	 *
  	 * @return Flag indicating whether the node can return multiple values
  	 */
  	public boolean isMultiple() {
  	    return true;
  	}
  
  	/**
  	 * propertyMetadata() is never simple. Otherwise it could not be referenced.
  	 *
  	 * @return Flag indicating whether the node has a simple type
  	 */
  	public boolean hasSimpleType() {
  	    return false;
  	}
  
  	/**
  	 * This predicate finds out whether the propertyMetadata results in a collection
  	 * of instances, which conceptually have identity.
  	 *
  	 * @return Flag indicating whether the node is an identity carrying type
  	 */
  	public boolean hasIdentity() {
  	    return XmlSchema.classCanBeReferenced(metadataType);
  	}
  
  	/**
  	 * propertyMetadata() is translated to a lookup of referenced metadata. The
  	 * result is a nodeset containing metadata objects.
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    SchematronConstraintNodeXslt2 objnode = children.get(0);
  	    // update the binding context to let attribute translation know that access to
  	    // property metadata is the intent
  	    ctx.propertyMetadataAccess = true;
  	    XpathFragment obj = objnode.translate(ctx);
  	    ctx.propertyMetadataAccess = false;
  
  	    if (objnode instanceof SchematronConstraintNodeXslt2.Variable) {
  		obj.fragment = "***ERROR[128]***";
  	    } else {
  
  		// Get the prefixes that possibly surround the identifier contained
  		// in xlink:href references
  		String alpha = schemaObject.alpha;
  		String beta = schemaObject.beta;
  		boolean alphaEx = StringUtils.isNotBlank(alpha);
  		boolean betaEx = StringUtils.isNotBlank(beta);
  
  		String metalink = (obj.fragment.isEmpty() ? "current()" : obj.fragment);
  		if (obj.priority < 18) {
  		    metalink = "(" + metalink + ")";
  		}
  		metalink += "/@metadata";
  
  		String refIdExpr = null;
  		if (alphaEx && betaEx) {
  		    // identifier prefix and suffix exist
  		    refIdExpr = "substring-before(substring-after($METAREFVAR,'" + alpha + "'),'" + beta + "')";
  		} else if (alphaEx) {
  		    // only identifier prefix exists
  		    refIdExpr = "substring-after($METAREFVAR,'" + alpha + "')";
  		} else if (betaEx) {
  		    // only identifier suffix exists
  		    refIdExpr = "substring-before($METAREFVAR,'" + beta + "')";
  		} else {
  		    // neither identifier prefix nor suffix exist
  		    refIdExpr = "$METAREFVAR";
  		}
  
  		obj.fragment = "for $METAREFVAR in " + metalink + " return key('idKey'," + refIdExpr + ")";
  		schemaObject.addIdKey();
  
  		obj.priority = 3;
  
  		// Treat negation. Note that if this is being negated it must be
  		// unique and boolean ...
  		if (negated) {
  		    obj.fragment = "not(" + obj.fragment + ")";
  		    obj.priority = 20;
  		    obj.atEnd.setState(BindingContext.CtxState.NONE);
  		}
  	    }
  
  	    return obj;
  	}
      }
  
      /**
       * This class represents oclIsKindOf and oclIsTypeOf nodes. The difference
       * between the two is expressed in an <i>exact</i> flag. The object also carries
       * a negation flag to express that an object is NOT kind of some type. The type
       * is given by the first of the children.
       */
      public static class KindOf extends SchematronConstraintNodeXslt2 {
  
  	// The class to be tested against
  	protected ClassInfo argumentClass = null;
  	// Exact match?
  	protected boolean exact = false;
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 * @param exact        Flag: Only check the given type
  	 * @param neg          Flag: Negated meaning
  	 */
  	public KindOf(AbstractSchematronSchema schemaObject, boolean exact, boolean neg) {
  	    this.schemaObject = schemaObject;
  	    this.exact = exact;
  	    negated = neg;
  	}
  
  	/**
  	 * If the class to be tested against is already known (it is not an expression)
  	 * this reference can be set via this method.
  	 *
  	 * @param ci ClassInfo representing the type to be tested against
  	 */
  	public void setClass(ClassInfo ci) {
  	    argumentClass = ci;
  	}
  
  	/**
  	 * This compiles the KindOf predicate (and its negation) to an equivalent Xpath
  	 * expression fragment. KindOf is translated to a predicate which compares the
  	 * element name against all instantiable subtypes of the given type, as well as
  	 * that type.
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 * @return Object containing the Xpath fragment
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    // Translate the object in the given context
  	    SchematronConstraintNodeXslt2 objnode = children.get(0);
  	    XpathFragment xptobj = objnode.translate(ctx);
  	    boolean emptyobject = xptobj.fragment.length() == 0;
  
  	    // Obtain the necessary classes from the model
  	    SortedSet<ClassInfo> relevantClasses = new TreeSet<>();
  
  	    if (!exact) {
  		relevantClasses = getInstantiableSubtypesInCompleteHierarchy(argumentClass);
  	    }
  
  	    if (isInstantiable(argumentClass)) {
  		relevantClasses.add(argumentClass);
  	    }
  
  	    // Construct the result expression
  	    if (relevantClasses.size() == 0) {
  
  		// There is no concrete class known. So, this must be false.
  		xptobj.fragment = negated ? "true()" : "false()";
  		xptobj.priority = 20;
  		xptobj.type = XpathType.BOOLEAN;
  		xptobj.atEnd.setState(BindingContext.CtxState.NONE);
  
  	    } else {
  
  		// There are one or more classes, append a predicate which
  		// compares against all possible names.
  
  		Pair<String, Integer> pair = getExpressionForTypeRestriction(relevantClasses);
  		String expression = pair.getLeft();
  		int expressionPriority = pair.getRight();
  
  		if (negated) {
  		    expression = "not(" + expression + ")";
  		}
  
  		if (!emptyobject) {
  		    if (xptobj.priority < 19) {
  			xptobj.bracket();
  		    }
  		    xptobj.fragment += "[" + expression + "]";
  		    xptobj.priority = 19;
  		} else {
  		    xptobj.fragment = expression;
  		    xptobj.priority = negated ? 20 : expressionPriority;
  		}
  	    }
  
  	    return xptobj;
  	}
      }
  
      /**
       * This class represents oclAsType(), which is for casting a type to one of its
       * subtypes.
       */
      public static class Cast extends SchematronConstraintNodeXslt2 {
  
  	// The class to be cast to
  	protected ClassInfo argumentClass = null;
  
  	// Connection info, required to access the config document
  	protected String targetClassName;
  
  	/**
  	 * Ctor
  	 *
  	 * @param schemaObject The schema object
  	 */
  	public Cast(AbstractSchematronSchema schemaObject) {
  	    this.schemaObject = schemaObject;
  	}
  
  	/**
  	 * If the class to be cast to is already known (it is not an expression) this
  	 * reference can be set via this method.
  	 *
  	 * @param ci ClassInfo representing the type to be cast to
  	 */
  	public void setClass(ClassInfo ci) {
  	    argumentClass = ci;
  	}
  
  	/**
  	 * This predicate finds out whether the Cast results in a simple XML schema
  	 * type.
  	 *
  	 * @return Flag indicating whether the node has a simple type
  	 */
  	public boolean hasSimpleType() {
  	    return isSimpleType(argumentClass, argumentClass.name());
  	}
  
  	/**
  	 * This predicate finds out whether the Cast results in an instance, which
  	 * conceptually has identity.
  	 *
  	 * @return Flag indicating whether the node is an identity carrying type
  	 */
  	public boolean hasIdentity() {
  	    return XmlSchema.classCanBeReferenced(argumentClass);
  	}
  
  	/**
  	 * This compiles the Cast to an Xpath fragment.
  	 *<br><br>
  	 * We realize this by making sure the current element is of the requested type
  	 * or any of its concrete subtypes.
  	 *
  	 * @param ctx BindingContext this node shall be compiled in
  	 * @return Object containing the Xpath fragment
  	 */
  	public XpathFragment translate(BindingContext ctx) {
  
  	    // Translate the object in the given context
  	    SchematronConstraintNodeXslt2 objnode = children.get(0);
  	    XpathFragment xptobj = objnode.translate(ctx);
  	    if (xptobj.fragment.length() == 0) {
  		xptobj.fragment = "self::*";
  		xptobj.priority = 19;
  	    }
  
  	    if (isAttributeWithVariableSelfAsSource(objnode)) {
  		xptobj.fragment = "$" + xptobj.findOrAdd(xptobj.fragment);
  		xptobj.priority = 20;
  	    }
  
  	    // Obtain the necessary classes from the model
  	    SortedSet<ClassInfo> relevantClasses = getInstantiableSubtypesInCompleteHierarchy(argumentClass);
  
  	    // also take into account the argument class itself
  	    if (isInstantiable(argumentClass)) {
  		relevantClasses.add(argumentClass);
  	    }
  
  	    // Append a predicate which compares against all possible
  	    // names.
  	    if (xptobj.priority < 19) {
  		xptobj.bracket();
  	    }
  	    xptobj.fragment += ("[" + getExpressionForTypeRestriction(relevantClasses).getLeft() + "]");
  	    xptobj.priority = 19;
  
  	    // If we are casting a CharacterString to a Codelist, we have to add
  	    // code, which property expresses the codelist access according to
  	    // the applied encoding rule.
  
  	    if (argumentClass.category() == Options.CODELIST && (argumentClass.matches("rule-xsd-all-naming-19139")
  		    || (argumentClass.matches("rule-xsd-cls-codelist-asDictionaryGml33")
  			    && argumentClass.asDictionaryGml33())
  		    || (argumentClass.matches("rule-xsd-cls-codelist-asDictionary") && argumentClass.asDictionary()))) {
  
  		// We need to know, whether we are subject to the 19139 regime
  		// or if GML 3.3 encoding rules apply
  		Attribute att = objnode.generatingAttribute();
  		PropertyInfo pip = ((PropertyInfo) (att.attributes[att.attributes.length
  			- 1].main.selector.modelProperty));
  		boolean is19139 = argumentClass.matches("rule-xsd-all-naming-19139");
  		boolean isgml33 = pip != null && pip.inClass().matches("rule-xsd-cls-codelist-asDictionaryGml33");
  
  		/*
  		 * If not translating according to GML 3.3 we need to prepare the
  		 * CodeListValuePattern.
  		 */
  		String clvpat = "{value}";
  		int nsubst = 1;
  
  		if (!isgml33) {
  
  		    String uri = argumentClass.taggedValue("codeList");
  		    if (StringUtils.isNotBlank(uri)) {
  			clvpat = "{codeList}/{value}";
  		    }
  
  		    clvpat = this.schemaObject.determineCodeListValuePattern(argumentClass, clvpat);
  
  		    clvpat = clvpat.replace("{codeList}", "',{codeList},'");
  		    clvpat = clvpat.replace("{value}", "',{value},'");
  		    if (clvpat.startsWith("',")) {
  			clvpat = clvpat.substring(2);
  		    }
  		    if (clvpat.endsWith(",'")) {
  			clvpat = clvpat.substring(0, clvpat.length() - 2);
  		    }
  		    if (!clvpat.startsWith("{")) {
  			clvpat = "'" + clvpat;
  		    }
  		    if (!clvpat.endsWith("}")) {
  			clvpat += "'";
  		    }
  		    if (!clvpat.equals("{value}")) {
  			clvpat = "concat(" + clvpat + ")";
  			nsubst = 2;
  		    }
  		}
  
  		// Special cases ...
  		boolean atcurr = ctx.state == BindingContext.CtxState.ATCURRENT;
  		if (is19139) {
  		    // 19139 encoding
  		    if (nsubst == 2 && atcurr) {
  			String v = xptobj.findOrAdd(xptobj.fragment);
  			xptobj.fragment = "$" + v;
  		    }
  		    String clvp = clvpat.replace("{codeList}", xptobj.fragment + "/@codeList");
  		    xptobj.fragment = clvp.replace("{value}", xptobj.fragment + "/@codeListValue");
  		} else if (!isgml33) {
  		    // In elder GMLs we might find the codespace in
  		    // the codespace attribute
  		    if (nsubst == 2 && atcurr) {
  			String v = xptobj.findOrAdd(xptobj.fragment);
  			xptobj.fragment = "$" + v;
  		    }
  		    String clvp = clvpat.replace("{codeList}", xptobj.fragment + "/@codeSpace");
  