Fixed version number (for 0release)
[dom-editor.git] / Dome / xslt.py
blob8add8eab22f13e18098b2209ded98a677c6eb093
1 from constants import XSLT_NS
2 from xml.dom import Node
4 from Ft.Xml.Xslt.StylesheetReader import StylesheetReader
5 from Ft.Xml.Xslt.StylesheetTree import XsltElement, XsltText
6 from Ft.Xml.Xslt.LiteralElement import LiteralElement
7 from Ft.Xml.Xslt.ApplyTemplatesElement import ApplyTemplatesElement
8 from Ft.Xml.Xslt.ValueOfElement import ValueOfElement
9 from Ft.Xml.Xslt.CopyElement import CopyElement
10 from Program import Program, Op, Block
12 def mode_prog_name(mode):
13 if mode:
14 return 'Mode:' + mode[1]
15 else:
16 return 'Default mode'
18 def import_sheet(doc):
19 #print "Import!", doc
21 root = Program('XSLT')
23 # The root program puts the mark on the Result node and the cursor on the Source.
24 # It then runs the program for the default mode. There is one program for each mode, and
25 # it acts as a dispatcher. It finds a template which matches the cursor node and runs
26 # the program for that template.
28 op = add(root.code.start, 'do_search', '/xslt/Result')
29 op = add(op, 'mark_selection')
30 op = add(op, 'do_search', '/xslt/Source')
31 op = add(op, 'play', 'XSLT/Default mode')
33 # This program copies a text node to the output
34 prog = Program('DefaultText')
35 root.add_sub(prog)
36 op = add(prog.code.start, 'yank')
37 op = add(op, 'mark_switch')
38 op = add(op, 'put_as_child_end')
39 op = add(op, 'move_left')
40 op = add(op, 'mark_switch')
42 # To start with, the cursor is on the source document node and
43 # the mark is on the result document node.
45 # The mode program is called with:
46 # => Cursor = context node
47 # Mark = result parent (append children here)
48 # <= Cursor is undefined
49 # Mark is unchanged
51 reader = StylesheetReader()
52 sheet = reader.fromDocument(doc)
54 global s
55 s = sheet
57 # sheet.matchTemplates is { mode -> { type -> { (ns, name) -> [match] for elements
58 # { [match] otherwise
60 # Each match is ((precedence,?), pattern, axis_type, TemplateElement)
62 # The list of matches is sorted; use the first that matches. Multiple lookups
63 # may be required (eg, lookup 'html' then None (for 'node()' and '*')).
64 # Patterns like 'text()|comment()' are broken down into two match elements.
66 # XXX: Could have two modes with the same name but different namespaces...
68 i = 1
70 for mode in sheet.matchTemplates.keys():
71 mode_name = mode_prog_name(mode)
72 prog = Program(mode_name)
73 root.add_sub(prog)
74 tests = prog.code.start
75 print "Mode", mode
76 types = sheet.matchTemplates[mode]
78 #loose_ends = []
79 all = []
80 for type in types.keys():
81 if type == Node.ELEMENT_NODE:
82 templates = types[type].values()[:]
83 else:
84 templates = [types[type]]
86 # templates is a list of templates for items of this type when in this mode
88 for tl in templates:
89 for t in tl:
90 all.append(t)
92 all = all[:]
93 all.sort()
94 all.reverse() # highest numbers first
96 last = None
97 for sort_key, (unused_pattern, axis_type, template) in all:
98 pattern = `template._match`
100 if pattern == last:
101 continue
102 last = pattern
104 #print sort_key, pattern, axis_type, template
105 name = pattern.replace('/', '%')
106 temp = Program(`i` + '-' + name)
107 op = add(temp.code.start, 'mark_switch')
108 make_template(op, template)
109 i += 1
110 prog.add_sub(temp)
112 if pattern.startswith('/'):
113 if pattern == '/':
114 pattern = ''
115 pattern = '/xslt/Source' + pattern # XXX: Hack
116 tests = add(tests, 'xslt_fail_if', pattern)
117 op = Op(action = ['play', temp.get_path()])
118 tests.link_to(op, 'fail')
119 add(op, 'mark_switch')
120 #loose_ends.append(op)
121 # Now add the built-in rules
123 #print "Tidy", loose_ends
125 tests = add(tests, 'xslt_fail_if', 'text()')
126 op = Op(action = ['play', 'XSLT/DefaultText'])
127 tests.link_to(op, 'fail')
129 tests = add(tests, 'do_global', '*')
130 tests = add(tests, 'map', prog.get_path())
131 #tests = add(tests, 'mark_switch')
132 #tests = add(tests, 'mark_switch')
133 #[ op.link_to(tests, 'next') for op in loose_ends ]
135 root.modified = 0
136 return root
138 def add(op, *action):
139 new = Op(action = action)
140 op.link_to(new, 'next')
141 return new
143 # A template is instantiated by running its program.
145 # => Cursor = result parent (append here)
146 # Mark = context node
148 # <= Cursor is undefined
149 # Mark is unchanged
151 # Add the instructions to instantiate this template to 'op'.
152 def make_template(op, temp):
153 for child in temp.children:
154 if isinstance(child, XsltText):
155 #print "Text node", child.data
156 op = add(op, 'add_node', 'et', child.data)
157 op = add(op, 'move_left')
159 elif isinstance(child, LiteralElement):
160 #print "Element", child._output_qname
161 op = add(op, 'add_node', 'ee', child._output_qname)
162 for (qname, namespace, value) in child._output_attrs:
163 if len(value._parsedParts) != 1:
164 print "TODO: can't handle attrib", value
165 else:
166 xpath = `value._parsedParts[0]`
167 if value._plainParts:
168 # XXX: Do this propertly
169 xpath = 'concat("%s", %s)' % (value._plainParts[0], xpath)
170 print "XPath =", xpath
171 op = add(op, 'mark_switch')
172 op = add(op, 'xpath', xpath)
173 op = add(op, 'mark_switch')
174 op = add(op, 'add_attrib', None, qname)
175 op = add(op, 'put_replace')
177 op = make_template(op, child)
178 op = add(op, 'move_left')
179 elif isinstance(child, ApplyTemplatesElement):
180 block = Block(op.parent)
181 block.toggle_restore()
182 sub = block.start
184 sub = add(sub, 'mark_switch')
185 # Ugly hack... global *|text() doesn't select in document order
186 if child._select:
187 sub = add(sub, 'do_global', `child._select.expression`)
188 else:
189 sub = add(sub, 'select_children')
190 sub = add(sub, 'map', 'XSLT/' + mode_prog_name(child._mode))
191 sub = add(sub, 'mark_switch')
193 op.link_to(block, 'next')
194 op = block
195 elif isinstance(child, ValueOfElement):
196 op = add(op, 'mark_switch')
197 op = add(op, 'xpath', `child._select.expression`)
198 op = add(op, 'mark_switch')
199 op = add(op, 'put_as_child_end')
200 op = add(op, 'move_left')
201 elif isinstance(child, CopyElement):
202 op = add(op, 'mark_switch')
203 op = add(op, 'shallow_yank')
204 op = add(op, 'mark_switch')
205 op = add(op, 'put_as_child_end')
206 op = make_template(op, child)
207 op = add(op, 'move_left')
208 else:
209 print "Unknown template type", child, "(%s)" % child.__class__
210 return op