1 from __future__
import nested_scopes
7 # All changes to the DOM must go through here.
8 # Notification to views of changes is done.
10 from Ft
.Xml
.cDomlette
import implementation
, nonvalParse
11 from Ft
.Xml
.Domlette
import GetAllNs
12 from Ft
.Xml
import XMLNS_NAMESPACE
14 from xml
.dom
import Node
19 def get_xslt_source(doc
, dome_data
):
20 print "get_xslt_source", dome_data
21 src
= doc
.createElementNS(None, 'Source')
23 src
.appendChild(support
.import_with_ns(doc
, dome_data
.documentElement
))
27 def __init__(self
, path
, root_program
= None, dome_data
= None):
28 "If root_program is given, then no data is loaded (used for lock_and_copy)."
29 self
.uri
= 'Prog.dome'
32 from Ft
.Xml
.InputSource
import InputSourceFactory
33 isrc
= InputSourceFactory()
34 dome_data
= nonvalParse(isrc
.fromUri(dome_data
))
43 from Ft
.Xml
.InputSource
import InputSourceFactory
44 isrc
= InputSourceFactory()
45 doc
= nonvalParse(isrc
.fromUri(path
))
47 doc
= implementation
.createDocument(None, 'root', None)
48 root
= doc
.documentElement
50 self
.root_program
= None
53 from Program
import Program
, load_dome_program
55 if root
.namespaceURI
== constants
.DOME_NS
and root
.localName
== 'dome':
56 for x
in root
.childNodes
:
57 if x
.namespaceURI
== constants
.DOME_NS
:
58 if x
.localName
== 'dome-program':
59 self
.root_program
= load_dome_program(x
)
60 elif x
.localName
== 'dome-data':
61 for y
in x
.childNodes
:
62 if y
.nodeType
== Node
.ELEMENT_NODE
:
65 data_to_load
= dome_data
.documentElement
66 elif (root
.namespaceURI
== constants
.XSLT_NS
and
67 root
.localName
in ['stylesheet', 'transform']) or \
68 root
.hasAttributeNS(constants
.XSLT_NS
, 'version'):
70 self
.root_program
= xslt
.import_sheet(doc
)
71 x
= implementation
.createDocument(None, 'xslt', None)
72 data_to_load
= x
.documentElement
73 src
= get_xslt_source(x
, dome_data
)
74 data_to_load
.appendChild(x
.createElementNS(None, 'Result'))
75 data_to_load
.appendChild(src
)
81 self
.root_program
= root_program
83 if not self
.root_program
:
84 self
.root_program
= Program('Root')
87 self
.doc
= implementation
.createDocument(None, 'root', None)
89 node
= support
.import_with_ns(self
.doc
, data_to_load
)
90 self
.doc
.replaceChild(node
, self
.doc
.documentElement
)
93 self
.views
= [] # Notified when something changes
94 self
.locks
= {} # Node -> number of locks
97 # Pop an (op_number, function) off one of these and call the function to
98 # move forwards or backwards in the undo history.
103 # Each series of suboperations in an undo stack which are part of a single
104 # user op will have the same number...
108 #print "GC freed:", gc.collect()
109 #print "Garbage", gc.garbage
111 def lock(self
, node
):
112 """Prevent removal of this node (or any ancestor)."""
113 #print "Locking", node.nodeName
114 self
.locks
[node
] = self
.get_locks(node
) + 1
116 self
.lock(node
.parentNode
)
118 def unlock(self
, node
):
119 """Reverse the effect of lock(). Must call unlock the same number
120 of times as lock to fully unlock the node."""
121 l
= self
.get_locks(node
)
123 self
.locks
[node
] = l
- 1
125 del self
.locks
[node
] # Or get a memory leak...
126 if node
== self
.doc
.documentElement
:
128 self
.unlock(node
.parentNode
)
131 raise Exception('unlock(%s): Node not locked!' % node
)
133 self
.unlock(node
.parentNode
)
135 def get_locks(self
, node
):
137 return self
.locks
[node
]
141 def lock_and_copy(self
, node
):
142 """Locks 'node' in the current model and returns a new model
143 with a copy of the subtree."""
144 if self
.get_locks(node
):
145 raise Exception("Can't enter locked node!")
146 m
= Model(self
.get_base_uri(node
), root_program
= self
.root_program
)
147 copy
= support
.import_with_ns(m
.doc
, node
)
149 m
.replace_node(root
, copy
)
154 "Increment the user_op counter. Undo will undo every operation between"
159 "Return the true root node (not a view root)"
160 return self
.doc
.documentElement
162 def add_view(self
, view
):
164 "'update_all(subtree) - called when a major change occurs."
165 #print "New view:", view
166 self
.views
.append(view
)
168 def remove_view(self
, view
):
169 #print "Removing view", view
170 self
.views
.remove(view
)
171 #print "Now:", self.views
175 def update_all(self
, node
):
176 "Called when 'node' has been updated."
177 "'node' is still in the document, so deleting or replacing"
178 "a node calls this on the parent."
182 def update_replace(self
, old
, new
):
183 "Called when 'old' is replaced by 'new'."
185 v
.update_replace(old
, new
)
187 def strip_space(self
, node
= None):
189 node
= self
.doc
.documentElement
190 if node
.nodeType
== Node
.TEXT_NODE
:
191 node
.data
= string
.strip(node
.data
)
193 node
.parentNode
.removeChild(node
)
195 for k
in node
.childNodes
[:]:
200 def normalise(self
, node
):
201 old
= node
.cloneNode(1)
203 self
.add_undo(lambda: self
.replace_node(node
, old
))
204 self
.update_all(node
)
206 def convert_to_text(self
, node
):
207 assert node
.nodeType
== Node
.COMMENT_NODE
208 new
= self
.doc
.createTextNode(node
.data
)
209 self
.replace_node(node
, new
)
212 def remove_ns(self
, node
):
213 nss
= GetAllNs(node
.parentNode
)
214 dns
= nss
.get(None, None)
215 create
= node
.ownerDocument
.createElementNS
217 if node
.nodeType
!= Node
.ELEMENT_NODE
:
218 return node
.cloneNode(1)
219 new
= create(dns
, node
.nodeName
)
220 for a
in node
.attributes
.values():
221 if a
.localName
== 'xmlns' and a
.prefix
is None:
222 print "Removing xmlns attrib on", node
224 new
.setAttributeNS(a
.namespaceURI
, a
.name
, a
.value
)
226 for k
in node
.childNodes
:
227 new
.appendChild(ns_clone(k
))
230 self
.replace_node(node
, new
)
233 def set_name(self
, node
, namespace
, name
):
234 if self
.get_locks(node
):
235 raise Exception('Attempt to set name on locked node %s' % node
)
237 new
= node
.ownerDocument
.createElementNS(namespace
, name
)
238 self
.replace_shallow(node
, new
)
241 def replace_shallow(self
, old
, new
):
242 """Replace old with new, keeping the old children."""
243 assert not new
.childNodes
244 assert not new
.parentNode
246 old_name
= old
.nodeName
247 old_ns
= old
.namespaceURI
249 kids
= old
.childNodes
[:]
250 attrs
= old
.attributes
.values()
251 parent
= old
.parentNode
252 [ old
.removeChild(k
) for k
in kids
]
253 parent
.replaceChild(new
, old
)
254 [ new
.appendChild(k
) for k
in kids
]
255 [ new
.setAttributeNS(a
.namespaceURI
, a
.name
, a
.value
) for a
in attrs
]
257 self
.add_undo(lambda: self
.replace_shallow(new
, old
))
259 self
.update_replace(old
, new
)
262 if __main__
.no_gui_mode
:
263 def add_undo(self
, fn
):
266 def add_undo(self
, fn
):
267 self
.undo_stack
.append((self
.user_op
, fn
))
268 if not self
.doing_undo
:
271 def set_data(self
, node
, data
):
274 self
.add_undo(lambda: self
.set_data(node
, old_data
))
275 self
.update_all(node
)
277 def replace_node(self
, old
, new
):
278 if self
.get_locks(old
):
279 raise Exception('Attempt to replace locked node %s' % old
)
280 old
.parentNode
.replaceChild(new
, old
)
281 self
.add_undo(lambda: self
.replace_node(new
, old
))
283 self
.update_replace(old
, new
)
285 def delete_shallow(self
, node
):
286 """Replace node with its contents"""
287 kids
= node
.childNodes
[:]
288 next
= node
.nextSibling
289 parent
= node
.parentNode
290 for n
in kids
+ [node
]:
291 if self
.get_locks(n
):
292 raise Exception('Attempt to move/delete locked node %s' % n
)
294 self
.delete_internal(k
)
295 self
.delete_internal(node
)
297 self
.insert_before_interal(next
, k
, parent
)
298 self
.update_all(parent
)
300 def delete_nodes(self
, nodes
):
301 #print "Deleting", nodes
303 if self
.get_locks(n
):
304 raise Exception('Attempt to delete locked node %s' % n
)
307 self
.delete_internal(n
)
310 def delete_internal(self
, node
):
311 "Doesn't update display."
312 next
= node
.nextSibling
313 parent
= node
.parentNode
314 parent
.removeChild(node
)
315 self
.add_undo(lambda: self
.insert_before(next
, node
, parent
= parent
))
317 def insert_before_interal(self
, node
, new
, parent
):
318 "Insert 'new' before 'node'. If 'node' is None then insert at the end"
319 "of parent's children."
320 assert new
.nodeType
!= Node
.DOCUMENT_FRAGMENT_NODE
321 assert parent
.nodeType
== Node
.ELEMENT_NODE
322 parent
.insertBefore(new
, node
)
323 self
.add_undo(lambda: self
.delete_nodes([new
]))
326 if not self
.undo_stack
:
327 raise Exception('Nothing to undo')
329 assert not self
.doing_undo
331 uop
= self
.undo_stack
[-1][0]
333 # Swap stacks so that the undo actions will populate the redo stack...
334 (self
.undo_stack
, self
.redo_stack
) = (self
.redo_stack
, self
.undo_stack
)
337 while self
.redo_stack
and self
.redo_stack
[-1][0] == uop
:
338 self
.redo_stack
[-1][1]()
339 self
.redo_stack
.pop()
341 (self
.undo_stack
, self
.redo_stack
) = (self
.redo_stack
, self
.undo_stack
)
345 if not self
.redo_stack
:
346 raise Exception('Nothing to redo')
348 uop
= self
.redo_stack
[-1][0]
351 while self
.redo_stack
and self
.redo_stack
[-1][0] == uop
:
352 self
.redo_stack
[-1][1]()
353 self
.redo_stack
.pop()
357 def insert(self
, node
, new
, index
= 0):
358 if len(node
.childNodes
) > index
:
359 self
.insert_before(node
.childNodes
[index
], new
)
361 self
.insert_before(None, new
, parent
= node
)
363 def insert_after(self
, node
, new
):
364 self
.insert_before(node
.nextSibling
, new
, parent
= node
.parentNode
)
366 def insert_before(self
, node
, new
, parent
= None):
367 "Insert 'new' before 'node'. If 'node' is None then insert at the end"
368 "of parent's children."
370 parent
= node
.parentNode
371 if new
.nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
:
372 for n
in new
.childNodes
[:]:
373 self
.insert_before_interal(node
, n
, parent
)
375 self
.insert_before_interal(node
, new
, parent
)
376 self
.update_all(parent
)
378 def split_qname(self
, node
, name
):
380 namespaceURI
= XMLNS_NAMESPACE
383 prefix
, localName
= string
.split(name
, ':')
384 namespaceURI
= self
.prefix_to_namespace(node
, prefix
)
388 return namespaceURI
, localName
390 def set_attrib(self
, node
, name
, value
, with_update
= 1):
391 """Set an attribute's value. If value is None, remove the attribute.
392 Returns the new attribute node, or None if removing."""
393 namespaceURI
, localName
= self
.split_qname(node
, name
)
395 if node
.hasAttributeNS(namespaceURI
, localName
):
396 old
= node
.getAttributeNS(namespaceURI
, localName
)
399 #print "Set (%s,%s) = %s" % (namespaceURI, name, value)
401 node
.setAttributeNS(namespaceURI
, name
, value
)
403 node
.removeAttributeNS(namespaceURI
, localName
)
405 self
.add_undo(lambda: self
.set_attrib(node
, name
, old
))
408 self
.update_all(node
)
410 if localName
== 'xmlns':
412 return node
.attributes
[(namespaceURI
, localName
)]
414 def prefix_to_namespace(self
, node
, prefix
):
415 "Use the xmlns attributes to workout the namespace."
417 if nss
.has_key(prefix
):
418 return nss
[prefix
] or None
420 if prefix
== 'xmlns':
421 return XMLNS_NAMESPACE
422 raise Exception("No such namespace prefix '%s'" % prefix
)
426 def get_base_uri(self
, node
):
427 """Go up through the parents looking for a uri attribute.
428 If there isn't one, use the document's URI."""
430 if node
.nodeType
== Node
.DOCUMENT_NODE
:
432 if node
.hasAttributeNS(None, 'uri'):
433 return node
.getAttributeNS(None, 'uri')
434 node
= node
.parentNode