1 #!/usr/bin/env python 2 3 """ 4 XSL-based form templating. 5 """ 6 7 import Constants 8 import libxslt 9 import libxml2 10 11 """ 12 import libxml2 13 14 def quiet(context, s): 15 pass 16 17 libxml2.registerErrorHandler(quiet, None) 18 """ 19 20 def path_to_node(node, attribute_ref, name, multivalue=0): 21 22 """ 23 Generate an XSLForms path to the given 'node', producing an attribute 24 reference if 'attribute_ref' is true; for example: 25 26 /package#1/discriminators#5/discriminator#1/category 27 28 Otherwise an element reference is produced; for example: 29 30 /package#1/discriminators#5/discriminator#1 31 32 Use the given 'name' to complete the path if an attribute reference is 33 required (and if a genuine attribute is found at the context node - 34 otherwise 'name' will be None and the context node will be treated like an 35 attribute). 36 37 If 'multivalue' is true, produce an attribute reference using the given 38 'name' of the following form: 39 40 /package#1/categories#1/category##name 41 """ 42 43 l = [] 44 # Skip attribute reference. 45 if node.type == "attribute": 46 node = node.parent 47 # Manually insert the attribute name if defined. 48 if attribute_ref: 49 # A real attribute is referenced. 50 if name is not None: 51 l.insert(0, name) 52 if multivalue: 53 l.insert(0, Constants.multi_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 def other_field_name(context, nodes): 111 node = libxml2.xmlNode(nodes[0]) 112 return path_to_node(node, 0, None, 0) 113 114 def other_multi_field_name(context, multivalue_name, nodes): 115 node = libxml2.xmlNode(nodes[0]) 116 return path_to_node(node, 1, multivalue_name, 1) 117 118 libxslt.registerExtModuleFunction("this-position", "http://www.boddie.org.uk/ns/xmltools/template", this_position) 119 libxslt.registerExtModuleFunction("field-name", "http://www.boddie.org.uk/ns/xmltools/template", field_name) 120 libxslt.registerExtModuleFunction("multi-field-name", "http://www.boddie.org.uk/ns/xmltools/template", multi_field_name) 121 libxslt.registerExtModuleFunction("new-field", "http://www.boddie.org.uk/ns/xmltools/template", new_field) 122 libxslt.registerExtModuleFunction("other-field-name", "http://www.boddie.org.uk/ns/xmltools/template", other_field_name) 123 libxslt.registerExtModuleFunction("other-multi-field-name", "http://www.boddie.org.uk/ns/xmltools/template", other_multi_field_name) 124 125 def get_field_name(field_or_multi_name): 126 return field_or_multi_name.split(Constants.multi_separator)[0] 127 128 def get_element_path(field_or_multi_name): 129 130 """ 131 Convert the given 'field_or_multi_name' back to an XPath reference. 132 For example: 133 /configuration#1/base-system##value -> /configuration[1]/base-system 134 """ 135 136 field_name = get_field_name(field_or_multi_name) 137 parts = field_name.split(Constants.path_separator) 138 new_parts = [] 139 for part in parts: 140 path_parts = part.split(Constants.pair_separator) 141 if len(path_parts) == 2: 142 new_parts.append(path_parts[0] + "[" + path_parts[1] + "]") 143 else: 144 new_parts.append(path_parts[0]) 145 return "/".join(new_parts) 146 147 # vim: tabstop=4 expandtab shiftwidth=4