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
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
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
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
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, []
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'
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)
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)
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
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
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)
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)
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
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)
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
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
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
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
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))
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
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))
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
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
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
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)
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
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
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
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