class GenericVariableElement(XsltElement): category = None content = ContentInfo.Template legalAttrs = { 'name': AttributeInfo.QName(required=1), 'select': AttributeInfo.Expression(), 'f:node-set': AttributeInfo.YesNoAvt(default='no'), } doesSetup = True def setup(self): # Check for deprecated f:node-set if (FT_EXT_NAMESPACE, 'node-set') in self.attributes: warnings.warn( "You are using the deprecated f:node-set attribute" " on xsl:variable or xsl:param. Please switch to" " using exslt:node-set", DeprecationWarning, 2) # check for a bad binding if self._select and self.children: raise XsltException(Error.VAR_WITH_CONTENT_AND_SELECT, self._name) # See the bottom of this file for these helper "nodes" binding_save = self.parent.children[0] if not isinstance(binding_save, PushVariablesNode): # varBindings not yet saved for this level in the stylesheet tree parent = self.parent binding_save = PushVariablesNode(parent.root, parent.baseUri) parent.insertChild(0, binding_save) parent.root.primeInstructions.append(binding_save) return def instantiate(self, context, processor): # NOTE: all we want to do is change the varBindings context.processorNss = self.namespaces context.currentInstruction = self if self._select: result = self._select.evaluate(context) elif self.children: #This used to be a try with the popResult() in the finally. #But this would cause masking of underlying exceptions triggered #in variable bodies. See #http://lists.fourthought.com/pipermail/4suite-dev/2003-March/001236.html processor.pushResultTree(self.baseUri) for child in self.children: child.instantiate(context, processor) result = processor.popResult() # Why is the check for childNodes necessary? This will always # be an RTF. if self.attributes.get((FT_EXT_NAMESPACE, 'node-set')) == 'yes' \ and hasattr(result, 'childNodes'): result = [result] else: result = u"" context.varBindings[self._name] = result return
class NamespaceAliasElement(XsltElement): category = CategoryTypes.TOP_LEVEL_ELEMENT content = ContentInfo.Empty legalAttrs = { 'stylesheet-prefix' : AttributeInfo.Prefix(required=1), 'result-prefix' : AttributeInfo.Prefix(required=1), }
class ElementElement(XsltElement): category = CategoryTypes.INSTRUCTION content = ContentInfo.Template legalAttrs = { 'name': AttributeInfo.RawQNameAvt(required=1), 'namespace': AttributeInfo.UriReferenceAvt(isNsName=1), 'use-attribute-sets': AttributeInfo.QNames(), } def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self (prefix, local) = self._name.evaluate(context) if prefix is not None: name = prefix + u':' + local else: name = local # From sec. 7.1.2 of the XSLT spec, # 1. if 'namespace' attr is not present, use ns in scope, based on prefix # from the element QName in the 'name' attr value; if no prefix, use # default ns in scope # 2. if 'namespace' attr is present and empty string, use empty ns ALWAYS # 3. if 'namespace' attr is present, namespace is attr value # if not self._namespace: if prefix is not None: if not self.namespaces.has_key(prefix): raise XsltRuntimeException(Error.UNDEFINED_PREFIX, self, prefix) namespace = self.namespaces[prefix] else: namespace = self.namespaces[None] else: namespace = (self._namespace and self._namespace.evaluate(context) or EMPTY_NAMESPACE) self.execute(context, processor, name, namespace) return def execute(self, context, processor, name, namespace): #FIXME: Use proper pysax AttributeList objects processor.writers[-1].startElement(name, namespace) for attr_set_name in self._use_attribute_sets: try: attr_set = processor.attributeSets[attr_set_name] except KeyError: raise XsltRuntimeException(Error.UNDEFINED_ATTRIBUTE_SET, self, attr_set_name) attr_set.instantiate(context, processor) for child in self.children: child.instantiate(context, processor) processor.writers[-1].endElement(name, namespace) return
class KeyElement(XsltElement): category = CategoryTypes.TOP_LEVEL_ELEMENT content = ContentInfo.Empty legalAttrs = { 'name' : AttributeInfo.QName(required=1), 'match' : AttributeInfo.Pattern(required=1), 'use' : AttributeInfo.Expression(required=1), } def getKeyInfo(self): return (self._name, (self._match, self._use, self.namespaces))
class AttributeElement(XsltElement): category = CategoryTypes.INSTRUCTION content = ContentInfo.Template legalAttrs = { 'name': AttributeInfo.RawQNameAvt(required=1), 'namespace': AttributeInfo.UriReferenceAvt(isNsName=1), } def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self (prefix, local) = self._name.evaluate(context) if prefix is not None: name = prefix + ':' + local else: name = local if name == 'xmlns': raise XsltRuntimeException(Error.BAD_ATTRIBUTE_NAME, self, name) # From sec. 7.1.3 of the XSLT spec, # 1. if 'namespace' attr is not present, use ns in scope, based on prefix # from the element QName in the 'name' attr value; if no prefix, use # empty namespace # 2. if 'namespace' attr is present and empty string, use empty ns ALWAYS # 3. if 'namespace' attr is present, namespace is attr value # if not self._namespace: if prefix is not None: if not self.namespaces.has_key(prefix): raise XsltRuntimeException(Error.UNDEFINED_PREFIX, self, prefix) namespace = self.namespaces[prefix] else: namespace = EMPTY_NAMESPACE else: namespace = (self._namespace and self._namespace.evaluate(context) or EMPTY_NAMESPACE) processor.pushResultString() had_nontext = 0 try: for child in self.children: child.instantiate(context, processor) if processor.writers[-1].had_nontext: had_nontext = 1 finally: if had_nontext: raise XsltRuntimeException(Error.NONTEXT_IN_ATTRIBUTE, self) content = processor.popResult() processor.writers[-1].attribute(name, content, namespace) return
class CreateIndexElement(XsltElement): """ f:create-index allows one to create an arbitrary key at run time using any node data. It is similar to xsl:key, except that it is computed on demand at run-time, and uses an XPath selection rather than an XSLT match, which gives more flexibility over what is indexed. These keys can be accessed using the extension function f:lookup(). Avoid making a dynamic index have the same name as a proper xsl:key. In particular this will confuse tools such as the <f:dump-keys/> diagnostic extension. """ legalAttrs = { 'name': AttributeInfo.StringAvt(required=1, description='The name of the key to create'), 'select': AttributeInfo.Expression( required=1, description='Selects which nodes are to be indexed'), 'use': AttributeInfo.Expression( required=1, description= 'The expression that computes the index key value for each node'), } def instantiate(self, context, processor): selected = self._select.evaluate(context) name = self._name.evaluate(context) index = {} if not isinstance(selected, list): return state = context.copy() size = len(selected) pos = 1 for node in selected: context.node, context.position, context.size = node, pos, size context.currentNode = node key = Conversions.StringValue(self._use.evaluate(context)) if key not in index: index[key] = [] index[key].append(node) pos += 1 if (FT_EXT_NAMESPACE, 'indices') not in processor.extensionParams: processor.extensionParams[(FT_EXT_NAMESPACE, 'indices')] = {} processor.extensionParams[(FT_EXT_NAMESPACE, 'indices')][name] = index context.set(state) return
class WithParamElement(XsltElement): category = None content = ContentInfo.Template legalAttrs = { 'name' : AttributeInfo.QName(required=1), 'select' : AttributeInfo.Expression(), } doesSetup = 1 def setup(self): if not self._select: self._select = RtfExpr(self.children) return
class ImportElement(XsltElement): category = None content = ContentInfo.Empty legalAttrs = { 'href' : AttributeInfo.UriReference(required=1), }
class CopyOfElement(XsltElement): category = CategoryTypes.INSTRUCTION content = ContentInfo.Empty legalAttrs = { 'select': AttributeInfo.Expression(required=1), } def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self writer = processor.writers[-1] result = self._select.evaluate(context) if hasattr(result, "nodeType"): writer.copyNodes(result) elif type(result) == type([]): #FIXME: Should probably do a sort-doc-order first for child in result: writer.copyNodes(child) else: string = Conversions.StringValue(result) writer.text(string) return
class ProcessingInstructionElement(XsltElement): category = CategoryTypes.INSTRUCTION content = ContentInfo.Template legalAttrs = { 'name': AttributeInfo.NCNameAvt(required=1), } def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self target = self._name.evaluate(context) if target.lower() == 'xml': raise XsltRuntimeException(Error.ILLEGAL_XML_PI, self) processor.pushResultString() had_nontext = 0 try: for child in self.children: child.instantiate(context, processor) if processor.writers[-1].had_nontext: had_nontext = 1 finally: if had_nontext: raise XsltRuntimeException(Error.NONTEXT_IN_PI, self) content = processor.popResult() # Per the spec, PI data can't contain '?>', but we are allowed # to replace it with '? >' instead of signalling an error. data = content.replace(u'?>', u'? >') processor.writers[-1].processingInstruction(target, data) return
class RawTextOutputElement(XsltElement): """ Given a foreign XPath object, f:raw-text-output creates a text node based on the object, just like xsl:value-of with disable-output-escaping="yes". Unlike xsl:value-of, however, this element does not use the string-value of the object; it instead feeds the object directly to the current output writer. Therefore, unless a custom output writer is used, the object must be a Python Unicode string. The intent is to provide a way to serialize a Unicode string that may contain characters that are not permitted in an XPath string object. For example, another extension can convert raw binary data to a Unicode string, and then this extension can reserialize that string through the XSLT output stream, without risk of losing any data due to XPath's restrictions on string content. """ legalAttrs = { 'select': AttributeInfo.Expression( required=1, description= "An XPath expression that returns a Python Unicode object."), } def instantiate(self, context, processor): processor.writer.text(self._select.evaluate(context), escapeOutput=False) return
class MessageElement(XsltElement): category = CategoryTypes.INSTRUCTION content = ContentInfo.Template legalAttrs = { 'terminate': AttributeInfo.YesNo(default='no'), } def instantiate(self, context, processor): op = OutputParameters.OutputParameters() op.method = "xml" op.encoding = processor.writers[-1]._outputParams.encoding op.omitXmlDeclaration = 1 stream = cStringIO.StringIO() processor.pushResult(XmlWriter.XmlWriter(op, stream)) try: for child in self.children: child.instantiate(context, processor) finally: processor.popResult() msg = stream.getvalue() if self._terminate: raise XsltRuntimeException(Error.STYLESHEET_REQUESTED_TERMINATION, self, msg) else: processor.xslMessage(msg) return
class AttributeSetElement(XsltElement): category = CategoryTypes.TOP_LEVEL_ELEMENT content = ContentInfo.Rep(ContentInfo.QName(XSL_NAMESPACE, 'xsl:attribute')) legalAttrs = { 'name': AttributeInfo.QName(required=1), 'use-attribute-sets': AttributeInfo.QNames(), } doesPrime = 1 def prime(self, processor, context): processor.attributeSets[self._name] = self return def instantiate(self, context, processor, used=None): if used is None: used = [] if self in used: raise XsltRuntimeException(Error.CIRCULAR_ATTRIBUTE_SET, self, self._name) else: used.append(self) old_vars = context.varBindings context.varBindings = processor.stylesheet.getGlobalVariables() for attr_set_name in self._use_attribute_sets: try: attr_set = processor.attributeSets[attr_set_name] except KeyError: raise XsltRuntimeException(Error.UNDEFINED_ATTRIBUTE_SET, self, attr_set_name) attr_set.instantiate(context, processor, used) for child in self.children: child.instantiate(context, processor) context.varBindings = old_vars used.remove(self) return
class FtOutputElement(XsltElement): """ f:output is similar to xsl:output, but it allows you to compute the output parameters dynamically (as attribute value templates). Unlike xsl:output, this element is not expected to be empty; the output parameters apply only to the serialization of the element's content. """ content = ContentInfo.Template legalAttrs = { 'method': AttributeInfo.QNameAvt(), 'version': AttributeInfo.NMTokenAvt(), 'encoding': AttributeInfo.StringAvt(), 'omit-xml-declaration': AttributeInfo.YesNoAvt(), 'standalone': AttributeInfo.YesNoAvt(), 'doctype-public': AttributeInfo.StringAvt(), 'doctype-system': AttributeInfo.StringAvt(), 'cdata-section-elements': AttributeInfo.QNamesAvt(), 'indent': AttributeInfo.YesNoAvt(), 'media-type': AttributeInfo.StringAvt(), } def __init__(self, *args, **kwds): XsltElement.__init__(self, *args, **kwds) self._output_parameters = OutputParameters.OutputParameters() return def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self # this uses attributes directly from self self._output_parameters.avtParse(self, context) processor.addHandler(self._output_parameters, processor.writer.getStream()) try: for child in self.children: child.instantiate(context, processor) finally: processor.removeHandler() return
class SetupTranslationsElement(XsltElement): category = CategoryTypes.TOP_LEVEL_ELEMENT legalAttrs = { 'domain': AttributeInfo.StringAvt( required=1, description='The domain name of the message catalog'), 'localedir': AttributeInfo.StringAvt(description='The message catalog path'), } doesPrime = 1 #def instantiate(self, context, processor): def prime(self, processor, context): import gettext domain = self._domain.evaluate(context) localedir = self._localedir.evaluate(context) or None translations = gettext.translation(domain, localedir) processor.extensionParams[(FT_EXT_NAMESPACE, 'translations')] = translations return
class ValueOfElement(XsltElement): category = CategoryTypes.INSTRUCTION content = ContentInfo.Empty legalAttrs = { 'select': AttributeInfo.StringExpression(required=1), 'disable-output-escaping': AttributeInfo.YesNo(default='no'), } def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self text = Conversions.StringValue(self._select.evaluate(context)) if text: if self._disable_output_escaping: processor.writers[-1].text(text, escapeOutput=False) else: processor.writers[-1].text(text) return
class SystemElement(XsltElement): legalAttrs = { 'command': AttributeInfo.StringAvt(required=1), } def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self command = self._command.evaluate(context) os.system(command) return (context, )
class WhitespaceElement(XsltElement): category = CategoryTypes.TOP_LEVEL_ELEMENT content = ContentInfo.Empty legalAttrs = { 'elements': AttributeInfo.Tokens(required=1), } _strip_whitespace = None def getWhitespaceInfo(self): return (self._strip_whitespace, self._elements)
class ReplaceElement(XsltElement): """ f:replace performs a search and replace on a string, placing the results in the output. The content is treated as a template. The string value of the output from this template is the replacement string. All instances of the string given by the 'substring' attribute are replaced with the replacement string. """ legalAttrs = { 'string': AttributeInfo.StringExpression( description= "The string to be processed. If not given, the string value of the context node is used." ), 'substring': AttributeInfo.StringExpression( required=1, description="The sub-string to be replaced."), } def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self if self._string: value = self._string.evaluate(context) else: value = context.node string_ = Conversions.StringValue(value) substring = Conversions.StringValue(self._substring.evaluate(context)) writer = processor.writer for chunk in string_.split(substring): writer.text(chunk) for child in self.children: child.instantiate(context, processor) return
class MsgControlElement(XsltElement): """ f:msg-control provides, as a side effect, context-sensitive control over whether messages (i.e., those produced by xsl:message) and warnings are output by the processor. """ legalAttrs = { 'suppress': AttributeInfo.YesNoAvt( default='no', description="Disable display of all XSLT messages."), } def instantiate(self, context, processor): processor.messageControl(self._suppress.evaluate(context)) return
class FunctionElement(XsltElement): content = ContentInfo.Seq( ContentInfo.Rep(ContentInfo.QName(XSL_NAMESPACE, 'xsl:param')), ContentInfo.Template, ) legalAttrs = { 'name': AttributeInfo.QNameButNotNCName(required=1), } doesPrime = True def prime(self, processor, context): context.addFunction(self._name, self) return def __call__(self, context, *args): processor = context.processor # Save context state as XPath is side-effect free ctx_state = context.copy() ctx_namespaces = context.processorNss ctx_instruction = context.currentInstruction context.processorNss = self.namespaces context.currentInstruction = self # Set the parameter list counter = 0 self.result = u'' for child in self.children: if child.expandedName == (XSL_NAMESPACE, 'param'): if counter < len(args): context.varBindings[child._name] = args[counter] else: # default child.instantiate(context, processor) counter = counter + 1 else: child.instantiate(context, processor) # Restore context state context.currentInstruction = ctx_instruction context.processorNss = ctx_namespaces context.set(ctx_state) return self.result
class IfElement(XsltElement): category = CategoryTypes.INSTRUCTION content = ContentInfo.Template legalAttrs = { 'test': AttributeInfo.BooleanExpression(required=1), } def instantiate(self, context, processor, new_level=1): context.processorNss = self.namespaces context.currentInstruction = self if Conversions.BooleanValue(self._test.evaluate(context)): for child in self.children: child.instantiate(context, processor) return
class TextElement(XsltElement): category = CategoryTypes.INSTRUCTION content = ContentInfo.Text legalAttrs = { 'disable-output-escaping': AttributeInfo.YesNo(default='no'), } def instantiate(self, context, processor): if self.children: value = self.children[0].data if self._disable_output_escaping: processor.writers[-1].text(value, escapeOutput=False) else: processor.writers[-1].text(value) return
class FtApplyTemplates(ApplyTemplatesElement.ApplyTemplatesElement): """ The f:apply-templates element is an extension of the xsl:apply-templates element. It differs from xsl:apply-templates in the following way: The value of the mode attribute is an attribute value template rather than a static string. Thus, the mode can be computed at run time. """ legalAttrs = ApplyTemplatesElement.ApplyTemplatesElement.legalAttrs.copy() legalAttrs['mode'] = AttributeInfo.QNameAvt( description= "The mode to be used for template application. In this variation the mode is an AVT and thus can be computed at run time." ) def _instantiate_mode(self, context): return self._mode.evaluate(context)
class ResultElement(XsltElement): """ When an func:result element is instantiated, during the instantiation of a func:function element, the function returns with its value. """ content = ContentInfo.Template legalAttrs = { 'select': AttributeInfo.Expression(), } doesSetup = doesPrime = True def setup(self): if not self._select: self._select = RtfExpr(self.children) return def prime(self, processor, context): self._function = None current = self.parent while current: # this loop will stop when it hits the top of the tree if current.expandedName == (EXSL_FUNCTIONS_NS, 'function'): self._function = current break current = current.parent if not self._function: raise XsltRuntimeException(ExsltError.RESULT_NOT_IN_FUNCTION, self) if not self.isLastChild(): siblings = self.parent.children for node in siblings[siblings.index(self) + 1:]: if node.expandedName != (XSL_NAMESPACE, 'fallback'): raise XsltRuntimeException( ExsltError.ILLEGAL_RESULT_SIBLINGS, self) return def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self self._function.result = self._select.evaluate(context) return
class ForEachElement(XsltElement): category = CategoryTypes.INSTRUCTION content = ContentInfo.Seq( ContentInfo.Rep(ContentInfo.QName(XSL_NAMESPACE, 'xsl:sort')), ContentInfo.Template) legalAttrs = { 'select': AttributeInfo.NodeSetExpression(required=1), } doesSetup = 1 def setup(self): sort_keys = filter(lambda x: isinstance(x, SortElement), self.children) if sort_keys: self._select = SortedExpression(self._select, sort_keys) return def instantiate(self, context, processor): context.processorNss = self.namespaces context.currentInstruction = self if self._select: node_set = self._select.evaluate(context) if type(node_set) != type([]): raise XsltRuntimeException(Error.INVALID_FOREACH_SELECT, self) else: node_set = context.node.childNodes state = context.copy() pos = 1 size = len(node_set) for node in node_set: context.node, context.position, context.size = node, pos, size context.currentNode = node for child in self.children: child.instantiate(context, processor) pos += 1 context.set(state) return
class ChainToElement(XsltElement): """ f:chain-to tells the processor to apply the output of the current stylsheet as the input of another stylesheet, establishing a chain of transforms. The next stylesheet in the chain is specified using an AVT, which allows for dynamically constructed chains. Children can be xsl:with-param elements, in which case the specified values are passed on to the next stylesheet as top-level parameters Warning: if the href attribute is blank, it will chain back to this same stylesheet and could lead to an infinite loop. FIXME: Trap this condition """ legalAttrs = { 'href': AttributeInfo.UriReferenceAvt( required=1, description="The URI of the next stylesheet in the chain"), } content = ContentInfo.Rep( ContentInfo.QName(XSL_NAMESPACE, 'xsl:with-param')) def instantiate(self, context, processor): href = self._href.evaluate(context) params = {} for child in self.children: context.processorNss = child.namespaces context.currentInstruction = child params[child._name] = child._select.evaluate(context) base = self.baseUri processor.chainTo = processor.inputSourceFactory.resolver.normalize( href, base) processor.chainParams = params #print "chain to: %s, from base %s and href %s"%(processor.chainTo, base, href) return
class OutputElement(XsltElement): category = CategoryTypes.TOP_LEVEL_ELEMENT content = ContentInfo.Empty legalAttrs = { 'method' : AttributeInfo.QName(), 'version' : AttributeInfo.NMToken(), 'encoding' : AttributeInfo.String(), 'omit-xml-declaration' : AttributeInfo.YesNo(), 'standalone' : AttributeInfo.YesNo(), 'doctype-public' : AttributeInfo.String(), 'doctype-system' : AttributeInfo.String(), 'cdata-section-elements' : AttributeInfo.QNames(), 'indent' : AttributeInfo.YesNo(), 'media-type' : AttributeInfo.String(), 'f:utfbom' : AttributeInfo.YesNo( default='no', description='Whether to force output of a byte order mark (BOM). Usually used to generate a UTF-8 BOM. Do not use unless you\'re sure you know what you\'re doing'), }
class IncludeElement(XsltElement): category = CategoryTypes.TOP_LEVEL_ELEMENT content = ContentInfo.Empty legalAttrs = { 'href' : AttributeInfo.UriReference(required=1), }
class DecimalFormatElement(XsltElement): category = CategoryTypes.TOP_LEVEL_ELEMENT content = ContentInfo.Empty legalAttrs = { 'name' : AttributeInfo.QName(), 'decimal-separator' : AttributeInfo.Char(default='.'), 'grouping-separator' : AttributeInfo.Char(default=','), 'infinity' : AttributeInfo.String(default='Infinity'), 'minus-sign' : AttributeInfo.Char(default='-'), 'NaN' : AttributeInfo.String(default='NaN'), 'percent' : AttributeInfo.Char(default='%'), 'per-mille' : AttributeInfo.Char(default=unichr(0x2030)), 'zero-digit' : AttributeInfo.Char(default='0'), 'digit' : AttributeInfo.Char(default='#'), 'pattern-separator' : AttributeInfo.Char(default=';'), } def getFormatInfo(self): format = (self._decimal_separator, self._grouping_separator, self._infinity, self._minus_sign, self._NaN, self._percent, self._per_mille, self._zero_digit, self._digit, self._pattern_separator, ) return (self._name, format)