Esempio n. 1
0
    def instantiate(self, context, used=None):
        if used is None:
            used = []

        if self in used:
            raise XsltError(XsltError.CIRCULAR_ATTRIBUTE_SET, self, self._name)
        else:
            used.append(self)

        # XSLT 1.0, Section 7.1.4, Paragraph 4:
        # The available variable bindings are only the top-level ones.
        variables = context.variables
        context.variables = context.global_variables

        attribute_sets = context.transform.attribute_sets
        for name in self._use_attribute_sets:
            try:
                attribute_set = attribute_sets[name]
            except KeyError:
                raise XsltError(XsltError.UNDEFINED_ATTRIBUTE_SET, self,
                                attr_set_name)
            else:
                attribute_set.instantiate(context)

        self.process_children(context)

        context.variables = variables
        used.remove(self)
        return
    def instantiate(self, context):
        context.instruction = self
        context.namespaces = self.namespaces

        target = self._name.evaluate(context)
        if target[:-1] in ('X', 'x'):
            if target.lower() == 'xml':
                raise XsltError(XsltError.ILLEGAL_XML_PI)

        context.push_string_writer()
        try:
            try:
                self.process_children(context)
            except RuntimeError:
                raise XsltError(XsltError.NONTEXT_IN_PI)
        finally:
            writer = context.pop_writer()

        # XSLT 1.0, Section 7.3, Paragraph 5:
        # ERROR: it is an error for the content to contain '?>'
        # RECOVERY: insert space between '?' and '>'
        data = writer.get_result()
        if '?>' in data:
            data = content.replace(u'?>', u'? >')
        context.processing_instruction(target, data)
        return
Esempio n. 3
0
 def prepare(self, element, value):
     if value is None:
         return self.default
     if not value:
         raise XsltError(XsltError.INVALID_NCNAME_ATTR, value=value)
     if ':' in value:
         raise XsltError(XsltError.INVALID_NCNAME_ATTR, value=value)
     return value
Esempio n. 4
0
 def prepare(self, element, value):
     if value is None:
         return self.default
     if not value:
         raise XsltError(XsltError.INVALID_PREFIX_ATTR, value=value)
     if ':' in value:
         raise XsltError(XsltError.INVALID_PREFIX_ATTR, value=value)
     if value == '#default':
         value = None
     return value
Esempio n. 5
0
    def instantiate(self, context):
        if self._select:
            context.instruction = self
            context.namespaces = self.namespaces
            try:
                nodes = self._select.evaluate_as_nodeset(context)
            except TypeError:
                raise
                raise XsltError(XsltError.INVALID_FOREACH_NODESET)
        else:
            nodes = context.node.xml_children

        # Save the context focus (node/pos/size) and state (tpl/curr)
        saved = (context.node, context.position, context.size,
                 context.template, context.current_node)
        # Now process the selected nodes
        context.template = None
        size = context.size = len(nodes)
        position = 1
        for node in nodes:
            context.node = context.current_node = node
            context.position = position
            self.process_children(context)
            position += 1

        (context.node, context.position, context.size, context.template,
         context.current_node) = saved
        return
Esempio n. 6
0
 def prime(self, context):
     processed = context.variables
     elements, deferred = self._variables, []
     num_writers = len(context._writers)
     while 1:
         for element in elements:
             if element._name in processed:
                 continue
             try:
                 element.instantiate(context)
             except XPathError, error:
                 if error.code != XPathError.UNDEFINED_VARIABLE:
                     raise
                 # Remove any aborted and possibly unbalanced
                 # outut handlers on the stack.
                 del context._writers[num_writers:]
                 deferred.append(element)
         if not deferred:
             break
         elif deferred == elements:
             # Just pick the first one as being the "bad" variable.
             raise XsltError(XsltError.CIRCULAR_VARIABLE,
                             name=deferred[0]._name)
         # Re-order stored variable elements to simplify processing for
         # the next transformation.
         for element in deferred:
             self._variables.remove(element)
             self._variables.append(element)
         # Try again, but this time processing only the ones that
         # referenced, as of yet, undefined variables.
         elements, deferred = deferred, []
Esempio n. 7
0
 def prepare(self, element, value):
     if value is None:
         return self.default and self.default == 'yes'
     elif value not in ['yes', 'no']:
         raise XsltError(XsltError.INVALID_ATTR_CHOICE,
                         value=value)  #, str(self))
     return value == 'yes'
Esempio n. 8
0
 def prepare(self, element, value):
     if value is None:
         return self.default
     try:
         return float(value or self.default)
     except:
         raise XsltError(XsltError.INVALID_NUMBER_ATTR, value=value)
Esempio n. 9
0
    def run(self, source, parameters=None, result=None):
        """
        Transform a source document as given via an InputSource.

        Assumes that either the Processor instance has already had
        stylesheets appended (via appendStylesheet(), for example), or
        the source document contains xml-stylesheet processing
        instructions that are not being ignored.

        The `parameters` argument is an optional dictionary of
        stylesheet parameters, the keys of which may be given as
        strings if they have no namespace, or as (uri, localname)
        tuples otherwise.

        The optional writer argument is a SAX-like event handler that
        is an Ft.Xml.Xslt.NullWriter subclass. The default writer is
        either an Ft.Xml.Xslt.XmlWriter, HtmlWriter or PlainTextWriter,
        depending on the stylesheet(s).

        The optional `output` argument is a Python file-like object
        to be used as the destination for the writer's output.
        """
        try:
            document = tree.parse(source)
        except ReaderError, e:
            raise XsltError(XsltError.SOURCE_PARSE_ERROR,
                            uri=(source.uri or '<Python string>'),
                            text=e)
Esempio n. 10
0
    def _parseSrc(self, isrc, features, properties):
        parser = sax.create_parser()
        parser.setContentHandler(self)
        for featurename, value in features:
            parser.setFeature(featurename, value)

        # Always set whitespace rules property
        parser.setProperty(sax.PROPERTY_WHITESPACE_RULES,
                           _XSLT_WHITESPACE_STRIPPING)
        for propertyname, value in properties:
            parser.setProperty(propertyname, value)

        prev_source = self._input_source
        try:
            self._input_source = isrc
            try:
                parser.parse(isrc)
            except SAXParseException, e:
                e = e.getException() or e
                if isinstance(e, XsltError):
                    raise e
                raise XsltError(XsltError.STYLESHEET_PARSE_ERROR,
                                uri=isrc.uri,
                                text=str(e))
        finally:
            self._input_source = prev_source

        return self._root.stylesheet
Esempio n. 11
0
    def _run(self, node, parameters=None, result=None):
        """
        Runs the stylesheet processor against the given XML DOM node with the
        stylesheets that have been registered. It does not mutate the source.
        If writer is given, it is used in place of the default output method
        decisions for choosing the proper writer.
        """
        #QUESTION: What about ws stripping?
        #ANSWER: Whitespace stripping happens only in the run*() interfaces.
        #  This method is use-at-your-own-risk. The XSLT conformance of the
        #  source is maintained by the caller. This exists as a performance
        #  hook.
        parameters = parameters or {}

        self.attributeSets = {}
        self.keys = {}

        #See f:chain-to extension element
        self.chainTo = None
        self.chainParams = None

        if not self.transform:
            raise XsltError(XsltError.NO_STYLESHEET)

        # Use an internal result to gather the output only if the caller
        # didn't supply other means of retrieving it.
        if result is None:
            result = stringresult()
        result.parameters = self.transform.output_parameters
        assert result.writer

        # Initialize any stylesheet parameters
        initial_variables = parameters.copy()
        for name in parameters:
            if name not in self.transform.parameters:
                del initial_variables[name]

        # Prepare the stylesheet for processing
        context = xsltcontext.xsltcontext(node,
                                          variables=initial_variables,
                                          transform=self.transform,
                                          processor=self,
                                          extfunctions=self._extfunctions,
                                          output_parameters=result.parameters)
        context.add_document(node, node.xml_base)
        context.push_writer(result.writer)
        self.transform.root.prime(context)

        # Process the document
        try:
            self.transform.apply_templates(context, [node])
        except XPathError, e:
            raise
            instruction = context.instruction
            strerror = str(e)
            e.message = MessageSource.EXPRESSION_POSITION_INFO % (
                instruction.baseUri, instruction.lineNumber,
                instruction.columnNumber, instruction.nodeName, strerror)
            raise
Esempio n. 12
0
 def prepare(self, element, value):
     if value is None:
         if self.default is None:
             return None
         value = self.default
     elif not isqname(value):
         raise XsltError(XsltError.INVALID_QNAME_ATTR, value=value)
     return splitqname(value)
Esempio n. 13
0
 def parse(self, expr):
     """Parses the string `expr` into an AST"""
     try:
         return self._parse(expr)
     except _xpatternparser.error, error:
         raise XsltError(XsltError.INVALID_PATTERN,
                         line=error.lineno,
                         column=error.offset,
                         text=error.msg)
Esempio n. 14
0
 def instantiate(self, context):
     try:
         precedence = context.template.import_precedence
     except AttributeError:
         assert context.template is None
         raise XsltError(XsltError.APPLYIMPORTS_WITH_NULL_CURRENT_TEMPLATE,
                         self)
     context.transform.apply_imports(context, precedence)
     return
Esempio n. 15
0
    def prepare(self, element, value):
        if value is None:
            if self.default is None:
                return None
            value = self.default
        elif not value:
            raise XsltError(XsltError.QNAME_BUT_NOT_NCNAME, value=value)

        try:
            index = value.index(':')
        except ValueError:
            raise XsltError(XsltError.QNAME_BUT_NOT_NCNAME, value=value)
        prefix, local = value[:index], value[index + 1:]
        try:
            namespace = element.namespaces[prefix]
        except KeyError:
            raise XsltRuntimeException(XsltError.UNDEFINED_PREFIX,
                                       elem=element,
                                       prefix=prefix)
        return (namespace, local)
Esempio n. 16
0
 def prime(self, context,
              _test_elements=(if_element.if_element,),
              _choose_elements=(choose_elements.when_element,
                                choose_elements.otherwise_element,)):
     transform = self.root.stylesheet
     try:
         template = self._template = transform.named_templates[self._name]
     except KeyError:
         raise XsltError(XsltError.NAMED_TEMPLATE_NOT_FOUND,
                         self, self._name)
     # NOTE: Tail recursion is now checked for in the xsl:template setup().
     return
Esempio n. 17
0
    def _match_nodes(self, context, nodes):
        initial_focus = context.node, context.position, context.size
        context.size = len(nodes)
        position = 1
        for node in nodes:
            context.node = context.current_node = node
            context.position = position
            position += 1
            # Get the possible matches for `node`
            type_key = node.xml_typecode
            type_table = self._match_table
            if type_key in type_table:
                if type_key == tree.element.xml_typecode:
                    element_table = type_table[type_key]
                    name_key = node.xml_name
                    if name_key in element_table:
                        matches = element_table[name_key]
                    else:
                        matches = element_table[None]
                else:
                    matches = type_table[type_key]
            else:
                matches = type_table[tree.node.xml_typecode]

            for pattern, axis_type, namespaces, use_expr in matches:
                context.namespaces = namespaces
                try:
                    m = pattern.match(context, node, axis_type)
                except XPathError, exc:
                    raise XsltError(exc.code)

                if m:
                    focus = context.node, context.position, context.size
                    context.node, context.position, context.size = node, 1, 1
                    value = use_expr.evaluate(context)
                    if isinstance(value, datatypes.nodeset):
                        for value in value:
                            yield datatypes.string(value), node
                    else:
                        yield datatypes.string(value), node
                    context.node, context.position, context.size = focus

            if isinstance(node, tree.element):
                for item in self._match_nodes(context, node.xml_children):
                    yield item
                if self._matches_attribute and node.xml_attributes:
                    attributes = tuple(node.xml_attributes.nodes())
                    for item in self._match_nodes(context, attributes):
                        yield item
            elif isinstance(node, tree.entity):
                for item in self._match_nodes(context, node.xml_children):
                    yield item
Esempio n. 18
0
 def _lookup(cls, output_parameters):
     method = output_parameters.method
     try:
         cls = cls._methods[method]
     except KeyError:
         if method[0] is None:
             # display only localName if in the null namespace
             method = method[1]
         raise XsltError(XsltError.UNKNOWN_OUTPUT_METHOD, str(method))
     if (cls is xmlwriter.xmlwriter
             and output_parameters.cdata_section_elements):
         cls = xmlwriter.cdatasectionwriter
     return cls
Esempio n. 19
0
    def instantiate(self, context):
        context.instruction = self
        context.namespaces = self.namespaces

        prefix, name = self._name.evaluate(context)
        if prefix:
            name = prefix + u':' + name
        elif name == 'xmlns':
            # Section 7.1.3, Paragraph 2
            raise XsltError(XsltError.BAD_ATTRIBUTE_NAME, name=name)

        # From sec. 7.1.3 of the XSLT spec:
        # 1. if 'namespace' is not present, use ns in scope, based on prefix
        #    from the element QName in 'name'; if no prefix, use null-namespace
        # 2. if 'namespace' is present and empty string, use null-namespace
        # 3. otherwise, use 'namespace' directly
        #
        if not self._namespace:
            if prefix is not None:
                try:
                    namespace = self.namespaces[prefix]
                except KeyError:
                    raise XsltError(XsltError.UNDEFINED_PREFIX, prefix=prefix)
            else:
                namespace = None
        else:
            namespace = self._namespace.evaluate_as_string(context) or None

        context.push_string_writer()
        try:
            try:
                self.process_children(context)
            except RuntimeError:
                raise XsltError(XsltError.NONTEXT_IN_ATTRIBUTE)
        finally:
            writer = context.pop_writer()
        context.attribute(name, writer.get_result(), namespace)
        return
Esempio n. 20
0
 def prepare(self, element, value):
     if value is None:
         if self.default is None:
             return None
         value = self.default
     try:
         return parse_xpath(value)
     except SyntaxError, error:
         raise XsltError(XsltError.INVALID_EXPRESSION,
                         value=value,
                         baseuri=element.baseUri,
                         line=element.lineNumber,
                         col=element.columnNumber,
                         msg=str(error))
Esempio n. 21
0
    def instantiate(self, context):
        context.instruction = self
        context.namespaces = self.namespaces

        # XSLT 1.0, Section 7.1.2, Paragraph 2:
        # ERROR: not a QName
        # RECOVERY: instantiate children excluding attribute nodes
        prefix, name = self._name.evaluate(context)
        if prefix:
            name = prefix + u':' + name

        # XSLT 1.0, Section 7.1.2:
        # - if `namespace` is not present, `name` is expanded using the
        #   in-scope namespace declarations
        # - if `namespace` is present, the empty string is the null namespace,
        #   otherwise, the string is the namespace.
        if not self._namespace:
            try:
                namespace = self.namespaces[prefix]
            except KeyError:
                raise XsltError(XsltError.UNDEFINED_PREFIX, prefix=prefix)
        else:
            namespace = self._namespace.evaluate_as_string(context) or None

        context.start_element(name, namespace)
        if self._use_attribute_sets:
            attribute_sets = context.transform.attribute_sets
            for set_name in self._use_attribute_sets:
                try:
                    attribute_set = attribute_sets[set_name]
                except KeyError:
                    raise XsltError(XsltError.UNDEFINED_ATTRIBUTE_SET,
                                    name=set_name)
                attribute_set.instantiate(context)
        self.process_children(context)
        context.end_element(name, namespace)
        return
Esempio n. 22
0
 def prepare(self, element, value):
     if value is None:
         return _avt_constant(element, self, self.default)
     elif '{' not in value and '}' not in value:
         return _avt_constant(element, self, value)
     try:
         return _avt_wrapper(element, self, value)
     except XsltError, error:
         # an error from the AVT parser
         raise XsltError(XsltError.INVALID_AVT,
                         value=value,
                         baseuri=element.baseUri,
                         line=element.lineNumber,
                         col=element.columnNumber,
                         msg=str(error))
Esempio n. 23
0
 def prepare(self, element, value):
     if value is None:
         return self.default
     if value not in self.values:
         # check for an `attribute_type` instance
         for allowed in self.values:
             if isinstance(allowed, self.__class__):
                 try:
                     allowed.prepare(element, value)
                 except:
                     pass
                 else:
                     break
         else:
             # if we get here it is an error
             raise XsltError(XsltError.INVALID_ATTR_CHOICE, value=value)
     return value
Esempio n. 24
0
 def instantiate(self, context):
     output_parameters = outputparameters.outputparameters(
         method='xml',
         encoding=context.output_parameters.encoding,
         omit_xml_declaration=True)
     writer = xmlwriter.xmlwriter(output_parameters, StringIO())
     context.push_writer(writer)
     try:
         self.process_children(context)
     finally:
         writer = context.pop_writer()
     msg = writer.stream.getvalue()
     if self._terminate:
         raise XsltError(XsltError.STYLESHEET_REQUESTED_TERMINATION,
                         msg=msg)
     else:
         context.message(msg)
     return
Esempio n. 25
0
    def instantiate(self, context):
        context.instruction = self
        context.namespaces = self.namespaces

        node = context.node
        if isinstance(node, tree.element):
            # Namespace nodes are automatically copied as well
            # See XSLT 1.0 Sect 7.5
            context.start_element(node.xml_qname, node.xml_namespace,
                                  node.xmlns_attributes.copy())
            if self._use_attribute_sets:
                attribute_sets = context.transform.attribute_sets
                for name in self._use_attribute_sets:
                    try:
                        attribute_set = attribute_sets[name]
                    except KeyError:
                        raise XsltError(XsltError.UNDEFINED_ATTRIBUTE_SET,
                                        self, name)
                    attribute_set.instantiate(context)
            self.process_children(context)
            context.end_element(node.xml_qname, node.xml_namespace)

        elif isinstance(node, (tree.text, tree.comment)):
            context.text(node.xml_value)

        elif isinstance(node, tree.entity):
            self.process_children(context)

        elif isinstance(node, tree.attribute):
            context.attribute(node.xml_qname, node.xml_value,
                              node.xml_namespace)

        elif isinstance(node, tree.processing_instruction):
            context.processing_instruction(node.xml_target, node.xml_data)

        elif isinstance(node, tree.namespace):
            # Relies on XmlWriter rules, which is very close to spec:
            # http://www.w3.org/1999/11/REC-xslt-19991116-errata/#E25
            context.namespace(node.xml_qname, node.xml_value)

        else:
            raise RuntimeError("Unupported node type: %r" % type(node))

        return
Esempio n. 26
0
    def prepare(self, element, value):
        if value is None:
            if self.default is None:
                return None
            value = self.default
        elif not isqname(value):
            raise XsltError(XsltError.INVALID_QNAME_ATTR, value=value)

        prefix, local = splitqname(value)
        if prefix:
            try:
                namespace = element.namespaces[prefix]
            except KeyError:
                raise XsltRuntimeException(XsltError.UNDEFINED_PREFIX,
                                           elem=element,
                                           prefix=prefix)
        else:
            namespace = None
        return (namespace, local)
Esempio n. 27
0
    def apply_imports(self, context, precedence):
        node, mode = context.node, context.mode
        # Get the possible template rules for `node`
        type_key = node.xml_typecode
        if mode in self.match_templates:
            type_table = self.match_templates[mode]
            if type_key in type_table:
                if type_key == tree.element.xml_typecode:
                    element_table = type_table[type_key]
                    name = node.xml_name
                    if name in element_table:
                        template_rules = element_table[name]
                    else:
                        template_rules = element_table[None]
                else:
                    template_rules = type_table[type_key]
            else:
                template_rules = type_table[tree.node.xml_typecode]
        else:
            template_rules = ()

        first_template = locations = None
        for sort_key, pattern, axis_type, template in template_rules:
            # Filter out those patterns with a higher import precedence than
            # what was specified.
            if sort_key[0] < precedence:
                context.namespaces = template.namespaces
                try:
                    m = pattern.match(context, node, axis_type)
                except XpathError, exc:
                    raise XsltError(exc.code, node, locations)

                if m:
                    # Make sure the template starts with a clean slate
                    state = context.template, context.variables
                    context.template = template
                    context.variables = context.global_variables
                    try:
                        template.instantiate(context)
                    finally:
                        context.template, context.variables = state
                    break
Esempio n. 28
0
    def instantiate(self, context):
        context.instruction = self
        context.namespaces = self.namespaces

        context.start_element(self.nodeName, self._output_namespace,
                              self._output_nss)

        for name, namespace, value in self._output_attrs:
            value = value.evaluate(context)
            context.attribute(name, value, namespace)

        if self._use_attribute_sets:
            attribute_sets = context.transform.attribute_sets
            for name in self._use_attribute_sets:
                try:
                    attribute_set = attribute_sets[name]
                except KeyError:
                    raise XsltError(XsltError.UNDEFINED_ATTRIBUTE_SET, name=name)
                attribute_set.instantiate(context)

        self.process_children(context)

        context.end_element(self.nodeName, self._output_namespace)
        return
Esempio n. 29
0
    def apply_templates(self, context, nodes, mode=None, params=None):
        """
        Intended to be used by XSLT instruction implementations only.

        Implements the xsl:apply-templates instruction by attempting to
        let the stylesheet apply its own template for the given context.
        If the stylesheet does not have a matching template, the
        built-in templates are invoked.

        context is an XsltContext instance. params is a dictionary of
        parameters being passed in, defaulting to None.
        """
        initial_focus = context.node, context.position, context.size
        initial_state = context.template, context.mode

        if params is None:
            params = {}

        context.size, context.mode = len(nodes), mode
        # Note, it is quicker to increment the `position` variable than it
        # is to use enumeration: itertools.izip(nodes, itertools.count(1))
        position = 1
        for node in nodes:
            # Set the current node for this template application
            context.node = context.current_node = node
            context.position = position
            position += 1

            # Get the possible template rules for `node`
            type_key = node.xml_typecode
            if mode in self.match_templates:
                type_table = self.match_templates[mode]
                if type_key in type_table:
                    if type_key == tree.element.xml_typecode:
                        element_table = type_table[type_key]
                        name = node.xml_name
                        if name in element_table:
                            template_rules = element_table[name]
                        else:
                            template_rules = element_table[None]
                    else:
                        template_rules = type_table[type_key]
                else:
                    template_rules = type_table[tree.node.xml_typecode]
            else:
                template_rules = ()

            first_template = locations = None
            for sort_key, pattern, axis_type, template in template_rules:
                context.namespaces = template.namespaces
                if pattern.match(context, node, axis_type):
                    if 1:  # recovery_method == Recovery.SILENT
                        # (default until recovery behaviour is selectable)
                        # Just use the first matching pattern since they are
                        # already sorted in descending order.
                        break
                    else:  # recovery_method in (Recovery.WARNING, Recovery.NONE)
                        if not first_template:
                            first_template = template
                        else:
                            if not locations:
                                locations = [
                                    _template_location(first_template)
                                ]
                            locations.append(_template_location(template))
            else:
                # All template rules have been processed
                if locations:
                    # Multiple template rules have matched.  Report the
                    # template rule conflicts, sorted by position
                    locations.sort()
                    locations = '\n'.join(TEMPLATE_CONFLICT_LOCATION % location
                                          for location in locations)
                    exception = XsltError(XsltError.MULTIPLE_MATCH_TEMPLATES,
                                          node, locations)
                    if 1:  # recovery_method == Recovery.WARNING
                        processor.warning(str(exception))
                    else:
                        raise exception
                if first_template:
                    template = first_template
                    context.namespaces = template.namespaces
                else:
                    template = None

            if template:
                context.template = template
                # Make sure the template starts with a clean slate
                variables = context.variables
                context.variables = context.global_variables
                try:
                    template.instantiate(context, params)
                finally:
                    context.variables = variables
            else:
                # Nothing matched, use builtin templates
                if params and self.builtin_param_warning:
                    context.processor.warning(BUILTIN_TEMPLATE_WITH_PARAMS)
                    self.builtin_param_warning = False
                if isinstance(node, (tree.element, tree.entity)):
                    self.apply_templates(context, node.xml_children, mode)
                elif isinstance(node, (tree.text, tree.attribute)):
                    context.text(node.xml_value)

        # Restore context
        context.node, context.position, context.size = initial_focus
        context.template, context.mode = initial_state
        return
Esempio n. 30
0
    def setup(self, _param_element=variable_elements.param_element):
        """
        Called only once, at the first initialization
        """
        self.output_parameters = outputparameters.outputparameters()

        # Sort the top-level elements in decreasing import precedence to ease
        # processing later.
        precedence_key = operator.attrgetter('import_precedence')
        elements = sorted(self.children, key=precedence_key, reverse=True)

        # Merge the top-level stylesheet elements into their respective
        # lists.  Any element name not in the mapping is discarded.
        # Note, by sharing the same list no merging is required later.
        whitespace_elements, variable_elements = [], []
        top_level_elements = {
            'strip-space': whitespace_elements,
            'preserve-space': whitespace_elements,
            'output': [],
            'key': [],
            'decimal-format': [],
            'namespace-alias': [],
            'attribute-set': [],
            'variable': variable_elements,
            'param': variable_elements,
            'template': [],
        }
        # Using `groupby` takes advantage of series of same-named elements
        # appearing adjacent to each other.
        key = operator.attrgetter('expanded_name')
        for (namespace, name), nodes in itertools.groupby(self.children, key):
            if namespace == XSL_NAMESPACE and name in top_level_elements:
                top_level_elements[name].extend(nodes)

        # - process the `xsl:preserve-space` and `xsl:strip-space` elements
        # RECOVERY: Multiple matching patterns use the last occurance
        space_rules = {}
        for element in whitespace_elements:
            strip = element._strip_whitespace
            for token in element._elements:
                namespace, name = token
                space_rules[token] = (namespace, name, strip)
        self.space_rules = space_rules.values()
        # sort in decreasing priority, where `*` is lowest, followed by
        # `prefix:*`, then all others.
        self.space_rules.sort(reverse=True)

        # - process the `xsl:output` elements
        # Sort in increasing import precedence, so the last one added
        # will have the highest import precedence
        elements = top_level_elements['output']
        getter = operator.attrgetter('_method', '_version', '_encoding',
                                     '_omit_xml_declaration', '_standalone',
                                     '_doctype_system', '_doctype_public',
                                     '_cdata_section_elements', '_indent',
                                     '_media_type', '_byte_order_mark',
                                     '_canonical_form')
        for element in elements:
            (method, version, encoding, omit_xmldecl, standalone,
             doctype_system, doctype_public, cdata_elements, indent,
             media_type, byte_order_mark, canonical_form) = getter(element)
            if method is not None:
                self.output_parameters.method = method
            if version is not None:
                self.output_parameters.version = version
            if encoding is not None:
                self.output_parameters.encoding = encoding
            if omit_xmldecl is not None:
                self.output_parameters.omit_xml_declaration = omit_xmldecl
            if standalone is not None:
                self.output_parameters.standalone = standalone
            if doctype_system is not None:
                self.output_parameters.doctype_system = doctype_system
            if doctype_public is not None:
                self.output_parameters.doctype_public = doctype_public
            if cdata_elements:
                self.output_parameters.cdata_section_elements += cdata_elements
            if indent is not None:
                self.output_parameters.indent = indent
            if media_type is not None:
                self.output_parameters.media_type = media_type
            if byte_order_mark is not None:
                self.output_parameters.byte_order_mark = byte_order_mark
            if canonical_form is not None:
                self.output_parameters.canonical_form = canonical_form

        # - process the `xsl:key` elements
        # Group the keys by name
        elements = top_level_elements['key']
        name_key = operator.attrgetter('_name')
        elements.sort(key=name_key)
        keys = self._keys = {}
        for name, elements in itertools.groupby(elements, name_key):
            keys[name] = tuple(elements)

        # - process the `xsl:decimal-format` elements
        formats = self.decimal_formats = {}
        getter = operator.attrgetter('_decimal_separator',
                                     '_grouping_separator', '_infinity',
                                     '_minus_sign', '_NaN', '_percent',
                                     '_per_mille', '_zero_digit', '_digit',
                                     '_pattern_separator')
        for element in top_level_elements['decimal-format']:
            name = element._name
            format = getter(element)
            # It is an error to declare a decimal-format more than once
            # (even with different import precedence) with different values.
            if name in formats and formats[name] != format:
                # Construct a useful name for the error message.
                if name:
                    namespace, name = name
                    if namespace:
                        name = element.namespaces[namespace] + ':' + name
                else:
                    name = '#default'
                raise XsltError(XsltError.DUPLICATE_DECIMAL_FORMAT, name)
            else:
                formats[name] = format
        # Add the default decimal format, if not declared.
        if None not in formats:
            formats[None] = ('.', ',', 'Infinity', '-', 'NaN', '%',
                             unichr(0x2030), '0', '#', ';')

        # - process the `xsl:namespace-alias` elements
        elements = top_level_elements['namespace-alias']
        elements.reverse()
        aliases = self.namespace_aliases = {}
        for precedence, group in itertools.groupby(elements, precedence_key):
            mapped = {}
            for element in group:
                namespace = element.namespaces[element._stylesheet_prefix]
                if namespace not in aliases:
                    mapped[namespace] = True
                    result_prefix = element._result_prefix
                    result_namespace = element.namespaces[result_prefix]
                    aliases[namespace] = (result_namespace, result_prefix)
                # It is an error for a namespace URI to be mapped to multiple
                # different namespace URIs (with the same import precedence).
                elif namespace in mapped:
                    raise XsltError(XsltError.DUPLICATE_NAMESPACE_ALIAS,
                                    element._stylesheet_prefix)
        if aliases:
            # apply namespace fixup for the literal elements
            _fixup_aliases(self, aliases)

        # - process the `xsl:attribute-set` elements
        sets = self.attribute_sets = {}
        for element in top_level_elements['attribute-set']:
            sets[element._name] = element

        # - process the `xsl:param` and `xsl:variable` elements
        index, self._variables = {}, variable_elements[:]
        variable_elements.reverse()
        for element in variable_elements:
            name = element._name
            if name not in index:
                # unique (or first) variable binding
                index[name] = 1
            else:
                # shadowed variable binding, remove from processing list
                self._variables.remove(element)
        self.parameters = frozenset(element._name
                                    for element in self._variables
                                    if isinstance(element, _param_element))

        # - process the `xsl:template` elements
        match_templates = collections.defaultdict(_type_dispatch_table)
        named_templates = self.named_templates = {}
        elements = top_level_elements['template']
        elements.reverse()
        getter = operator.attrgetter('node_test', 'axis_type', 'node_type')
        for position, element in enumerate(elements):
            match, name = element._match, element._name
            precedence = element.import_precedence
            if match:
                namespaces = element.namespaces
                template_priority = element._priority
                mode_table = match_templates[element._mode]
                for pattern in match:
                    node_test, axis_type, node_type = getter(pattern)
                    if template_priority is None:
                        priority = node_test.priority
                    else:
                        priority = template_priority
                    sort_key = (precedence, priority, position)
                    info = (sort_key, node_test, axis_type, element)
                    # Add the template rule to the dispatch table
                    type_key = node_type.xml_typecode
                    if type_key == tree.element.xml_typecode:
                        # Element types are further keyed by the name test.
                        name_key = node_test.name_key
                        if name_key:
                            prefix, local = name_key
                            # Unprefixed names are in the null-namespace
                            try:
                                namespace = prefix and namespaces[prefix]
                            except KeyError:
                                raise XPathError(XPathError.UNDEFINED_PREFIX,
                                                 prefix=prefix)
                            else:
                                name_key = namespace, local
                        mode_table[type_key][name_key].append(info)
                    else:
                        # Every other node type gets lumped into a single list
                        # for that node type
                        mode_table[type_key].append(info)
            if name:
                # XSLT 1.0, Section 6, Paragraph 3:
                # It is an error if a stylesheet contains more than one
                # template with the same name and same import precedence.
                if name not in named_templates:
                    named_templates[name] = element
                elif named_templates[name].import_precedence == precedence:
                    # Construct a useful name for the error message.
                    namespace, name = name
                    if namespace:
                        name = element.namespaces[namespace] + ':' + name
                    raise XsltError(XsltError.DUPLICATE_NAMED_TEMPLATE, name)
        # Now expanded the tables and convert to regular dictionaries to
        # prevent inadvertant growth when non-existant keys are used.
        match_templates = self.match_templates = dict(match_templates)
        for mode, type_table in match_templates.iteritems():
            # Add those patterns that don't have a distinct type:
            #   node(), id() and key() patterns
            any_patterns = type_table[tree.node.xml_typecode]
            type_table = match_templates[mode] = dict(type_table)
            for type_key, patterns in type_table.iteritems():
                if type_key == tree.element.xml_typecode:
                    # Add those that are wildcard tests ('*' and 'prefix:*')
                    wildcard_names = patterns[None]
                    name_table = type_table[type_key] = dict(patterns)
                    for name_key, patterns in name_table.iteritems():
                        if name_key is not None:
                            patterns.extend(wildcard_names)
                        patterns.extend(any_patterns)
                        patterns.sort(reverse=True)
                        name_table[name_key] = tuple(patterns)
                else:
                    patterns.extend(any_patterns)
                    patterns.sort(reverse=True)
                    type_table[type_key] = tuple(patterns)
        #self._dump_match_templates(match_templates)
        return