Added select_children. Hack for XSLT, but useful anyway.
[dom-editor.git] / Dome / xslt.py
blob820c8cf51fc325fa12ec425f63917eea7f79059d
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
56 print sheet
58 # sheet.matchTemplates is { mode -> { type -> { (ns, name) -> [match] for elements
59 # { [match] otherwise
61 # Each match is (pattern, axis_type, TemplateElement)
63 # The list of matches is sorted; use the first that matches. Multiple lookups
64 # may be required (eg, lookup 'html' then None (for 'node()' and '*')).
65 # Patterns like 'text()|comment()' are broken down into two match elements.
67 # XXX: Could have two modes with the same name but different namespaces...
69 i = 1
71 for mode in sheet.matchTemplates.keys():
72 mode_name = mode_prog_name(mode)
73 prog = Program(mode_name)
74 root.add_sub(prog)
75 tests = prog.code.start
76 print "Mode", mode
77 types = sheet.matchTemplates[mode]
78 loose_ends = []
79 for type in types.keys():
80 if type == Node.ELEMENT_NODE:
81 templates = types[type].values()
82 else:
83 templates = [types[type]]
84 for tl in templates:
85 for t in tl:
86 pattern = `t[0]`
87 name = pattern.replace('/', '%')
88 temp = Program(`i` + '-' + name)
89 op = add(temp.code.start, 'mark_switch')
90 make_template(op, t[2])
91 i += 1
92 prog.add_sub(temp)
94 if pattern.startswith('/'):
95 if pattern == '/':
96 pattern = ''
97 pattern = '/xslt/Source' + pattern # XXX: Hack
98 tests = add(tests, 'fail_if', pattern)
99 op = Op(action = ['play', temp.get_path()])
100 tests.link_to(op, 'fail')
101 loose_ends.append(op)
102 # Now add the built-in rules
104 print "Tidy", loose_ends
106 tests = add(tests, 'fail_if', 'text()')
107 op = Op(action = ['play', 'XSLT/DefaultText'])
108 tests.link_to(op, 'fail')
110 tests = add(tests, 'do_global', '*')
111 tests = add(tests, 'map', prog.get_path())
112 tests = add(tests, 'mark_switch')
113 tests = add(tests, 'mark_switch')
114 [ op.link_to(tests, 'next') for op in loose_ends ]
116 root.modified = 0
117 return root
119 def add(op, *action):
120 new = Op(action = action)
121 op.link_to(new, 'next')
122 return new
124 # A template is instantiated by running its program.
126 # => Cursor = result parent (append here)
127 # Mark = context node
129 # <= Cursor is undefined
130 # Mark is unchanged
132 # Add the instructions to instantiate this template to 'op'.
133 def make_template(op, temp):
134 for child in temp.children:
135 if isinstance(child, XsltText):
136 print "Text node", child.data
137 op = add(op, 'add_node', 'et', child.data)
138 op = add(op, 'move_left')
140 elif isinstance(child, LiteralElement):
141 print "Element", child._output_qname
142 op = add(op, 'add_node', 'ee', child._output_qname)
143 op = make_template(op, child)
144 op = add(op, 'move_left')
145 elif isinstance(child, ApplyTemplatesElement):
146 block = Block(op.parent)
147 block.toggle_restore()
148 sub = block.start
150 sub = add(sub, 'mark_switch')
151 # Ugly hack... global *|text() doesn't select in document order
152 if child._select:
153 sub = add(sub, 'do_global', `child._select`)
154 else:
155 sub = add(sub, 'select_children')
156 sub = add(sub, 'map', 'XSLT/' + mode_prog_name(child._mode))
157 sub = add(sub, 'mark_switch')
159 op.link_to(block, 'next')
160 op = block
161 elif isinstance(child, ValueOfElement):
162 op = add(op, 'mark_switch')
163 op = add(op, 'xpath', `child._select`)
164 op = add(op, 'mark_switch')
165 op = add(op, 'put_as_child_end')
166 op = add(op, 'move_left')
167 elif isinstance(child, CopyElement):
168 op = add(op, 'mark_switch')
169 op = add(op, 'shallow_yank')
170 op = add(op, 'mark_switch')
171 op = add(op, 'put_as_child_end')
172 op = make_template(op, child)
173 op = add(op, 'move_left')
174 else:
175 print "Unknown template type", child, "(%s)" % child.__class__
176 return op