1 #!/usr/bin/env python 2 3 """ 4 XSL-based form templating. 5 """ 6 7 import Constants 8 import libxslt 9 10 """ 11 import libxml2 12 13 def quiet(context, s): 14 pass 15 16 libxml2.registerErrorHandler(quiet, None) 17 """ 18 19 def path_to_node(node, attribute_ref, name, multivalue=0): 20 21 """ 22 Generate an XSLForms path to the given 'node', producing an attribute 23 reference if 'attribute_ref' is true; for example: 24 25 /package#1/discriminators#5/discriminator#1/category 26 27 Otherwise an element reference is produced; for example: 28 29 /package#1/discriminators#5/discriminator#1 30 31 Use the given 'name' to complete the path if an attribute reference is 32 required (and if a genuine attribute is found at the context node - 33 otherwise 'name' will be None and the context node will be treated like an 34 attribute). 35 36 If 'multivalue' is true, produce an attribute reference using the given 37 'name' of the following form: 38 39 /package#1/categories#1/category##name 40 """ 41 42 l = [] 43 # Skip attribute reference. 44 if node.type == "attribute": 45 node = node.parent 46 # Manually insert the attribute name if defined. 47 if attribute_ref: 48 # A real attribute is referenced. 49 if name is not None: 50 l.insert(0, name) 51 if multivalue: 52 l.insert(0, Constants.pair_separator) 53 l.insert(0, Constants.pair_separator) 54 l.insert(0, node.name) 55 node = node.parent 56 l.insert(0, Constants.path_separator) 57 # Otherwise, treat the element name as an attribute name. 58 else: 59 l.insert(0, node.name) 60 l.insert(0, Constants.path_separator) 61 node = node.parent 62 # Element references. 63 while node is not None and node.type != "document_xml": 64 l.insert(0, str(int(node.xpathEval("count(preceding-sibling::*) + 1")))) 65 l.insert(0, Constants.pair_separator) 66 l.insert(0, node.name) 67 l.insert(0, Constants.path_separator) 68 node = node.parent 69 return "".join(l) 70 71 def path_to_context(context, attribute_ref, multivalue_name=None): 72 73 """ 74 As a libxslt extension function, return a string containing the XSLForms 75 path to the 'context' node, using the special "this-name" variable to 76 complete the path if an attribute reference is required (as indicated by 77 'attribute_ref' being set to true). If 'multivalue_name' is set, produce a 78 reference to a multivalued field using the given string as the attribute 79 name. 80 """ 81 82 pctxt = libxslt.xpathParserContext(_obj=context) 83 context = pctxt.context() 84 node = context.contextNode() 85 transform_context = context.transformContext() 86 name_var = transform_context.variableLookup("this-name", None) 87 if multivalue_name is not None: 88 name = multivalue_name 89 multivalue = 1 90 elif name_var is not None: 91 name = name_var[0].content 92 multivalue = 0 93 else: 94 name = None 95 multivalue = 0 96 return path_to_node(node, attribute_ref, name, multivalue) 97 98 def this_position(context): 99 return path_to_context(context, 0) 100 101 def field_name(context): 102 return path_to_context(context, 1) 103 104 def multi_field_name(context, multivalue_name): 105 return path_to_context(context, 1, multivalue_name) 106 107 def new_field(context, name): 108 return path_to_context(context, 0) + "/" + name 109 110 libxslt.registerExtModuleFunction("this-position", "http://www.boddie.org.uk/ns/xmltools/template", this_position) 111 libxslt.registerExtModuleFunction("field-name", "http://www.boddie.org.uk/ns/xmltools/template", field_name) 112 libxslt.registerExtModuleFunction("multi-field-name", "http://www.boddie.org.uk/ns/xmltools/template", multi_field_name) 113 libxslt.registerExtModuleFunction("new-field", "http://www.boddie.org.uk/ns/xmltools/template", new_field) 114 115 # vim: tabstop=4 expandtab shiftwidth=4