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
18 def get_xslt_source(doc
, dome_data
):
19 print "get_xslt_source", dome_data
20 src
= doc
.createElementNS(None, 'Source')
22 src
.appendChild(support
.import_with_ns(doc
, dome_data
.documentElement
))
26 def __init__(self
, path
, root_program
= None, dome_data
= None):
27 "If root_program is given, then no data is loaded (used for lock_and_copy)."
28 self
.uri
= 'Prog.dome'
31 from Ft
.Xml
.InputSource
import InputSourceFactory
32 isrc
= InputSourceFactory()
33 dome_data
= nonvalParse(isrc
.fromUri(dome_data
))
42 from Ft
.Xml
.InputSource
import InputSourceFactory
43 isrc
= InputSourceFactory()
44 doc
= nonvalParse(isrc
.fromUri(path
))
46 doc
= implementation
.createDocument(None, 'root', None)
47 root
= doc
.documentElement
49 self
.root_program
= None
52 from Program
import Program
, load_dome_program
54 if root
.namespaceURI
== constants
.DOME_NS
and root
.localName
== 'dome':
55 for x
in root
.childNodes
:
56 if x
.namespaceURI
== constants
.DOME_NS
:
57 if x
.localName
== 'dome-program':
58 self
.root_program
= load_dome_program(x
)
59 elif x
.localName
== 'dome-data':
60 for y
in x
.childNodes
:
61 if y
.nodeType
== Node
.ELEMENT_NODE
:
64 data_to_load
= dome_data
.documentElement
65 elif (root
.namespaceURI
== constants
.XSLT_NS
and
66 root
.localName
in ['stylesheet', 'transform']) or \
67 root
.hasAttributeNS(constants
.XSLT_NS
, 'version'):
69 self
.root_program
= xslt
.import_sheet(doc
)
70 x
= implementation
.createDocument(None, 'xslt', None)
71 data_to_load
= x
.documentElement
72 src
= get_xslt_source(x
, dome_data
)
73 data_to_load
.appendChild(x
.createElementNS(None, 'Result'))
74 data_to_load
.appendChild(src
)
80 self
.root_program
= root_program
82 if not self
.root_program
:
83 self
.root_program
= Program('Root')
86 self
.doc
= implementation
.createDocument(None, 'root', None)
88 node
= support
.import_with_ns(self
.doc
, data_to_load
)
89 self
.doc
.replaceChild(node
, self
.doc
.documentElement
)
92 self
.views
= [] # Notified when something changes
93 self
.locks
= {} # Node -> number of locks
96 # Pop an (op_number, function) off one of these and call the function to
97 # move forwards or backwards in the undo history.
102 # Each series of suboperations in an undo stack which are part of a single
103 # user op will have the same number...
107 #print "GC freed:", gc.collect()
108 #print "Garbage", gc.garbage
110 def lock(self
, node
):
111 """Prevent removal of this node (or any ancestor)."""
112 #print "Locking", node.nodeName
113 self
.locks
[node
] = self
.get_locks(node
) + 1
115 self
.lock(node
.parentNode
)
117 def unlock(self
, node
):
118 """Reverse the effect of lock(). Must call unlock the same number
119 of times as lock to fully unlock the node."""
120 l
= self
.get_locks(node
)
122 self
.locks
[node
] = l
- 1
124 del self
.locks
[node
] # Or get a memory leak...
125 if node
== self
.doc
.documentElement
:
127 self
.unlock(node
.parentNode
)
130 raise Exception('unlock(%s): Node not locked!' % node
)
132 self
.unlock(node
.parentNode
)
134 def get_locks(self
, node
):
136 return self
.locks
[node
]
140 def lock_and_copy(self
, node
):
141 """Locks 'node' in the current model and returns a new model
142 with a copy of the subtree."""
143 if self
.get_locks(node
):
144 raise Exception("Can't enter locked node!")
145 m
= Model(self
.get_base_uri(node
), root_program
= self
.root_program
)
146 copy
= support
.import_with_ns(m
.doc
, node
)
148 m
.replace_node(root
, copy
)
153 "Increment the user_op counter. Undo will undo every operation between"
158 "Return the true root node (not a view root)"
159 return self
.doc
.documentElement
161 def add_view(self
, view
):
163 "'update_all(subtree) - called when a major change occurs."
164 #print "New view:", view
165 self
.views
.append(view
)
167 def remove_view(self
, view
):
168 #print "Removing view", view
169 self
.views
.remove(view
)
170 #print "Now:", self.views
174 def update_all(self
, node
):
175 "Called when 'node' has been updated."
176 "'node' is still in the document, so deleting or replacing"
177 "a node calls this on the parent."
181 def update_replace(self
, old
, new
):
182 "Called when 'old' is replaced by 'new'."
184 v
.update_replace(old
, new
)
186 def strip_space(self
, node
= None):
188 node
= self
.doc
.documentElement
189 if node
.nodeType
== Node
.TEXT_NODE
:
190 #node.data = node.data.strip()
192 # node.parentNode.removeChild(node)
193 if not node
.data
.strip():
194 node
.parentNode
.removeChild(node
)
196 for k
in node
.childNodes
[:]:
201 def normalise(self
, node
):
202 old
= node
.cloneNode(1)
204 self
.add_undo(lambda: self
.replace_node(node
, old
))
205 self
.update_all(node
)
207 def convert_to_text(self
, node
):
208 assert node
.nodeType
== Node
.COMMENT_NODE
209 new
= self
.doc
.createTextNode(node
.data
)
210 self
.replace_node(node
, new
)
213 def remove_ns(self
, node
):
214 nss
= GetAllNs(node
.parentNode
)
215 dns
= nss
.get(None, None)
216 create
= node
.ownerDocument
.createElementNS
218 if node
.nodeType
!= Node
.ELEMENT_NODE
:
219 return node
.cloneNode(1)
220 new
= create(dns
, node
.nodeName
)
221 for a
in node
.attributes
.values():
222 if a
.localName
== 'xmlns' and a
.prefix
is None:
223 print "Removing xmlns attrib on", node
225 new
.setAttributeNS(a
.namespaceURI
, a
.name
, a
.value
)
227 for k
in node
.childNodes
:
228 new
.appendChild(ns_clone(k
))
231 self
.replace_node(node
, new
)
234 def set_name(self
, node
, namespace
, name
):
235 if self
.get_locks(node
):
236 raise Exception('Attempt to set name on locked node %s' % node
)
238 new
= node
.ownerDocument
.createElementNS(namespace
, name
)
239 self
.replace_shallow(node
, new
)
242 def replace_shallow(self
, old
, new
):
243 """Replace old with new, keeping the old children."""
244 assert not new
.childNodes
245 assert not new
.parentNode
247 old_name
= old
.nodeName
248 old_ns
= old
.namespaceURI
250 kids
= old
.childNodes
[:]
251 attrs
= old
.attributes
.values()
252 parent
= old
.parentNode
253 [ old
.removeChild(k
) for k
in kids
]
254 parent
.replaceChild(new
, old
)
255 [ new
.appendChild(k
) for k
in kids
]
256 [ new
.setAttributeNS(a
.namespaceURI
, a
.name
, a
.value
) for a
in attrs
]
258 self
.add_undo(lambda: self
.replace_shallow(new
, old
))
260 self
.update_replace(old
, new
)
263 if __main__
.no_gui_mode
:
264 def add_undo(self
, fn
):
267 def add_undo(self
, fn
):
268 self
.undo_stack
.append((self
.user_op
, fn
))
269 if not self
.doing_undo
:
272 def set_data(self
, node
, data
):
275 self
.add_undo(lambda: self
.set_data(node
, old_data
))
276 self
.update_all(node
)
278 def replace_node(self
, old
, new
):
279 if self
.get_locks(old
):
280 raise Exception('Attempt to replace locked node %s' % old
)
281 old
.parentNode
.replaceChild(new
, old
)
282 self
.add_undo(lambda: self
.replace_node(new
, old
))
284 self
.update_replace(old
, new
)
286 def delete_shallow(self
, node
):
287 """Replace node with its contents"""
288 kids
= node
.childNodes
[:]
289 next
= node
.nextSibling
290 parent
= node
.parentNode
291 for n
in kids
+ [node
]:
292 if self
.get_locks(n
):
293 raise Exception('Attempt to move/delete locked node %s' % n
)
295 self
.delete_internal(k
)
296 self
.delete_internal(node
)
298 self
.insert_before_interal(next
, k
, parent
)
299 self
.update_all(parent
)
301 def delete_nodes(self
, nodes
):
302 #print "Deleting", nodes
304 if self
.get_locks(n
):
305 raise Exception('Attempt to delete locked node %s' % n
)
308 self
.delete_internal(n
)
311 def delete_internal(self
, node
):
312 "Doesn't update display."
313 next
= node
.nextSibling
314 parent
= node
.parentNode
315 parent
.removeChild(node
)
316 self
.add_undo(lambda: self
.insert_before(next
, node
, parent
= parent
))
318 def insert_before_interal(self
, node
, new
, parent
):
319 "Insert 'new' before 'node'. If 'node' is None then insert at the end"
320 "of parent's children."
321 assert new
.nodeType
!= Node
.DOCUMENT_FRAGMENT_NODE
322 assert parent
.nodeType
== Node
.ELEMENT_NODE
323 parent
.insertBefore(new
, node
)
324 self
.add_undo(lambda: self
.delete_nodes([new
]))
327 if not self
.undo_stack
:
328 raise Exception('Nothing to undo')
330 assert not self
.doing_undo
332 uop
= self
.undo_stack
[-1][0]
334 # Swap stacks so that the undo actions will populate the redo stack...
335 (self
.undo_stack
, self
.redo_stack
) = (self
.redo_stack
, self
.undo_stack
)
338 while self
.redo_stack
and self
.redo_stack
[-1][0] == uop
:
339 self
.redo_stack
[-1][1]()
340 self
.redo_stack
.pop()
342 (self
.undo_stack
, self
.redo_stack
) = (self
.redo_stack
, self
.undo_stack
)
346 if not self
.redo_stack
:
347 raise Exception('Nothing to redo')
349 uop
= self
.redo_stack
[-1][0]
352 while self
.redo_stack
and self
.redo_stack
[-1][0] == uop
:
353 self
.redo_stack
[-1][1]()
354 self
.redo_stack
.pop()
358 def insert(self
, node
, new
, index
= 0):
359 if len(node
.childNodes
) > index
:
360 self
.insert_before(node
.childNodes
[index
], new
)
362 self
.insert_before(None, new
, parent
= node
)
364 def insert_after(self
, node
, new
):
365 self
.insert_before(node
.nextSibling
, new
, parent
= node
.parentNode
)
367 def insert_before(self
, node
, new
, parent
= None):
368 "Insert 'new' before 'node'. If 'node' is None then insert at the end"
369 "of parent's children."
371 parent
= node
.parentNode
372 if new
.nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
:
373 for n
in new
.childNodes
[:]:
374 self
.insert_before_interal(node
, n
, parent
)
376 self
.insert_before_interal(node
, new
, parent
)
377 self
.update_all(parent
)
379 def split_qname(self
, node
, name
):
381 namespaceURI
= XMLNS_NAMESPACE
384 prefix
, localName
= name
.split(':')
385 namespaceURI
= self
.prefix_to_namespace(node
, prefix
)
389 return namespaceURI
, localName
391 def set_attrib(self
, node
, name
, value
, with_update
= 1):
392 """Set an attribute's value. If value is None, remove the attribute.
393 Returns the new attribute node, or None if removing."""
394 namespaceURI
, localName
= self
.split_qname(node
, name
)
396 if node
.hasAttributeNS(namespaceURI
, localName
):
397 old
= node
.getAttributeNS(namespaceURI
, localName
)
400 #print "Set (%s,%s) = %s" % (namespaceURI, name, value)
402 node
.setAttributeNS(namespaceURI
, name
, value
)
404 node
.removeAttributeNS(namespaceURI
, localName
)
406 self
.add_undo(lambda: self
.set_attrib(node
, name
, old
))
409 self
.update_all(node
)
411 if localName
== 'xmlns':
413 return node
.attributes
[(namespaceURI
, localName
)]
415 def prefix_to_namespace(self
, node
, prefix
):
416 "Use the xmlns attributes to workout the namespace."
418 if nss
.has_key(prefix
):
419 return nss
[prefix
] or None
421 if prefix
== 'xmlns':
422 return XMLNS_NAMESPACE
423 raise Exception("No such namespace prefix '%s'" % prefix
)
427 def get_base_uri(self
, node
):
428 """Go up through the parents looking for a uri attribute.
429 If there isn't one, use the document's URI."""
431 if node
.nodeType
== Node
.DOCUMENT_NODE
:
433 if node
.hasAttributeNS(None, 'uri'):
434 return node
.getAttributeNS(None, 'uri')
435 node
= node
.parentNode