Package com.jclark.xsl.tr

Source Code of com.jclark.xsl.tr.SheetImpl$KeyParser

// $Id: SheetImpl.java 113 2005-03-28 21:52:49Z blindsey $

package com.jclark.xsl.tr;

import com.jclark.xsl.om.*;

import com.jclark.xsl.expr.Pattern;
import com.jclark.xsl.expr.ExprParser;
import com.jclark.xsl.expr.StringExpr;
import com.jclark.xsl.expr.TopLevelPattern;
import com.jclark.xsl.expr.NodeSetExpr;
import com.jclark.xsl.expr.BooleanExpr;
import com.jclark.xsl.expr.VariantExpr;
import com.jclark.xsl.expr.Variant;
import com.jclark.xsl.expr.StringVariant;
import com.jclark.xsl.expr.NumberVariant;
import com.jclark.xsl.expr.VariableSet;
import com.jclark.xsl.expr.EmptyVariableSet;
import com.jclark.xsl.expr.ExtensionContext;

import com.jclark.xsl.util.Comparator;
import com.jclark.xsl.util.BilevelComparator;
import com.jclark.xsl.util.TextComparator;
import com.jclark.xsl.util.NumberComparator;
import com.jclark.xsl.util.ReverseComparator;

import com.jclark.xsl.sax.SaxFilterMaker;

import java.io.IOException;

import java.net.URL;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;
import java.util.StringTokenizer;
import java.util.Locale;

import java.text.Collator;

/**
* Actually does the work of compiling the stylesheet's object model
*  into the template rules, actions, variable definitions, etc.
*
*  The constructor compiles the stylesheet, the method "process()"
*  runs the transformation
*/
class SheetImpl
    implements SheetDetails, LoadContext, NamespaceConstants
{
    private SaxFilterMaker _xrap = null;
   
    private ActionDebugTarget _debugger = null;

    private ImportantBoolean stripSource = null;

    private Hashtable stripSourceElementTable = null;

    private Hashtable stripSourceNamespaceTable = null;

    private TemplateRuleSet templateRules =
        new TemplateRuleSet(new BuiltinAction());

    private Hashtable modeTable = new Hashtable();

    private Hashtable namedTemplateTable = new Hashtable();

    private Hashtable variableInfoTable = new Hashtable();

    private Hashtable userFunctionTable = new Hashtable();

    private Hashtable attributeSetTable = new Hashtable();

    private Hashtable namespaceAliasTable = new Hashtable();

    private Hashtable keysDefinitionsTable = new Hashtable();

    private Vector idAttributes = new Vector();

    private static final Action emptyAction = new EmptyAction();

    private Hashtable topLevelTable = new Hashtable();

    private Hashtable actionTable = new Hashtable();

    Importance currentImportance = Importance.create();

    int importCount = 0;

    static NodeSetExpr childrenExpr = ExprParser.getChildrenExpr();

    VariableSet currentLocalVariables = new EmptyVariableSet();

    int nCurrentLocalVariables = 0;

    Vector excludedNamespaces;

    XMLProcessor _omBuilder;

    static StringVariant emptyStringVariant = new StringVariant("");

    Name XSL_WHEN;
    Name XSL_OTHERWISE;
    Name XSL_STYLESHEET;
    Name XSL_TRANSFORM;
    Name XSL_WITH_PARAM;
    Name XSL_SORT;
    Name XSL_FOR_EACH;
    Name XSL_FALLBACK;
    Name XSL_VERSION;
    Name XSL_VENDOR;
    Name XSL_VENDOR_URL;
    Name XSL_USE_ATTRIBUTE_SETS;
    Name XSL_ATTRIBUTE;

    // stylesheet attribute names
    Name ATTRIBUTE;
    Name CASE_ORDER;
    Name CDATA_SECTION_ELEMENTS;
    Name COUNT;
    Name DATA_TYPE;
    Name DEFAULT;
    Name DISABLE_OUTPUT_ESCAPING;
    Name DOCTYPE_PUBLIC;
    Name DOCTYPE_SYSTEM;
    Name ELEMENTS;
    Name ENCODING;
    Name EXCLUDE_RESULT_PREFIXES;
    Name FORMAT;
    Name FROM;
    Name GROUPING_SEPARATOR;
    Name GROUPING_SIZE;
    Name HREF;
    Name INDENT;
    Name LANG;
    Name LETTER_VALUE;
    Name LEVEL;
    Name MATCH;
    Name MEDIA_TYPE;
    Name METHOD;
    Name MODE;
    Name NAME;
    Name NAMESPACE;
    Name OMIT_XML_DECLARATION;
    Name ORDER;
    Name PRIORITY;
    Name RESULT_PREFIX;
    Name SELECT;
    Name STANDALONE;
    Name STYLESHEET_PREFIX;
    Name TERMINATE;
    Name TEST;
    Name USE;
    Name USE_ATTRIBUTE_SETS;
    Name VALUE;
    Name VERSION;

    // output method properties from the stylesheet
    OutputMethodImpl _outputMethod;

    // output method properties with overrides from client code
    OutputMethodImpl _runtimeOutputMethod = null;

    LoadContext sheetLoadContext;
    NameTable nameTable;
    ExtensionHandler extensionHandler;
 
    /**
     * The constructor builds (compiles) a stylesheet
     * @param node the (xslt D) OM root Node of the loaded stylesheet
     * @param omBuilder a (xslt D) OM builder we can use for
     *               included/imported sheets
     * @param extensionHandler for extensions?
     * @param sheetLoadContext ??
     * @param nameTable ??
     */
    SheetImpl(Node node, XMLProcessor omBuilder,
              ExtensionHandler extensionHandler,
        LoadContext sheetLoadContext,
              NameTable nameTable)
  throws IOException, XSLException
    {
  this.sheetLoadContext = sheetLoadContext;
  this.nameTable = nameTable;
  this._omBuilder = omBuilder;
  this.extensionHandler = extensionHandler;
        _debugger =  sheetLoadContext.getDebugger();

  XSL_WHEN = xsl("when");
  XSL_OTHERWISE = xsl("otherwise");
  XSL_STYLESHEET = xsl("stylesheet");
  XSL_TRANSFORM = xsl("transform");
  XSL_WITH_PARAM = xsl("with-param");
  XSL_SORT = xsl("sort");
  XSL_FOR_EACH = xsl("for-each");
  XSL_FALLBACK = xsl("fallback");
  XSL_VERSION = xsl("version");
  XSL_VENDOR = xsl("vendor");
  XSL_VENDOR_URL = xsl("vendor-url");
  XSL_USE_ATTRIBUTE_SETS = xsl("use-attribute-sets");
  XSL_ATTRIBUTE = xsl("attribute");

        // no namespace for these attribute names
  ATTRIBUTE = nameTable.createName("attribute");
  CASE_ORDER = nameTable.createName("case-order");
  CDATA_SECTION_ELEMENTS = nameTable.createName("cdata-section-elements");
  COUNT = nameTable.createName("count");
  DATA_TYPE = nameTable.createName("data-type");
  DEFAULT = nameTable.createName("default");
  DISABLE_OUTPUT_ESCAPING =
            nameTable.createName("disable-output-escaping");
  DOCTYPE_PUBLIC = nameTable.createName("doctype-public");
  DOCTYPE_SYSTEM = nameTable.createName("doctype-system");
  ELEMENTS = nameTable.createName("elements");
  ENCODING = nameTable.createName("encoding");
  EXCLUDE_RESULT_PREFIXES =
            nameTable.createName("exclude-result-prefixes");
  FORMAT = nameTable.createName("format");
  FROM = nameTable.createName("from");
  GROUPING_SEPARATOR = nameTable.createName("grouping-separator");
  GROUPING_SIZE = nameTable.createName("grouping-size");
  HREF = nameTable.createName("href");
  INDENT = nameTable.createName("indent");
  LANG = nameTable.createName("lang");
  LETTER_VALUE = nameTable.createName("letter-value");
  LEVEL = nameTable.createName("level");
  MATCH = nameTable.createName("match");
  MEDIA_TYPE = nameTable.createName("media-type");
  METHOD = nameTable.createName("method");
  MODE = nameTable.createName("mode");
  NAME = nameTable.createName("name");
  NAMESPACE = nameTable.createName("namespace");
  OMIT_XML_DECLARATION = nameTable.createName("omit-xml-declaration");
  ORDER = nameTable.createName("order");
  PRIORITY = nameTable.createName("priority");
  RESULT_PREFIX = nameTable.createName("result-prefix");
  SELECT = nameTable.createName("select");
  STANDALONE = nameTable.createName("standalone");
  STYLESHEET_PREFIX = nameTable.createName("stylesheet-prefix");
  TERMINATE = nameTable.createName("terminate");
  TEST = nameTable.createName("test");
  USE = nameTable.createName("use");
  USE_ATTRIBUTE_SETS = nameTable.createName("use-attribute-sets");
  VALUE = nameTable.createName("value");
  VERSION = nameTable.createName("version");
   
  topLevelTable.put(xsl("include"), new IncludeParser());
  topLevelTable.put(xsl("import"), new ImportParser());
  topLevelTable.put(xsl("template"), new TemplateParser());
  topLevelTable.put(xsl("attribute-set"), new AttributeSetParser());
  topLevelTable.put(xsl("decimal-format"), new DecimalFormatParser());
  topLevelTable.put(xsl("key"), new KeyParser());
  topLevelTable.put(xsl("variable"), new VariableTopLevelParser());
  topLevelTable.put(xsl("param"), new ParamTopLevelParser());
  topLevelTable.put(xsl("strip-space"), new StripSpaceParser());
  topLevelTable.put(xsl("preserve-space"), new PreserveSpaceParser());
  topLevelTable.put(xsl("output"), new OutputParser());
  topLevelTable.put(xsl("namespace-alias"), new NamespaceAliasParser());
  topLevelTable.put(func("function"), new EXSLFunctionTopLevelParser());

  actionTable.put(xsl("text"), new TextParser());
  actionTable.put(xsl("apply-templates"), new ApplyTemplatesParser());
  actionTable.put(xsl("call-template"), new CallTemplateParser());
  actionTable.put(xsl("for-each"), new ForEachParser());
  actionTable.put(xsl("value-of"), new ValueOfParser());
  actionTable.put(xsl("number"), new NumberParser());
  actionTable.put(xsl("if"), new IfParser());
  actionTable.put(xsl("choose"), new ChooseParser());
  actionTable.put(xsl("copy"), new CopyParser());
  actionTable.put(xsl("copy-of"), new CopyOfParser());
  actionTable.put(xsl("comment"), new CommentParser());
  actionTable.put(xsl("processing-instruction"),
                        new ProcessingInstructionParser());
  actionTable.put(xsl("element"), new ElementParser());
  actionTable.put(xsl("attribute"), new AttributeParser());
  actionTable.put(xsl("apply-imports"), new ApplyImportsParser());
  actionTable.put(xsl("variable"), new VariableActionParser());
  actionTable.put(xsl("param"), new ParamActionParser());
  actionTable.put(xsl("message"), new MessageParser());

  actionTable.put(xt("document"), new DocumentParser());

        actionTable.put(exsl("document"), new DocumentParser());

        actionTable.put(func("result"), new EXSLFunctionResultParser());

  actionTable.put(xfyxt("xrap"), new XRAPParser());

        _outputMethod = new OutputMethodImpl();

        // ... and now, do something big,
        // compile from the OM into something we can execute

  parseSheet(node);

    }


    /**
     * sets the xrap processor for use during processing
     */
    public void setSaxExtensionFilter(SaxFilterMaker xrap)
    { _xrap = xrap; }


    /**
     * gets the xrap processor for use during processing
     */
    public SaxFilterMaker getSaxExtensionFilter()
    { return _xrap; }


    /**
     * sets the xrap processor for use during processing
     */
    public void setDebugger(ActionDebugTarget debugger)
    { _debugger = debugger; }


    /**
     * gets the xrap processor for use during processing
     */
    public ActionDebugTarget getDebugger()
    { return _debugger; }


    //////////////////////////////////////////////////////////
    //
    //
    //


    /**
     * returns the current values set either by client code,
     * the stylesheet, or defaulted for the output method properties
     */
    public OutputMethod getOutputMethod()
    {
        return _runtimeOutputMethod == null ? _outputMethod : _runtimeOutputMethod;
    }

    /**
     * returns the current values set either by client code,
     * the stylesheet, or defaulted for the output method properties
     * see java.xml.transform.Transform.getOutputMethodProperties()
     */
    public Properties getOutputMethodProperties()
    {
        // FIXME
        return null;
    }

    /**
     * resets the output method to just those properties which were
     * set in the stylesheet and/or defaults
     */
    public void clearOutputMethodProperties()
    {
        _runtimeOutputMethod = null;
    }

    /**
     * override the stylesheet's or default value for an
     * output method property
     */
    public void setOutputMethodProperty(String encodedPropertyNamespace,
                                        String encodedPropertyName, String value)
        throws XSLException
    {
        if (_runtimeOutputMethod == null) {
            _runtimeOutputMethod = (OutputMethodImpl) _outputMethod.clone();
        }
        Name name = (encodedPropertyNamespace == null) ?
            nameTable.createName(encodedPropertyName) :
            nameTable.createName("omp:" + encodedPropertyName,
                                 encodedPropertyNamespace);
       
        // null node for namespace prefix mapping
        _runtimeOutputMethod.setSpecifiedValue(name, value, null);    
    }

    /**
     * returns the current value of the named property
     */
    public String getOutputMethodProperty(String encodedPropertyNamespace,
                                          String encodedPropertyName)
    {
        OutputMethod om = ( _runtimeOutputMethod == null) ?
            _outputMethod :
            _runtimeOutputMethod;
       
        Name name = (encodedPropertyNamespace == null) ?
            nameTable.createName(encodedPropertyName) :
            nameTable.createName("omp:" + encodedPropertyName,
                                 encodedPropertyNamespace);

        return om.getSpecifiedValue(name);
    }
    //
    //
    //
    /////////////////////////////////////////////////////////

    /**
     * process an XML input document against this stylesheet
     * @param node the source document
     * @param omBuilder an object model builder we can (re-)use for e.g. "document()"
     * @param params the XSLT run-time parameters
     * @param root the destination for thetransformation results
     */
    public Result process(Node node, XMLProcessor omBuilder,
                          ParameterSet params, Result root)
        throws XSLException
    {
  root.start(_runtimeOutputMethod == null ?
                   _outputMethod :
                   _runtimeOutputMethod);

  ProcessContextImpl pci =
            new ProcessContextImpl(this, node, omBuilder, params);

        pci.processSafe(node, null, root);

  root.end();

  return root;
    }
   
    // parse the om representation of the stylesheet
    // in order to build a compiled sheet
    private void parseSheet(Node rootNode)
        throws XSLException, IOException
    {

        if (rootNode == null) {
            throw new XSLException("null document rootNode");
        }
        // get the root Element
  Node sheetNode = rootNode.getChildren().next()

        if (sheetNode == null) {
            throw new XSLException("no root element");
        }
       
        // make sure it's an XSLT stylesheet if it's in the XSL namespace
  if (XSL_NAMESPACE.equals(sheetNode.getName().getNamespace())) {
      if (!XSL_STYLESHEET.equals(sheetNode.getName())
    && !XSL_TRANSFORM.equals(sheetNode.getName()))
    throw new XSLException("bad document element for stylesheet",
               sheetNode);
           
            // now start our recursive descent
      parseTopLevel(sheetNode);

  } else {
            // the root element wasn't in the XSL namespace, so ...
      parseRootTemplate(rootNode);
        }
   currentImportance.set(importCount);

  templateRules.compile();

  for (Enumeration iter = modeTable.elements() ;
             iter.hasMoreElements() ; ) {
      ((TemplateRuleSet)iter.nextElement()).compile();
        }
    }

    //
    //
    //
    private Vector getExcludedNamespaces(Node node) throws XSLException
    {
  String prefixList = node.getAttributeValue(EXCLUDE_RESULT_PREFIXES);
  if (prefixList == null)
      return null;
  Vector v = new Vector();
  StringTokenizer iter = new StringTokenizer(prefixList);
  while (iter.hasMoreElements()) {
      String prefix = (String)iter.nextElement();
      if (prefix == null) {
    break;
            }
      v.addElement(getPrefixNamespace(node, prefix));
  }
  if (v.size() == 0) {
      return null;
        }
  return v;
    }

    /**
     * @return  the namespace URI reference for the given prefix
     *   in scope at the given Node
     */
    String getPrefixNamespace(Node node, String prefix) throws XSLException
    {
  NamespacePrefixMap map = node.getNamespacePrefixMap();
  String ns = map.getNamespace(prefix);
  if (ns == null) {
      if (prefix.equals("#default")) {
    ns = map.getDefaultNamespace();
            }
      if (ns == null) {
    throw new XSLException("undefined prefix", node);
            }
  }
  return ns;
    }

    /**
     * come here if we have a "xsl:stylesheet" root element
     * expect XSLT elements permitted at the top level ... params,
     * templates, keys, etc.
     */
    void parseTopLevel(Node sheetNode) throws XSLException, IOException
    {

        //        System.out.println("parseTopLevel:: {" + sheetNode.getName().getNamespace() + "}" +  sheetNode.getName().getLocalPart());


  Vector saveExcludedNamespaces = excludedNamespaces;
  excludedNamespaces = getExcludedNamespaces(sheetNode);

  // hmmm ... should we really silently ignore some elements?
  for (NodeIterator iter = sheetNode.getChildren();;) {
      Node node = iter.next();
      if (node == null) {
    break;
            }
      TopLevelParser parser = null;
      try {
    Name name = node.getName();

    if (name == null) {
        throw new XSLException("illegal data characters inside xsl:stylesheet", node);
                }
    parser = (TopLevelParser)topLevelTable.get(name);
    if (parser == null && name.getNamespace() == null) {
        throw new XSLException("illegal top-level element", node);
                }
      }
      catch (ClassCastException e) { }
      if (parser != null) {
    parser.parse(node);
            }
  }
  excludedNamespaces = saveExcludedNamespaces;
    }

    //
    // handles a stylesheet with no "xsl:stylesheet" or "xsl:transform"
    //
    private void parseRootTemplate(Node defNode) throws XSLException
    {

        //        System.out.println("parseRootTemplate:: {" + defNode.getName().getNamespace() + "}" +  defNode.getName().getLocalPart());

  templateRules.add(ExprParser.parsePattern(defNode, "/"),
        currentImportance,
        null,
        parseActions(defNode, emptyAction));
    }

    /**
     * Parse the attributes on node as literal attributes and then
     * parse the actions.
     */
    Action parseAttributesAndActions(Node node) throws XSLException
    {

        //        System.out.println("parseAttributesAndActions:: {" + node.getName().getNamespace() + "}" +  node.getName().getLocalPart());

  AppendAction sequence = parseUseAttributeSets(node, true, null);

  for (NodeIterator iter = node.getAttributes();;) {
      Node att = iter.next();
      if (att == null) {
    break;
            }
      if (sequence == null) {
    if (_debugger != null) {
                    sequence = new AppendActionDebug(_debugger, node, "");
                } else {
                    sequence = new AppendAction();
                }
            }
      String value = att.getData();
      Name name = att.getName();

      if (XSL_NAMESPACE.equals(name.getNamespace())) {
    continue; // FIXME error checking
            }

      if (value.indexOf('{') >= 0 || value.indexOf('}') >= 0) {
    sequence.add(new TemplateAttributeAction(name,
               ExprParser.parseValueExpr(node, value, currentLocalVariables)));
            } else {
    sequence.add(new LiteralAttributeAction(name, value));
            }
        }
  return parseActions(node, null, sequence);
    }

    /**
     *
     */
    Action parseUseAttributeSetsAndActions(Node node) throws XSLException
    {
  return parseActions(node,
          emptyAction,
          parseUseAttributeSets(node, false, null));
    }

    /**
     *
     */
    AppendAction parseUseAttributeSets(Node node, boolean literal,
               AppendAction sequence) throws XSLException
    {
  String value = node.getAttributeValue(literal
                ? XSL_USE_ATTRIBUTE_SETS
                : USE_ATTRIBUTE_SETS);
  if (value != null) {
      for (StringTokenizer iter = new StringTokenizer(value);
     iter.hasMoreElements() ) {

    if (sequence == null) {
        sequence = new AppendAction();
                }
    sequence.add(new UseAttributeSetAction(expandSourceElementTypeName((String)iter.nextElement(),node)));
      }
  }
  return sequence;
    }

    /**
     *
     */
    Action parseActions(Node node, Action ifEmpty) throws XSLException
    {
        //        System.out.println("parseActions1:: {" + node.getName().getNamespace() + "}" +  node.getName().getLocalPart());
        return parseActions(node, ifEmpty, null);
    }
 
    /**
     *
     */
    Action parseActions(Node node, Action ifEmpty,
                        AppendAction sequence) throws XSLException
    {
        //        System.out.println("parseActions2:: {" + node.getName().getNamespace() + "}" +  node.getName().getLocalPart());

  final VariableSet startLocalVariables = currentLocalVariables;
  final int nStartLocalVariables = nCurrentLocalVariables;
  NodeIterator iter = node.getChildren();
  node = iter.next();
  if (node == null) {
      if (sequence == null) {
    return ifEmpty;
      } else {
    return sequence;
            }
  }
  if (sequence == null) {
      if (_debugger != null) {
                sequence = new AppendActionDebug(_debugger, node, "");
            } else {
                sequence = new AppendAction();
            }
        }
  do {
      switch (node.getType()) {
      case Node.TEXT:
    if (_debugger != null) {
                    sequence.add(new CharsActionDebug(_debugger, node, "",
                                                      node.getData()));
                } else {
                    sequence.add(new CharsAction(node.getData()));
                }
    break;
      case Node.ELEMENT:
    {
        Name name = node.getName();
        String ns = name.getNamespace();
        if (!XSL_NAMESPACE.equals(ns) &&
                        !XT_NAMESPACE.equals(ns) &&
                        !XFYXT_NAMESPACE.equals(ns)) {
      if (_debugger != null) {
                            sequence.add(new LiteralElementActionDebug(_debugger,
                                                                       node, "",
                                                                       node.getName(),
                                                                       literalNamespacePrefixMap(node),
                                                                       parseAttributesAndActions(node)));
                        } else {
                            sequence.add(new LiteralElementAction(node.getName(),
                                                                  literalNamespacePrefixMap(node),
                                                                  parseAttributesAndActions(node)));
                        }
        } else {
      ActionParser actionParser = null;
      try {
          actionParser = (ActionParser)actionTable.get(name);
      } catch (ClassCastException e) {

                        }
      if (actionParser == null) {
          if (name.equals(XSL_SORT)
        && XSL_FOR_EACH.equals(node.getParent().getName()))
        ;
          else if (name.equals(XSL_FALLBACK))
        ; // FIXME error checking
          else
        throw new XSLException("expected action not " + name, node);
      } else {
          sequence.add(actionParser.parse(node));
                        }
        }
    }
      }
      node = iter.next();
  } while (node != null);

  // FIXME: should use finally here
  if (nStartLocalVariables != nCurrentLocalVariables) {
      sequence.add(new UnbindLocalVariablesAction(nCurrentLocalVariables -
                                                        nStartLocalVariables));
      nCurrentLocalVariables = nStartLocalVariables;
      currentLocalVariables = startLocalVariables;
  }
  return sequence;
    }

    /**
     *
     */
    String getRequiredAttribute(Node node, Name name)
        throws XSLException
    {
  String value = node.getAttributeValue(name);
  if (value == null) {
      throw new XSLException("missing attribute \"" +
                                   name + "\"", node);
        }
  return value;
    }

    /**
     *
     */
    String getOptionalAttribute(Node node, Name name, String dflt)
    {
  String value = node.getAttributeValue(name);
  return value == null ? dflt : value;
    }

    /**
     *
     */
    String getData(Node node) throws XSLException
    {
  node = node.getChildren().next();
  if (node == null) {
      return "";
        }
  String data = node.getData();
  if (data == null) {
      throw new XSLException("only character data allowed", node);
        }
  return data;
    }

    /**
     *
     */
    NumberListFormatTemplate getNumberListFormatTemplate(Node node)
        throws XSLException
    {
  NumberListFormatTemplate t = new NumberListFormatTemplate();

  String value = node.getAttributeValue(FORMAT);
  if (value != null) {
      StringExpr expr =
                ExprParser.parseValueExpr(node, value,
                                          currentLocalVariables);
      String format = expr.constantValue();
      if (format != null) {
    t.setFormat(format);
      } else {
    t.setFormat(expr);
            }
        }

  // FIXME should be able to use attribute value templates for these
  value = node.getAttributeValue(LANG);
  if (value != null)
      t.setLang(value);
  value = node.getAttributeValue(LETTER_VALUE);
  if (value != null)
      t.setLetterValue(value);
  value = node.getAttributeValue(GROUPING_SIZE);
  if (value != null) {
      try {
    t.setGroupingSize(Integer.parseInt(value));
      }
      catch (NumberFormatException e) { }
  }
  value = node.getAttributeValue(GROUPING_SEPARATOR);
  if (value != null)
      t.setGroupingSeparator(value);
  return t;
    }

    /**
     *
     */
    Action addParams(ParamAction action, Node node)
        throws XSLException
    {
  for (NodeIterator iter = node.getChildren() ; ; ) {
      node = iter.next();
      if (node == null) {
    break;
            }
      if (XSL_WITH_PARAM.equals(node.getName()))
    action.addParam(expandSourceElementTypeName(getRequiredAttribute(node, NAME),
                                                            node),
        getVariantExpr(node));
  }
  return action;
    }

    NodeSetExpr getSortNodeSetExpr(Node node,
                                   NodeSetExpr expr) throws XSLException
    {
  ComparatorTemplate comparatorTemplate = null;
  for (NodeIterator iter = node.getChildren();;) {
      node = iter.next();
      if (node == null) {
    break;
            }
      if (XSL_SORT.equals(node.getName())) {
    Locale locale = Lang.getLocale(node.getAttributeValue(LANG));
    Comparator cmp;
    String dataType = node.getAttributeValue(DATA_TYPE);
    if ("number".equals(dataType)) {
        cmp = new NumberComparator();
    } else {
        int caseOrder = 0;
        String caseOrderString =
                        node.getAttributeValue(CASE_ORDER);

        if ("upper-first".equals(caseOrderString)) {
      caseOrder = TextComparator.UPPER_FIRST;
        } else if ("lower-first".equals(caseOrderString)) {
      caseOrder = TextComparator.LOWER_FIRST;
                    }
        cmp = TextComparator.create(locale, caseOrder);
    }
    if ("descending".equals(node.getAttributeValue(ORDER)))
        cmp = new ReverseComparator(cmp);
    ComparatorTemplate templ
        = new NodeComparatorTemplate(cmp,
             ExprParser.parseStringExpr(node,
                      getOptionalAttribute(node, SELECT, "."), currentLocalVariables));
    if (comparatorTemplate == null)
        comparatorTemplate = templ;
    else
        comparatorTemplate = new BilevelComparatorTemplate(comparatorTemplate, templ);
      }
  }
  if (comparatorTemplate == null)
      return expr;
  return new SortNodeSetExpr(expr, comparatorTemplate);
    }

    /**
     * gets the value (an expression to be evaluated later) to be bound for a variable or parameter
     */
    VariantExpr getVariantExpr(Node defNode) throws XSLException
    {
  String expr = defNode.getAttributeValue(SELECT);

  if (defNode.getChildren().next() == null) {
      if (expr == null) {
    expr = "\"\"";
            }
  } else {
      if (expr != null) {
    throw new XSLException("non-empty content with select attribute", defNode);
            }

      // OPT optimize case when there's a single text node child;
      // optimize case when the children consist of text nodes
      // and xsl:value-of elements
      return new ResultFragmentExpr(parseActions(defNode, emptyAction),
            defNode,
            extensionHandler);
  }
  return ExprParser.parseVariantExpr(defNode, expr,
                                           currentLocalVariables);
    }

    /**
     * obtain the collection of templates which may be
     * applied in a named Mode
     */
    public TemplateRuleSet getModeTemplateRuleSet(Name modeName)
    {
  if (modeName == null) {
            // default, unnamed mode
      return templateRules;
        }
  TemplateRuleSet ruleSet = (TemplateRuleSet)modeTable.get(modeName);
  if (ruleSet == null) {
      ruleSet = new TemplateRuleSet(new BuiltinAction(modeName));
      modeTable.put(modeName, ruleSet);
  }
  return ruleSet;
    }

    /**
     * obtain the definition of the named key
     */
    public KeyDefinition getKeyDefinition(Name keyName)
    {
  return (KeyDefinition) keysDefinitionsTable.get(keyName);
    }

    // shortcut for creating names in the xsl namespace
    private Name xsl(String name)
    {
  return nameTable.createName("xsl:" + name, XSL_NAMESPACE);
    }
 
    // shortcut for creating names in the exslt namespace
    private Name exsl(String name)
    {
  return nameTable.createName("exsl:" + name, EXSL_COMMON_NAMESPACE);
    }

    // shortcut for creating names in the exslt function (http://exslt.org/functions) namespace
    private Name func(String name)
    {
  return nameTable.createName("func:" + name, EXSL_FUNCTIONS_NAMESPACE);
    }

    private Name regexp(String name)
    {
  return nameTable.createName("regexp:" + name, EXSL_REGEXP_NAMESPACE);
    }

    // shortcut for creating names in the xt namespace
    private Name xt(String name)
    {
  return nameTable.createName("xt:" + name, XT_NAMESPACE);
    }

    // shortcut for creating names in the xfyxt namespace
    private Name xfyxt(String name)
    {
  return nameTable.createName("xfyxt:" + name, XFYXT_NAMESPACE);
    }


    private boolean namespaceExcluded(String ns)
    {
  if (ns == null) {
      return false;
        }
  if (ns.equals(XSL_NAMESPACE) || ns.equals(XT_NAMESPACE) ||
            ns.equals(XFYXT_NAMESPACE)) {
      return true;
        }
  if (excludedNamespaces == null) {
      return false;
        }
  int len = excludedNamespaces.size();
  for (int i = 0; i < len; i++) {
      if (excludedNamespaces.elementAt(i).equals(ns)) {
    return true;
            }
        }
        return false;
    }

    //
    private NamespacePrefixMap literalNamespacePrefixMap(Node node)
    {
  NamespacePrefixMap map = node.getNamespacePrefixMap();
  NamespacePrefixMap newMap = map;
  String ns = map.getDefaultNamespace();
  if (namespaceExcluded(ns)) {
      newMap = newMap.unbindDefault();
        }
  int size = map.getSize();

  for (int i = 0; i < size; i++) {
      ns = map.getNamespace(i);
      if (namespaceExcluded(ns)) {
    newMap = newMap.unbind(map.getPrefix(i));
            }
  }
  return newMap;
    }

    //
    static Name expandSourceElementTypeName(String nameString,
                                            Node node)
        throws XSLException
    {
        // WDL Debug
        if (node == null) {
            throw new XSLException("no node for prefix map");
        }
        NamespacePrefixMap pm = node.getNamespacePrefixMap();
        if (pm == null) {
            throw new XSLException("no prefix map");
        }
        Name n = pm.expandAttributeName(nameString, node);
        if (n == null) {
            throw new XSLException("no name for {" + nameString + "}");
        }
   
  return node.getNamespacePrefixMap().expandAttributeName(nameString,
                                                                node);
    }

    /**
     *
     */
    public LoadContext getSourceLoadContext()
    {
  return this;
    }

    /**
     *
     */
    public boolean getStripSource(Name elementTypeName)
    {
  ImportantBoolean match = stripSource;
  if (stripSourceNamespaceTable != null) {
      String ns = elementTypeName.getNamespace();
      if (ns != null) {
    ImportantBoolean ib =
                    (ImportantBoolean)stripSourceNamespaceTable.get(ns);
    if (ib != null &&
                    (match == null ||
                     ib.getImportance().compareTo(match.getImportance()) >= 0)){
        match = ib;
                }
      }
  }
  if (stripSourceElementTable != null) {
      ImportantBoolean ib =
                (ImportantBoolean)stripSourceElementTable.get(elementTypeName);
      if (ib != null &&
                (match == null ||
                 ib.getImportance().compareTo(match.getImportance()) >= 0)) {
    match = ib;
            }
  }
  return match != null ? match.getBoolean() : false;
    }

    /**
     * @return true
     */
    public boolean getIncludeComments()
    {
  return true;
    }

    /**
     * @return true
     */
    public boolean getIncludeProcessingInstructions()
    {
  return true;
    }

    /**
     *
     */
    public VariableInfo getGlobalVariableInfo(Name name)
    {
  return (VariableInfo)variableInfoTable.get(name);
    }

    /**
     *
     */
    public Variant getSystemProperty(Name name)
    {
        // FIXME: maybe I ought to change this
  if (name.equals(XSL_VERSION)) {
      return new NumberVariant(1.0);
        }
  if (name.equals(XSL_VENDOR)) {
      return new StringVariant("James Clark");
        }
  if (name.equals(XSL_VENDOR_URL)) {
      return new StringVariant("http://www.jclark.com/");
        }
  return emptyStringVariant;
    }

    /**
     *
     */
    public ExtensionContext createExtensionContext(String namespace)
        throws XSLException
    {
  return extensionHandler.createContext(namespace);
    }

    /**
     *
     */
    public Action getAttributeSet(Name name)
    {
  return (Action)attributeSetTable.get(name);
    }

    /**
     *
     */
    public boolean haveNamespaceAliases()
    {
  return namespaceAliasTable.size() != 0;
    }
 
    /**
     *
     */
    public String getNamespaceAlias(String ns)
    {
  if (ns == null) {
      return null;
  }
  return (String)namespaceAliasTable.get(ns);
    }

    /////////////////////////////////////////////////////////////
    //
    // internal class definitions
    //
    //
    //////////////////////////////////////////////////////////

    //
    // for parsing XSLT stylesheet top-level elements
    //
    private interface TopLevelParser
    {
        void parse(Node node) throws XSLException, IOException;
    }

    //
    // hanldles an xslt "include" (and "import")
    //
    private class IncludeParser implements TopLevelParser
    {
        public void parse(Node ruleNode) throws XSLException, IOException
        {

            Node sheetNode =
                _omBuilder.load(new URL(ruleNode.getURL(),
                                        getRequiredAttribute(ruleNode,
                                                             HREF)),
                                0,
                                sheetLoadContext,
                                nameTable).getChildren().next();
           
            if (XSL_NAMESPACE.equals(sheetNode.getName().getNamespace())) {
                if (!XSL_STYLESHEET.equals(sheetNode.getName())
                    && !XSL_TRANSFORM.equals(sheetNode.getName()))
                    throw new XSLException("bad document element for stylesheet",
                                           sheetNode);
                parseTopLevel(sheetNode);
            }
            else {
                parseRootTemplate(sheetNode);
            }
        }
    }

    // parses "xsl:import"
    private class ImportParser extends IncludeParser
    {
        public void parse(Node ruleNode) throws XSLException, IOException
        {
            //    System.out.println("importParser:parse(): {" + ruleNode.getName().getNamespace() + "}" +  ruleNode.getName().getLocalPart());

            // HST: simplified and fixed the way import precedence is recorded
            //Importance zero = Importance.create();
            Importance oldCurrentImportance = currentImportance;
       currentImportance = Importance.create();

       //System.err.println("< xsl:import: "+currentImportance.compareTo(zero));
            super.parse(ruleNode);
       currentImportance.set(importCount);

       //System.err.println("> xsl:import: "+currentImportance.compareTo(zero));
       importCount++;
            currentImportance = oldCurrentImportance;
        }
    }
   
    // parses "xsl:template"
    private class TemplateParser implements TopLevelParser
    {
        public void parse(Node defNode) throws XSLException
        {

            //            System.out.println("TemplateParser:parse(): {" + defNode.getName().getNamespace() + "}" +  defNode.getName().getLocalPart());
            String name = defNode.getAttributeValue(NAME);
            Action contents = parseActions(defNode, emptyAction);

            if (name != null) {
                namedTemplateTable.put(expandSourceElementTypeName(name,
                                                                   defNode),
                                       contents);
            }

            String pattern = defNode.getAttributeValue(MATCH);
            if (pattern == null) {

                // no match pattern, presumably only reached
                // through xsl:call-template
                return;
            }

            String modeString = defNode.getAttributeValue(MODE);

            TemplateRuleSet ruleSet;  // based upon mode

            if (modeString != null) {
                ruleSet =
                    getModeTemplateRuleSet(expandSourceElementTypeName(modeString,
                                                                       defNode));
            } else {
                ruleSet = templateRules;
            }
            try {
                ruleSet.add(ExprParser.parsePattern(defNode, pattern),
                            currentImportance,
                            Priority.create(defNode.getAttributeValue(PRIORITY)),
                            contents);
            }
            catch (NumberFormatException e) {
                throw new XSLException("invalid priority", defNode);
            }
        }
    }
   
    // parses "xsl:attribute-set"
    private class AttributeSetParser implements TopLevelParser
    {
        public void parse(Node defNode) throws XSLException
        {
            Name name =
                expandSourceElementTypeName(getRequiredAttribute(defNode,
                                                                 NAME),
                                            defNode);

            AppendAction action = (AppendAction)attributeSetTable.get(name);
            if (action == null) {
                action = new AppendAction();
                attributeSetTable.put(name, action);
            }
            parseUseAttributeSets(defNode, false, action);
            for (SafeNodeIterator iter = defNode.getChildren();;) {
                Node node = iter.next();
                if (node == null) {
                    break;
                }
                if (!XSL_ATTRIBUTE.equals(node.getName()))
                    throw new XSLException("only xsl:attribute allowed inside xsl:attribute-set", node);
            }
            parseActions(defNode, null, action);
        }
    }
   
    // parses "xsl:key"
    private class KeyParser implements TopLevelParser
    {
        public void parse(Node defNode) throws XSLException
        {

            String pattern = getRequiredAttribute(defNode, MATCH);
      TopLevelPattern matchPattern = ExprParser.parsePattern(defNode, pattern);

            String name = getRequiredAttribute(defNode, NAME);
            Name expname = expandSourceElementTypeName(name, defNode);

      String use = getRequiredAttribute(defNode, USE);

      // FIXME: -- need to check enforcment of no variable refs constraint?
      StringExpr useExpr =
    ExprParser.parseStringExpr(defNode, use, currentLocalVariables);

      KeyDefinition kd = new KeyDefinition(expname, matchPattern, useExpr);
      keysDefinitionsTable.put(expname, kd);
        }
    }
   
    // parses "xsl:decimal-format"
    private class DecimalFormatParser implements TopLevelParser
    {
        public void parse(Node defNode) throws XSLException
        {
            // FIXME
        }
    }
   
    // parses "xsl:variable" top level elements
    private class VariableTopLevelParser implements TopLevelParser
    {
        public void parse(Node defNode) throws XSLException
        {
            //            System.out.println("VariableTopLevelParser:parse1(): {" + defNode.getName().getNamespace() + "}" +  defNode.getName().getLocalPart());
            parse(defNode, false);
        }
       
        void parse(Node defNode, boolean isParam) throws XSLException
        {

            //            System.out.println("VariableTopLevelParser:parse2(): {" + defNode.getName().getNamespace() + "}" +  defNode.getName().getLocalPart());
            variableInfoTable
                .put(expandSourceElementTypeName(getRequiredAttribute(defNode,
                                                                      NAME),
                                                 defNode),
                     new VariableInfo(getVariantExpr(defNode),
                                      isParam));
        }
    }
   
    // parses "xsl:param" top level element
    private class ParamTopLevelParser extends VariableTopLevelParser
    {
        public void parse(Node defNode) throws XSLException
        {
            parse(defNode, true);
        }
    }
   
    // parses EXSLT "func:function" top level elements
    private class EXSLFunctionTopLevelParser implements TopLevelParser
    {
        public void parse(Node defNode) throws XSLException
        {
            parse(defNode, false);
        }
       
        void parse(Node defNode, boolean isParam) throws XSLException
        {
            userFunctionTable
                .put(expandSourceElementTypeName(getRequiredAttribute(defNode,
                                                                      NAME),
                                                 defNode),
                     // FIXME what kinda object:
                     new VariableInfo(getVariantExpr(defNode),
                                      isParam));
        }
    }
   

    // parses "xsl:strip-space" top level element
    private class StripSpaceParser implements TopLevelParser
    {
        public void parse(Node node) throws XSLException
        {
            parse(node, true);
        }
       
        void parse(Node node, boolean strip) throws XSLException
        {
            StringTokenizer iter
                = new StringTokenizer(getRequiredAttribute(node, ELEMENTS));
            ImportantBoolean ib = new ImportantBoolean(strip, currentImportance);
            while (iter.hasMoreElements()) {
                String str = (String)iter.nextElement();
                Name name = expandSourceElementTypeName(str, node);

                if (name.getLocalPart().equals("*")) {
                    String ns = name.getNamespace();
                    if (ns == null)
                        stripSource = ib;
                    else {
                        if (stripSourceNamespaceTable == null)
                            stripSourceNamespaceTable = new Hashtable();
                        stripSourceNamespaceTable.put(ns, ib);
                    }
                }
                else {
                    if (stripSourceElementTable == null)
                        stripSourceElementTable = new Hashtable();
                    stripSourceElementTable.put(name, ib);
                }
            }
        }
    }
   
    // parses "xsl:preserve-space" top level element
    private class PreserveSpaceParser extends StripSpaceParser
    {
        public void parse(Node node) throws XSLException
        {
            parse(node, false);
        }
    }
   
    // parses "xsl:output" top level element
    private class OutputParser implements TopLevelParser
    {
        public void parse(Node node) throws XSLException
        {
            _outputMethod.merge(node);
        }
    }
   
    // parses xsl:namespace-alias top level element
    private class NamespaceAliasParser implements TopLevelParser
    {
        public void parse(Node node) throws XSLException
        {
            namespaceAliasTable.
                put(getPrefixNamespace(node,
                                       getRequiredAttribute(node,
                                                            STYLESHEET_PREFIX)),
                    getPrefixNamespace(node,
                                       getRequiredAttribute(node,
                                                            RESULT_PREFIX)));
        }
    }
   
    ////////////////////////////////////////////////////////////

    //
    // interface for parsing template actions
    //
    private interface ActionParser
    {
        Action parse(Node node) throws XSLException;
    }

    // parses "xsl:apply-templates"   
    private class ApplyTemplatesParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            //            System.out.println("ApplyTemplatesParser:parse1(): {" + node.getName().getNamespace() + "}" +  node.getName().getLocalPart());

            NodeSetExpr expr = getSortNodeSetExpr(node, getNodeSetExpr(node));
            String modeString = node.getAttributeValue(MODE);
            Name modeName = null;
            if (modeString != null)
                modeName = expandSourceElementTypeName(modeString, node);
            if (_debugger != null) {
                return addParams(new ProcessActionDebug(_debugger, node,
                                                        "", expr, modeName),
                                 node);
            } else {
                return addParams(new ProcessAction(expr, modeName), node);
            }
        }
       
        //
        // return the parsed XPath expression from the "select" attribute
        //
        NodeSetExpr getNodeSetExpr(Node node) throws XSLException
        {
            String select = node.getAttributeValue(SELECT);
            if (select == null) {
                return childrenExpr;
            }
            return ExprParser.parseNodeSetExpr(node, select,
                                               currentLocalVariables);
        }
       
    }
   
    // parses "xsl:for-each" template action element
    private class ForEachParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            if (_debugger != null) {
                return new ForEachActionDebug(_debugger, node, "",
                                              getSortNodeSetExpr(node, ExprParser
                                                                 .parseNodeSetExpr(node,
                                                                                   getRequiredAttribute(node, SELECT),
                                                                                   currentLocalVariables)),
                                              parseActions(node, emptyAction));
            } else {
                return new ForEachAction(getSortNodeSetExpr(node, ExprParser
                                                            .parseNodeSetExpr(node,
                                                                              getRequiredAttribute(node, SELECT),
                                                                              currentLocalVariables)),
                                         parseActions(node, emptyAction));
            }
        }
    }

    // parses "xsl:if" template action element
    //
    //  <!-- Category: instruction -->
    //     <xsl:if
    //       test = boolean-expression>
    //       <!-- Content: template -->
    //     </xsl:if>
    //
    private class IfParser implements ActionParser
    {
        // construct an IfAction
        public Action parse(Node node) throws XSLException
        {
            if (_debugger != null) {
                return new IfActionDebug(_debugger, node, "",
                                         makeCondition(node),
                                         parseActions(node, emptyAction),
                                         emptyAction);
            } else {
                return new IfAction(makeCondition(node),
                                    parseActions(node, emptyAction),
                                    emptyAction);
            }
        }
       
        // parse the XPath expression in the "test" attribute
        BooleanExpr makeCondition(Node node) throws XSLException
        {
            return ExprParser.parseBooleanExpr(node,
                                               getRequiredAttribute(node, TEST),
                                               currentLocalVariables);
        }
    }
 
    // <!-- Category: instruction -->
    //     <xsl:copy
    //       use-attribute-sets = qnames>
    //       <!-- Content: template -->
    //     </xsl:copy>
    //
    private class CopyParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            if (_debugger != null) {
                return new CopyActionDebug(_debugger, node, "",
                                           parseUseAttributeSetsAndActions(node));
            } else {
                return new CopyAction(parseUseAttributeSetsAndActions(node));
            }
        }
    }
   
    //  <!-- Category: instruction -->
    //     <xsl:copy-of
    //        select = expression />
    //
    private class CopyOfParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            if (_debugger != null) {

                return new CopyOfActionDebug(_debugger, node, "",
                                             ExprParser.
                                             parseVariantExpr(node,
                                                              getRequiredAttribute(node,
                                                                                   SELECT),
                                                              currentLocalVariables));
               
            } else {
                return new CopyOfAction(ExprParser.
                                        parseVariantExpr(node,
                                                         getRequiredAttribute(node,
                                                                              SELECT),
                                                         currentLocalVariables));
            }
        }
    }

    // <!-- Category: instruction -->
    //    <xsl:comment>
    //       <!-- Content: template -->
    //    </xsl:comment>
    //
    private class CommentParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            if (_debugger != null) {
                return new CommentActionDebug(_debugger, node, "",
                                              parseActions(node, emptyAction));
            } else {
                return new CommentAction(parseActions(node, emptyAction));
            }
        }
    }
   
    // <!-- Category: instruction -->
    //     <xsl:processing-instruction
    //       name = { ncname }>
    //       <!-- Content: template -->
    //     </xsl:processing-instruction>
    //
    private class ProcessingInstructionParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            if (_debugger != null) {
                return new
                    ProcessingInstructionActionDebug(_debugger, node, "",
                                                     ExprParser.
                                                     parseValueExpr(node,
                                                                    getRequiredAttribute(node, NAME),
                                                                    currentLocalVariables),
                                                     parseActions(node, emptyAction));
               
            } else {
                return new
                    ProcessingInstructionAction(ExprParser.
                                                parseValueExpr(node,
                                                               getRequiredAttribute(node, NAME),
                                                               currentLocalVariables),
                                                parseActions(node, emptyAction));
            }
        }
    }
   
    // <!-- Category: instruction -->
    //     <xsl:element
    //       name = { qname }
    //       namespace = { uri-reference }
    //       use-attribute-sets = qnames>
    //       <!-- Content: template -->
    //     </xsl:element>
    //
    private class ElementParser implements ActionParser
    {
        /**
         *
         */
        public Action parse(Node node) throws XSLException
        {
            if (_debugger != null) {
                return new ElementActionDebug(_debugger, node, "",
                                              ExprParser.
                                              parseValueExpr(node,
                                                             getRequiredAttribute(node,
                                                                                  NAME),
                                                             currentLocalVariables),
                                              getNamespaceExpr(node),
                                              node.getNamespacePrefixMap(),
                                              parseUseAttributeSetsAndActions(node));
            } else {
                return new ElementAction(ExprParser.
                                         parseValueExpr(node,
                                                        getRequiredAttribute(node,
                                                                             NAME),
                                                        currentLocalVariables),
                                         getNamespaceExpr(node),
                                         node.getNamespacePrefixMap(),
                                         parseUseAttributeSetsAndActions(node));
            }
        }
          
        /**
         *
         */
        StringExpr getNamespaceExpr(Node node) throws XSLException
        {
            String namespace = node.getAttributeValue(NAMESPACE);
            if (namespace == null) {
                return null;
            }
            return ExprParser.parseValueExpr(node,
                                             namespace,
                                             currentLocalVariables);
        }
    }

    // <!-- Category: instruction -->
    //     <xsl:attribute
    //       name = { qname }
    //       namespace = { uri-reference }>
    //       <!-- Content: template -->
    //     </xsl:attribute>
    //
    private class AttributeParser extends ElementParser
    {
        /**
         *
         */
        public Action parse(Node node) throws XSLException
        {
            if (_debugger != null) {
                return new AttributeActionDebug(_debugger, node, "",
                                                ExprParser.
                                                parseValueExpr(node,
                                                               getRequiredAttribute(node,
                                                                                    NAME),
                                                               currentLocalVariables),
                                                getNamespaceExpr(node),
                                                node.getNamespacePrefixMap(),
                                                parseActions(node, emptyAction));
            } else {
                return new AttributeAction(ExprParser.
                                           parseValueExpr(node,
                                                          getRequiredAttribute(node,
                                                                               NAME),
                                                          currentLocalVariables),
                                           getNamespaceExpr(node),
                                           node.getNamespacePrefixMap(),
                                           parseActions(node, emptyAction));
            }
        }
    }

    // xt:document  extension element
    private class DocumentParser implements ActionParser
    {
        /**
         *
         */
        public Action parse(Node node) throws XSLException
        {
            return new DocumentAction(ExprParser.
                                      parseValueExpr(node,
                                                     getRequiredAttribute(node,
                                                                          HREF),
                                                     currentLocalVariables),
                                      _outputMethod.mergeCopy(node),
                                      parseActions(node, emptyAction));
        }
    }


    // xt xrap extension element
    // <xfy:xrap>
    //  <!-- Content -->
    // </xfy:xrap />   
    private class XRAPParser implements ActionParser
    {
        /**
         *
         */
        public Action parse(Node node) throws XSLException
        {
            if (_debugger != null) {
                return new XRAPActionDebug(_debugger, node, "",
                                           node.getNamespacePrefixMap(),
                                           parseActions(node, emptyAction));
            } else {
                return new XRAPAction( node.getNamespacePrefixMap(),
                                       parseActions(node, emptyAction));
            }
        }
    }
   
    //
    //  <!-- Category: instruction -->
    //     <xsl:call-template
    //       name = qname>
    //       <!-- Content: xsl:with-param* -->
    //     </xsl:call-template>
    //
    private class CallTemplateParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {

            InvokeAction callAction;
            if (_debugger != null) {
                callAction =
                    new InvokeActionDebug(_debugger, node, "",
                                          expandSourceElementTypeName(getRequiredAttribute(node, NAME),
                                                                      node),
                                          namedTemplateTable);
               
            } else {
                callAction =
                    new InvokeAction(expandSourceElementTypeName(getRequiredAttribute(node, NAME),
                                                                 node),
                                     namedTemplateTable);
            }
           
//             return addParams(new InvokeAction(expandSourceElementTypeName(getRequiredAttribute(node, NAME), node),
//                                               namedTemplateTable),
//                              node);
           
            return addParams(callAction,
                             node);
           
           
        }
    }

   
    // <!-- Category: instruction -->
    //     <xsl:choose>
    //       <!-- Content: (xsl:when+, xsl:otherwise?) -->
    //     </xsl:choose>
    //
    private class ChooseParser extends IfParser
    {
        public Action parse(Node node)
            throws XSLException
        {
            return parseChoices(node.getChildren());
        }

        public Action parseChoices(NodeIterator iter)
            throws XSLException
        {
            Node node = iter.next();
            if (node == null) {
                return emptyAction;
            }
            Name name = node.getName();
            if (XSL_WHEN.equals(name)) {
                if (_debugger != null) {
                    return new IfActionDebug(_debugger, node, "",
                                             makeCondition(node),
                                             parseActions(node, emptyAction),
                                             parseChoices(iter));
                   
                } else {
                   
                    return new IfAction(makeCondition(node),
                                        parseActions(node, emptyAction),
                                        parseChoices(iter));
                }
            }
            if (XSL_OTHERWISE.equals(name)) {
                Node next = iter.next();
                if (next != null)
                    throw new XSLException("unexpected element after otherwise",
                                           next);
                return parseActions(node, emptyAction);
            }
            throw new XSLException("expected when or otherwise", node);
        }
    }
   
    // <!-- Category: instruction -->
    //     <xsl:text
    //       disable-output-escaping = "yes" | "no">
    //       <!-- Content: #PCDATA -->
    //     </xsl:text>
    //
    private class TextParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            Node child = node.getChildren().next();
            if (child != null) {
                String data = child.getData();
                if (data == null ||
                    child.getFollowingSiblings().next() != null)
                    throw new XSLException("xsl:text must not contain elements",
                                           node);
                if ("yes".equals(node.getAttributeValue(DISABLE_OUTPUT_ESCAPING))) {
                    if (_debugger != null) {
                        return new RawCharsActionDebug(_debugger, node, "",
                                                       data);
                    } else {
                        return new RawCharsAction(data);

                    }
                }
                if (_debugger != null) {
                    return new CharsActionDebug(_debugger, node, "",
                                                data);
                } else {
                    return new CharsAction(data);
                }
            } else {
                return emptyAction;
            }
        }
    }
   
    // <!-- Category: instruction -->
    //     <xsl:value-of
    //       select = string-expression
    //       disable-output-escaping = "yes" | "no" />
    //
    private class ValueOfParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            StringExpr expr =
                ExprParser.parseStringExpr(node,
                                           getRequiredAttribute(node, SELECT),
                                           currentLocalVariables);
            if ("yes".equals(node.getAttributeValue(DISABLE_OUTPUT_ESCAPING))){
                if (_debugger != null) {
                    return new RawValueOfActionDebug(_debugger, node, "",
                                                expr);
                } else {
                    return new RawValueOfAction(expr);
                }
            }
            if (_debugger != null) {
                return new ValueOfActionDebug(_debugger, node, "",
                                              expr);
            } else {
                return new ValueOfAction(expr);
            }
        }
    }
   
    // <!-- Category: instruction -->
    //     <xsl:number
    //       level = "single" | "multiple" | "any"
    //       count = pattern
    //       from = pattern
    //       value = number-expression
    //       format = { string }
    //       lang = { nmtoken }
    //       letter-value = { "alphabetic" | "traditional" }
    //       grouping-separator = { char }
    //       grouping-size = { number } />
    //
    private class NumberParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            NumberListFormatTemplate format =
                getNumberListFormatTemplate(node);
            String value = node.getAttributeValue(VALUE);
            if (value != null)
                return new
                    ExprNumberAction(ExprParser.
                                     parseNumberExpr(node, value,
                                                     currentLocalVariables),
                                     format);
            String level = getOptionalAttribute(node, LEVEL, "single");
            String countString = node.getAttributeValue(COUNT);
            Pattern count;
            if (countString == null) {
                count = null;
            } else {
                count = ExprParser.parsePattern(node, countString,
                                                currentLocalVariables);
      }
            String fromString = node.getAttributeValue(FROM);
            Pattern from;
            if (fromString == null) {
                from = null;
            } else {
                from = ExprParser.parsePattern(node, fromString, currentLocalVariables);
            }
            if (level.equals("any")) {
                if (_debugger != null) {
                    return new AnyLevelNumberActionDebug(_debugger, node, "",
                                                         count, from, format);
                } else {
                    return new AnyLevelNumberAction(count, from, format);
                }
            }
            if (level.equals("multiple")) {
                if (_debugger != null) {
                    return new MultiLevelNumberActionDebug(_debugger, node, "",
                                                           count, from, format);
                } else {
                    return new MultiLevelNumberAction(count, from, format);
                }
            }
            if (level.equals("single")) {
                if (_debugger != null) {
                    return new SingleLevelNumberActionDebug(_debugger, node, "",
                                                            count, from, format);
                } else {
                    return new SingleLevelNumberAction(count, from, format);
                }
            }
            throw new XSLException("bad level", node);
        }
    }
   
    // parses EXSLT Functions: result action
    // <func:result select='...' />
    private class EXSLFunctionResultParser implements ActionParser
    {

        public Action parse(Node node) throws XSLException
        {
            VariantExpr expr = getVariantExpr(node);
            return null;

//             if (_debugger != null) {
//                 return new ValueOfActionDebug(_debugger, node, "",
//                                               expr);
//             } else {
//                 return new ValueOfAction(expr);
//             }
        }
       
    }


    //
    //     <!-- Category: instruction -->
    //     <xsl:apply-imports />
    //
    private class ApplyImportsParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            if (_debugger != null) {
                return new ApplyImportsActionDebug(_debugger, node, "");
            } else {
                return new ApplyImportsAction();
            }
        }
    }
   
    //
    // parses non-top level xsl:variable or xsl:param
    // <!-- Category: instruction -->
    //     <xsl:variable
    //       name = qname
    //       select = expression>
    //       <!-- Content: template -->
    //     </xsl:variable>
    //
    private class VariableActionParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            Name name =
                expandSourceElementTypeName(getRequiredAttribute(node, NAME),
                                            node);
            VariantExpr expr = getVariantExpr(node);

            // Must create the VariantExpr before adding to local variables.
            currentLocalVariables = new AddVariableSet(name,
                                                       currentLocalVariables);
            nCurrentLocalVariables++;
            return makeAction(node, name, expr);
        }
       
        Action makeAction(Node node, Name name, VariantExpr expr)
        {
            if (_debugger != null) {
                return new BindLocalVariableActionDebug(_debugger, node, "",
                                                        name, expr);
            } else {
                return new BindLocalVariableAction(name, expr);
            }
        }
    }
   
    //
    //<!-- Category: top-level-element -->
    //     <xsl:param
    //       name = qname
    //       select = expression>
    //       <!-- Content: template -->
    //     </xsl:param>
    //
    private class ParamActionParser extends VariableActionParser
    {
        Action makeAction(Node node, Name name, VariantExpr expr)
        {
            if (_debugger != null) {
                return new BindLocalParamActionDebug(_debugger, node, "",
                                                     name, expr);
            } else {
                return new BindLocalParamAction(name, expr);
            }
        }
    }
   
    //
    // <!-- Category: instruction -->
    //     <xsl:message
    //       terminate = "yes" | "no">
    //       <!-- Content: template -->
    //     </xsl:message>
    //
    private class MessageParser implements ActionParser
    {
        public Action parse(Node node) throws XSLException
        {
            boolean terminate = "yes".equals(node.getAttributeValue(TERMINATE));
            Action content = parseActions(node, emptyAction);
            if (terminate) {
                if (_debugger != null) {
                    return new TerminateMessageActionDebug(_debugger, node, "",
                                                           content);
                } else {
                    return new TerminateMessageAction(content);
                }
            } else {
                if (_debugger != null) {
                    return new MessageActionDebug(_debugger, node, "",
                                                  content);
                } else {
                    return new MessageAction(content);
                }
            }
        }
    }
   
    // Use this with Result. Tracks the output method options
    // specified in the xsl:stylesheet element
    private class OutputMethodImpl implements OutputMethod, Cloneable
    {
        // "text", "html", "xml" or colonized qName
        private Name _outputMethodName = null;

        // don't include "method" and "cdata-section-elements"
        private Name[] _outputMethodAttNames = new Name[] {
            DOCTYPE_PUBLIC,
            DOCTYPE_SYSTEM,
            ENCODING,
            INDENT,
            MEDIA_TYPE,
            OMIT_XML_DECLARATION,
            STANDALONE,
            VERSION
        };

        private String[] _outputMethodAttValues = new String[] {
            null, null, null, null, null, null, null, null};

        private Vector _outputCdataSectionElements = new Vector();

        //        private Vector outputMethodAttributes = new Vector();

       
        /**
         * create a new OutputMethod for an xt:document extension element
         */
        OutputMethod mergeCopy(Node node) throws XSLException
        {
            SafeNodeIterator iter = node.getAttributes();
            if (iter.next() != null && iter.next() == null) {
                // we don't need to bother if they're no new attributes
                return this;
            }
            OutputMethodImpl om = (OutputMethodImpl)clone();
            om.merge(node);
            return om;
        }
       
        public Object clone()
        {
            try {
                OutputMethodImpl om = (OutputMethodImpl)super.clone();
                om._outputCdataSectionElements = (Vector)_outputCdataSectionElements.clone();
                om._outputMethodAttValues = (String[]) _outputMethodAttValues.clone();

                return om;
            }
            catch (CloneNotSupportedException e) {
                throw new Error("unexpected CloneNotSupportedException");
            }
        }
       
        /**
         * merge in the attributes found in an "xsl:output" top level element
         */
        void merge(Node node) throws XSLException
        {
            // FIXME error checking

            for (SafeNodeIterator iter = node.getAttributes() ; ; ) {
                Node att = iter.next();
                if (att == null) {
                    break;
                }

                Name name = att.getName();
                String value = att.getData();
                setSpecifiedValue(name, value, node);
            }
        }
       
        public Name getName()
        {
            return _outputMethodName;
        }
       
        /**
         * return the value for the named output method attribute,
         * if that attribute was specified in the stylesheet
         *
         * @return null if the value was not declared in the stylesheet
         */
        public String getSpecifiedValue(Name name)
        {
            for (int i = 0; i < _outputMethodAttNames.length; ++i) {
                if (_outputMethodAttNames[i].equals(name)) {
                    return _outputMethodAttValues[i];
                }
            }
            return null;

        }


        public void setSpecifiedValue(Name name, String value, Node node)
            throws XSLException
        {
            if (HREF.equals(name)) {
                // ignore
            } else if (METHOD.equals(name)) {
                if (node != null ) {
                    _outputMethodName =
                        node.getNamespacePrefixMap().expandAttributeName(value, node);
                } else {
                    // FIXME: value may have another mechanism for namespace ref
                }
            } else if (CDATA_SECTION_ELEMENTS.equals(name)) {
                StringTokenizer t = new StringTokenizer(value);
                if (node != null) {
                    NamespacePrefixMap nsm = node.getNamespacePrefixMap();
                   
                    while (t.hasMoreElements()) {
                        String elQnm = (String)t.nextElement();
                        Name cdataElNm = nsm.expandAttributeName(elQnm, node);
                        _outputCdataSectionElements.addElement(cdataElNm);
                    }
                } else {
                    // FIXME: value may have another mechanism for namespace ref
                }
            } else {
                for (int i = 0; i < _outputMethodAttNames.length; ++i) {
                    if (_outputMethodAttNames[i].equals(name)) {
                        _outputMethodAttValues[i] = value;
                        return;
                    }
                }
                // we grow arrays for non-standard att names (is this prudent?)
                int len = _outputMethodAttNames.length;
                Name[] newNames = new Name[len + 1];
                String[] newVals = new String[len + 1];
                System.arraycopy(_outputMethodAttNames, 0, newNames, 0, len);
                System.arraycopy(_outputMethodAttValues, 0, newVals, 0, len);
                newNames[len] = name;
                newVals[len] = value;
                _outputMethodAttNames = newNames;
                _outputMethodAttValues = newVals;
            }
        }      

        /**
         * all the known output method attribute names, excluding
         * "method" and "cdata-section-elements"
         */
        public Name[] getAttributeNames()
        {
            return _outputMethodAttNames;
        }

        /**
         * returns the defaulted value for an output method property
         * which hasn't been set by an xsl:output stylesheet element
         */
        private String getDefaultValue(Name name)
        {
            if (METHOD.equals(name)) {
                return "xml";
            } else {
                if ("xml".equals(getPropertyValue(METHOD))) {
                    if (DOCTYPE_PUBLIC.equals(name)) {
                    } else if (DOCTYPE_SYSTEM.equals(name)) {
                    } else if (ENCODING.equals(name)) {
                    } else if (INDENT.equals(name)) {
                    } else if (MEDIA_TYPE.equals(name)) {
                    } else if (OMIT_XML_DECLARATION.equals(name)) {
                        return "no";
                    } else if (STANDALONE.equals(name)) {
                        return "no";
                    } else if (VERSION.equals(name)) {
                        return "1.0";
                    } else {
                        return null;
                    }
                } else if ("html".equals(getPropertyValue(METHOD))) {
                    if (DOCTYPE_PUBLIC.equals(name)) {
                    } else if (DOCTYPE_SYSTEM.equals(name)) {
                    } else if (ENCODING.equals(name)) {
                    } else if (INDENT.equals(name)) {
                    } else if (MEDIA_TYPE.equals(name)) {
                    } else if (OMIT_XML_DECLARATION.equals(name)) {
                        return "no";
                    } else if (STANDALONE.equals(name)) {
                        return "no";
                    } else if (VERSION.equals(name)) {
                        return "1.0";
                    } else {
                        return null;
                    }
                } else if ("text".equals(getPropertyValue(METHOD))) {
                    if (DOCTYPE_PUBLIC.equals(name)) {
                    } else if (DOCTYPE_SYSTEM.equals(name)) {
                    } else if (ENCODING.equals(name)) {
                    } else if (INDENT.equals(name)) {
                    } else if (MEDIA_TYPE.equals(name)) {
                    } else if (OMIT_XML_DECLARATION.equals(name)) {
                        return "no";
                    } else if (STANDALONE.equals(name)) {
                        return "no";
                    } else if (VERSION.equals(name)) {
                        return "1.0";
                    } else {
                        return null;
                    }
                } else if ("java".equals(getPropertyValue(METHOD))) {
                    if (DOCTYPE_PUBLIC.equals(name)) {
                    } else if (DOCTYPE_SYSTEM.equals(name)) {
                    } else if (ENCODING.equals(name)) {
                    } else if (INDENT.equals(name)) {
                    } else if (MEDIA_TYPE.equals(name)) {
                    } else if (OMIT_XML_DECLARATION.equals(name)) {
                        return "no";
                    } else if (STANDALONE.equals(name)) {
                        return "no";
                    } else if (VERSION.equals(name)) {
                        return "1.0";
                    } else {
                        return null;
                    }
                } else {
                    if (DOCTYPE_PUBLIC.equals(name)) {
                    } else if (DOCTYPE_SYSTEM.equals(name)) {
                    } else if (ENCODING.equals(name)) {
                    } else if (INDENT.equals(name)) {
                    } else if (MEDIA_TYPE.equals(name)) {
                    } else if (OMIT_XML_DECLARATION.equals(name)) {
                        return "no";
                    } else if (STANDALONE.equals(name)) {
                        return "no";
                    } else if (VERSION.equals(name)) {
                        return "1.0";
                    } else {
                        return null;
                    }
                }

            }
            return null;
        }

        /**
         * gets the value specified in the stylesheet, if available,
         * else gets the defaulted value
         */
        public String getPropertyValue(Name name)
        {
            String val = getSpecifiedValue(name);
            return (val == null) ? getDefaultValue(name) : val;
        }

        /**
         * return a list of all element names which should
         * have their content escaped in cdata sections
         */
        public Name[] getCdataSectionElements()
        {
            Name names[] = new Name[_outputCdataSectionElements.size()];
            for (int i = 0; i < names.length; i++) {
                names[i] = (Name)_outputCdataSectionElements.elementAt(i);
            }
            return names;
        }
       
        public NameTable getNameTable()
        {
            return nameTable;
        }
       
    }

}
TOP

Related Classes of com.jclark.xsl.tr.SheetImpl$KeyParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.