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
):
14 return 'Mode:' + mode
[1]
18 def import_sheet(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')
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
51 reader
= StylesheetReader()
52 sheet
= reader
.fromDocument(doc
)
58 # sheet.matchTemplates is { mode -> { type -> { (ns, name) -> [match] for elements
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...
71 for mode
in sheet
.matchTemplates
.keys():
72 mode_name
= mode_prog_name(mode
)
73 prog
= Program(mode_name
)
75 tests
= prog
.code
.start
77 types
= sheet
.matchTemplates
[mode
]
79 for type in types
.keys():
80 if type == Node
.ELEMENT_NODE
:
81 templates
= types
[type].values()
83 templates
= [types
[type]]
87 name
= pattern
.replace('/', '%')
88 temp
= Program(`i`
+ '-' + name
)
89 op
= add(temp
.code
.start
, 'mark_switch')
90 make_template(op
, t
[2])
94 if pattern
.startswith('/'):
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
]
119 def add(op
, *action
):
120 new
= Op(action
= action
)
121 op
.link_to(new
, 'next')
124 # A template is instantiated by running its program.
126 # => Cursor = result parent (append here)
127 # Mark = context node
129 # <= Cursor is undefined
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()
150 sub
= add(sub
, 'mark_switch')
151 # Ugly hack... global *|text() doesn't select in document order
153 sub
= add(sub
, 'do_global', `child
._select`
)
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')
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')
175 print "Unknown template type", child
, "(%s)" % child
.__class
__