def variable_element(tagname, namespaces, attributes): # required `name` attribute try: name = attributes[None, 'name'] except KeyError: raise XUpdateError(XUpdateError.MISSING_REQUIRED_ATTRIBUTE, element=tagname, attribute='name') else: if not isqname(name): raise XUpdateError(XUpdateError.INVALID_QNAME_ATTR, attribute='name', value=name) prefix, name = splitqname(name) if prefix: try: namespace = namespaces[prefix] except KeyError: raise XUpdateError(XUpdateError.UNDEFINED_PREFIX, prefix=prefix) else: namespace = None name = (namespace, name) # optional `select` attribute if (None, 'select') in attributes: select = attributes[None, 'select'] try: select = parse_expression(select) except XPathError, error: raise XUpdateError(XUpdateError.SYNTAX_ERROR, expression=select, text=str(error))
def start_element(self, name, namespace=None, namespaces=None, attributes=None): """ attributes must be a mapping from (name, namespace) to value. namespace can be None """ self._complete_element() if self._need_doctype: self._printer.doctype(name, self.output_parameters.doctype_public, self.output_parameters.doctype_system) self._need_doctype = False self._element_name = name self._element_namespace = namespace prefix, local = xmlstring.splitqname(name) # Update in-scope namespaces inscope_namespaces = self._namespaces[-1].copy() if namespaces: inscope_namespaces.update(namespaces) inscope_namespaces[prefix] = namespace self._namespaces.append(inscope_namespaces) if attributes: for (name, namespace), value in attributes.iteritems(): self.attribute(name, value, namespace) return
def instantiate(self, context): context.push_string_writer(errors=False) for primitive in self: primitive.instantiate(context) writer = context.pop_writer() name = writer.get_result() prefix, local = splitqname(name) if prefix: namespace = self.namespaces[prefix] else: namespace = None context.namespaces = self.namespaces targets = self.select.evaluate_as_nodeset(context) if not targets: raise XUpdateError(XUpdateError.INVALID_SELECT) for target in targets: parent = target.xml_parent if target.xml_type == tree.attribute.xml_type: parent.xml_attributes[namespace, name] = target.xml_value elif target.xml_type == tree.processing_instruction.xml_type: pi = tree.processing_instruction(name, target.xml_data) parent.xml_replace-child(pi, target) elif target.xml_type == tree.element.xml_type: #FIXME: Use regular constructor. No more DOM factory element = tree.element(namespace, name) # Copy any existing attributes to the newly created element if target.xml_attributes: for (ns, qname), value in target.xml_attributes.iteritems(): element.xml_attributes[ns, qname] = value # Now copy any children as well while target.xml_first_child: element.xml_append(target.xml_first_child) parent.xml_replace(target, element) 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) return splitqname(value)
def instantiate(self, context): context.namespaces = self.namespaces name = self.name.evaluate_as_string(context) if self.namespace: namespace = self.namespace.evaluate_as_string(context) else: prefix, local = splitqname(name) try: namespace = self.namespaces[prefix] except KeyError: if not prefix: prefix = '#default' raise XUpdateError(XUpdateError.UNDEFINED_PREFIX, prefix=prefix) context.start_element(name, namespace) for child in self: child.instantiate(context) context.end_element(name, namespace) 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 instantiate(self, context): context.namespaces = self.namespaces name = self.name.evaluate_as_string(context) if self.namespace: namespace = self.namespace.evaluate_as_string(context) else: prefix, local = splitqname(name) if prefix: try: namespace = self.namespaces[prefix] except KeyError: raise XUpdateError(XUpdateError.UNDEFINED_PREFIX, prefix=prefix) else: namespace = None context.push_string_writer(errors=False) for child in self: child.instantiate(context) writer = context.pop_writer() context.attribute(name, writer.get_result(), namespace) return
def attribute(self, name, value, namespace=None): """ add an attribute to an element name - the qualified name of the attribute value - the attribute value: must be Unicode namespace - must be Unicode or None (the default) Strives for "sanity". For brilliant definition thereof, c.f. Joe English http://lists.xml.org/archives/xml-dev/200204/msg00170.html Uses terminology from that article See also discussions starting http://lists.fourthought.com/pipermail/4suite-dev/2003-March/001294.html http://lists.fourthought.com/pipermail/4suite-dev/2003-March/001283.html Note: attribute output is computed as invoked. This means that the ugly case attribute(u"foo", u"bar", "http://some-ns/") attribute(u"x:foo", u"baz", "http://some-ns/") will result in the ugly xmlns:org.4suite.4xslt.ns0="http://some-ns/" org.4suite.4xslt.ns0:foo="baz" The user can easily correct this by reversing the order of the calls """ if not self._element_name: if self._need_doctype: raise WriterError(WriterError.ATTRIBUTE_ADDED_TO_NON_ELEMENT) else: raise WriterError(WriterError.ATTRIBUTE_ADDED_TOO_LATE) prefix, local = xmlstring.splitqname(name) if namespace is None: name = local else: # The general approach is as follows: # - If the new namespace/prefix combo is unique in the scope, add # it as is. # # - If the prefix is new, but the namespace already present, avoid # psychosis by reusing the existing namespace (even if it means # putting a formerly prefixed node into defaulted namespace form). # Note that this can cause effective non-conformance in some cases # because the XSLT spec says that all namespace nodes must be # copied to the reslt tree (even if this causes psychosis). # There is no mandate that all ns nodes must be manifestd as # matching NS Decls in the serialization, but if the output is # to result tree fragment, the required ns nodes will simply # disappear. # # - If the prefix exists, but with a different namespace, generate # a new (and probably rather ugly) prefix. namespaces = self._namespaces[-1] if not prefix or prefix == 'xmlns': # Find an existing namespace/prefix pair for prefix, inscope_namespace in namespaces.iteritems(): if prefix and inscope_namespace == namespace: break else: # Generate a new prefix template = self.GENERATED_PREFIX for suffix in itertools.count(): prefix = template % suffix if prefix not in namespaces: break namespaces[prefix] = namespace elif prefix not in namespaces: # Find an existing namespace/prefix pair for inscope_prefix, inscope_namespace in namespaces.iteritems( ): if inscope_prefix and inscope_namespace == namespace: prefix = inscope_prefix break else: # Use given namespace/prefix pair namespaces[prefix] = namespace elif namespaces[prefix] != namespace: # An existing prefix/namespace pair that doesn't match what # we're trying to use. First, try to reuse an existing namespace # declaration. for prefix, inscope_namespace in namespaces.iteritems(): if prefix and inscope_namespace == namespace: break else: # Generate a new prefix template = self.GENERATED_PREFIX for suffix in itertools.count(): prefix = template % suffix if prefix not in namespaces: break namespaces[prefix] = namespace # Generate a new node name assert prefix, "'prefix' required for non-null namespace" name = prefix + ':' + local self._attributes[namespace, local] = (name, value) return
def qname_type(namespace, name): prefix, local = splitqname(name) return ContentModel(ContentModel.TYPE_NAME, (namespace, local), label=name)
def attribute(self, name, value, namespace=None): """ add an attribute to an element name - the qualified name of the attribute value - the attribute value: must be Unicode namespace - must be Unicode or None (the default) Strives for "sanity". For brilliant definition thereof, c.f. Joe English http://lists.xml.org/archives/xml-dev/200204/msg00170.html Uses terminology from that article See also discussions starting http://lists.fourthought.com/pipermail/4suite-dev/2003-March/001294.html http://lists.fourthought.com/pipermail/4suite-dev/2003-March/001283.html Note: attribute output is computed as invoked. This means that the ugly case attribute(u"foo", u"bar", "http://some-ns/") attribute(u"x:foo", u"baz", "http://some-ns/") will result in the ugly xmlns:org.4suite.4xslt.ns0="http://some-ns/" org.4suite.4xslt.ns0:foo="baz" The user can easily correct this by reversing the order of the calls """ if not self._element_name: if self._need_doctype: raise WriterError(WriterError.ATTRIBUTE_ADDED_TO_NON_ELEMENT) else: raise WriterError(WriterError.ATTRIBUTE_ADDED_TOO_LATE) prefix, local = xmlstring.splitqname(name) if namespace is None: name = local else: # The general approach is as follows: # - If the new namespace/prefix combo is unique in the scope, add # it as is. # # - If the prefix is new, but the namespace already present, avoid # psychosis by reusing the existing namespace (even if it means # putting a formerly prefixed node into defaulted namespace form). # Note that this can cause effective non-conformance in some cases # because the XSLT spec says that all namespace nodes must be # copied to the reslt tree (even if this causes psychosis). # There is no mandate that all ns nodes must be manifestd as # matching NS Decls in the serialization, but if the output is # to result tree fragment, the required ns nodes will simply # disappear. # # - If the prefix exists, but with a different namespace, generate # a new (and probably rather ugly) prefix. namespaces = self._namespaces[-1] if not prefix or prefix == 'xmlns': # Find an existing namespace/prefix pair for prefix, inscope_namespace in namespaces.iteritems(): if prefix and inscope_namespace == namespace: break else: # Generate a new prefix template = self.GENERATED_PREFIX for suffix in itertools.count(): prefix = template % suffix if prefix not in namespaces: break namespaces[prefix] = namespace elif prefix not in namespaces: # Find an existing namespace/prefix pair for inscope_prefix, inscope_namespace in namespaces.iteritems(): if inscope_prefix and inscope_namespace == namespace: prefix = inscope_prefix break else: # Use given namespace/prefix pair namespaces[prefix] = namespace elif namespaces[prefix] != namespace: # An existing prefix/namespace pair that doesn't match what # we're trying to use. First, try to reuse an existing namespace # declaration. for prefix, inscope_namespace in namespaces.iteritems(): if prefix and inscope_namespace == namespace: break else: # Generate a new prefix template = self.GENERATED_PREFIX for suffix in itertools.count(): prefix = template % suffix if prefix not in namespaces: break namespaces[prefix] = namespace # Generate a new node name assert prefix, "'prefix' required for non-null namespace" name = prefix + ':' + local self._attributes[namespace, local] = (name, value) return