1 /** @file
2 * @brief Garbage collected XML node implementation
3 */
4 /* Copyright 2003-2005 MenTaLguY <mental@rydia.net>
5 * Copyright 2003 Nathan Hurst
6 * Copyright 1999-2003 Lauris Kaplinski
7 * Copyright 2000-2002 Ximian Inc.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * See the file COPYING for details.
15 */
17 #include <cstring>
18 #include <string>
19 #include <glib/gstrfuncs.h>
21 #include "xml/node.h"
22 #include "xml/simple-node.h"
23 #include "xml/node-event-vector.h"
24 #include "xml/node-fns.h"
25 #include "xml/repr.h"
26 #include "debug/event-tracker.h"
27 #include "debug/simple-event.h"
28 #include "util/share.h"
29 #include "util/format.h"
31 namespace Inkscape {
33 namespace XML {
35 namespace {
37 Util::ptr_shared<char> stringify_node(Node const &node) {
38 gchar *string;
39 switch (node.type()) {
40 case ELEMENT_NODE: {
41 char const *id=node.attribute("id");
42 if (id) {
43 string = g_strdup_printf("element(%p)=%s(#%s)", &node, node.name(), id);
44 } else {
45 string = g_strdup_printf("element(%p)=%s", &node, node.name());
46 }
47 } break;
48 case TEXT_NODE:
49 string = g_strdup_printf("text(%p)=%s", &node, node.content());
50 break;
51 case COMMENT_NODE:
52 string = g_strdup_printf("comment(%p)=<!--%s-->", &node, node.content());
53 break;
54 case DOCUMENT_NODE:
55 string = g_strdup_printf("document(%p)", &node);
56 break;
57 default:
58 string = g_strdup_printf("unknown(%p)", &node);
59 }
60 Util::ptr_shared<char> result=Util::share_string(string);
61 g_free(string);
62 return result;
63 }
65 typedef Debug::SimpleEvent<Debug::Event::XML> DebugXML;
67 class DebugXMLNode : public DebugXML {
68 public:
69 DebugXMLNode(Node const &node, Util::ptr_shared<char> name)
70 : DebugXML(name)
71 {
72 _addProperty("node", stringify_node(node));
73 }
74 };
76 class DebugAddChild : public DebugXMLNode {
77 public:
78 DebugAddChild(Node const &node, Node const &child, Node const *prev)
79 : DebugXMLNode(node, Util::share_static_string("add-child"))
80 {
81 _addProperty("child", stringify_node(child));
82 _addProperty("position", Util::format("%d", ( prev ? prev->position() + 1 : 0 )));
83 }
84 };
86 class DebugRemoveChild : public DebugXMLNode {
87 public:
88 DebugRemoveChild(Node const &node, Node const &child)
89 : DebugXMLNode(node, Util::share_static_string("remove-child"))
90 {
91 _addProperty("child", stringify_node(child));
92 }
93 };
95 class DebugSetChildPosition : public DebugXMLNode {
96 public:
97 DebugSetChildPosition(Node const &node, Node const &child,
98 Node const *old_prev, Node const *new_prev)
99 : DebugXMLNode(node, Util::share_static_string("set-child-position"))
100 {
101 _addProperty("child", stringify_node(child));
103 unsigned old_position = ( old_prev ? old_prev->position() : 0 );
104 unsigned position = ( new_prev ? new_prev->position() : 0 );
105 if ( position > old_position ) {
106 --position;
107 }
109 _addProperty("position", Util::format("%d", position));
110 }
111 };
113 class DebugSetContent : public DebugXMLNode {
114 public:
115 DebugSetContent(Node const &node,
116 Util::ptr_shared<char> content)
117 : DebugXMLNode(node, Util::share_static_string("set-content"))
118 {
119 _addProperty("content", content);
120 }
121 };
123 class DebugClearContent : public DebugXMLNode {
124 public:
125 DebugClearContent(Node const &node)
126 : DebugXMLNode(node, Util::share_static_string("clear-content"))
127 {}
128 };
130 class DebugSetAttribute : public DebugXMLNode {
131 public:
132 DebugSetAttribute(Node const &node,
133 GQuark name,
134 Util::ptr_shared<char> value)
135 : DebugXMLNode(node, Util::share_static_string("set-attribute"))
136 {
137 _addProperty("name", Util::share_static_string(g_quark_to_string(name)));
138 _addProperty("value", value);
139 }
140 };
142 class DebugClearAttribute : public DebugXMLNode {
143 public:
144 DebugClearAttribute(Node const &node, GQuark name)
145 : DebugXMLNode(node, Util::share_static_string("clear-attribute"))
146 {
147 _addProperty("name", Util::share_static_string(g_quark_to_string(name)));
148 }
149 };
151 }
153 using Util::ptr_shared;
154 using Util::share_string;
155 using Util::share_unsafe;
156 using Util::share_static_string;
157 using Util::List;
158 using Util::MutableList;
159 using Util::cons;
160 using Util::rest;
161 using Util::set_rest;
163 SimpleNode::SimpleNode(int code, Document *document)
164 : Node(), _name(code), _attributes(), _child_count(0),
165 _cached_positions_valid(false)
166 {
167 g_assert(document != NULL);
169 this->_document = document;
170 this->_parent = this->_next = NULL;
171 this->_first_child = this->_last_child = NULL;
173 _observers.add(_subtree_observers);
174 }
176 SimpleNode::SimpleNode(SimpleNode const &node, Document *document)
177 : Node(),
178 _cached_position(node._cached_position),
179 _name(node._name), _attributes(), _content(node._content),
180 _child_count(node._child_count),
181 _cached_positions_valid(node._cached_positions_valid)
182 {
183 g_assert(document != NULL);
185 _document = document;
186 _parent = _next = NULL;
187 _first_child = _last_child = NULL;
189 for ( SimpleNode *child = node._first_child ;
190 child != NULL ; child = child->_next )
191 {
192 SimpleNode *child_copy=dynamic_cast<SimpleNode *>(child->duplicate(document));
194 child_copy->_setParent(this);
195 if (_last_child) {
196 _last_child->_next = child_copy;
197 } else {
198 _first_child = child_copy;
199 }
200 _last_child = child_copy;
202 child_copy->release(); // release to avoid a leak
203 }
205 for ( List<AttributeRecord const> iter = node._attributes ;
206 iter ; ++iter )
207 {
208 _attributes = cons(*iter, _attributes);
209 }
211 _observers.add(_subtree_observers);
212 }
214 gchar const *SimpleNode::name() const {
215 return g_quark_to_string(_name);
216 }
218 gchar const *SimpleNode::content() const {
219 return this->_content;
220 }
222 gchar const *SimpleNode::attribute(gchar const *name) const {
223 g_return_val_if_fail(name != NULL, NULL);
225 GQuark const key = g_quark_from_string(name);
227 for ( List<AttributeRecord const> iter = _attributes ;
228 iter ; ++iter )
229 {
230 if ( iter->key == key ) {
231 return iter->value;
232 }
233 }
235 return NULL;
236 }
238 unsigned SimpleNode::position() const {
239 g_return_val_if_fail(_parent != NULL, 0);
240 return _parent->_childPosition(*this);
241 }
243 unsigned SimpleNode::_childPosition(SimpleNode const &child) const {
244 if (!_cached_positions_valid) {
245 unsigned position=0;
246 for ( SimpleNode *sibling = _first_child ;
247 sibling ; sibling = sibling->_next )
248 {
249 sibling->_cached_position = position;
250 position++;
251 }
252 _cached_positions_valid = true;
253 }
254 return child._cached_position;
255 }
257 Node *SimpleNode::nthChild(unsigned index) {
258 SimpleNode *child = _first_child;
259 for ( ; index > 0 && child ; child = child->_next ) {
260 index--;
261 }
262 return child;
263 }
265 bool SimpleNode::matchAttributeName(gchar const *partial_name) const {
266 g_return_val_if_fail(partial_name != NULL, false);
268 for ( List<AttributeRecord const> iter = _attributes ;
269 iter ; ++iter )
270 {
271 gchar const *name = g_quark_to_string(iter->key);
272 if (std::strstr(name, partial_name)) {
273 return true;
274 }
275 }
277 return false;
278 }
280 void SimpleNode::_setParent(SimpleNode *parent) {
281 if (_parent) {
282 _subtree_observers.remove(_parent->_subtree_observers);
283 }
284 _parent = parent;
285 if (parent) {
286 _subtree_observers.add(parent->_subtree_observers);
287 }
288 }
290 void SimpleNode::setContent(gchar const *content) {
291 ptr_shared<char> old_content=_content;
292 ptr_shared<char> new_content = ( content ? share_string(content) : ptr_shared<char>() );
294 Debug::EventTracker<> tracker;
295 if (new_content) {
296 tracker.set<DebugSetContent>(*this, new_content);
297 } else {
298 tracker.set<DebugClearContent>(*this);
299 }
301 _content = new_content;
303 if ( _content != old_content ) {
304 _document->logger()->notifyContentChanged(*this, old_content, _content);
305 _observers.notifyContentChanged(*this, old_content, _content);
306 }
307 }
309 void
310 SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const /*is_interactive*/)
311 {
312 g_return_if_fail(name && *name);
314 GQuark const key = g_quark_from_string(name);
316 MutableList<AttributeRecord> ref;
317 MutableList<AttributeRecord> existing;
318 for ( existing = _attributes ; existing ; ++existing ) {
319 if ( existing->key == key ) {
320 break;
321 }
322 ref = existing;
323 }
325 Debug::EventTracker<> tracker;
327 ptr_shared<char> old_value=( existing ? existing->value : ptr_shared<char>() );
329 ptr_shared<char> new_value=ptr_shared<char>();
330 if (value) {
331 new_value = share_string(value);
332 tracker.set<DebugSetAttribute>(*this, key, new_value);
333 if (!existing) {
334 if (ref) {
335 set_rest(ref, MutableList<AttributeRecord>(AttributeRecord(key, new_value)));
336 } else {
337 _attributes = MutableList<AttributeRecord>(AttributeRecord(key, new_value));
338 }
339 } else {
340 existing->value = new_value;
341 }
342 } else {
343 tracker.set<DebugClearAttribute>(*this, key);
344 if (existing) {
345 if (ref) {
346 set_rest(ref, rest(existing));
347 } else {
348 _attributes = rest(existing);
349 }
350 set_rest(existing, MutableList<AttributeRecord>());
351 }
352 }
354 if ( new_value != old_value && (!old_value || !new_value || strcmp(old_value, new_value))) {
355 _document->logger()->notifyAttributeChanged(*this, key, old_value, new_value);
356 _observers.notifyAttributeChanged(*this, key, old_value, new_value);
357 }
358 }
360 void SimpleNode::addChild(Node *generic_child, Node *generic_ref) {
361 g_assert(generic_child);
362 g_assert(generic_child->document() == _document);
363 g_assert(!generic_ref || generic_ref->document() == _document);
365 SimpleNode *child=dynamic_cast<SimpleNode *>(generic_child);
366 SimpleNode *ref=dynamic_cast<SimpleNode *>(generic_ref);
368 g_assert(!ref || ref->_parent == this);
369 g_assert(!child->_parent);
371 Debug::EventTracker<DebugAddChild> tracker(*this, *child, ref);
373 SimpleNode *next;
374 if (ref) {
375 next = ref->_next;
376 ref->_next = child;
377 } else {
378 next = _first_child;
379 _first_child = child;
380 }
381 if (!next) { // appending?
382 _last_child = child;
383 // set cached position if possible when appending
384 if (!ref) {
385 // if !next && !ref, child is sole child
386 child->_cached_position = 0;
387 _cached_positions_valid = true;
388 } else if (_cached_positions_valid) {
389 child->_cached_position = ref->_cached_position + 1;
390 }
391 } else {
392 // invalidate cached positions otherwise
393 _cached_positions_valid = false;
394 }
396 child->_setParent(this);
397 child->_next = next;
398 _child_count++;
400 _document->logger()->notifyChildAdded(*this, *child, ref);
401 _observers.notifyChildAdded(*this, *child, ref);
402 }
404 void SimpleNode::removeChild(Node *generic_child) {
405 g_assert(generic_child);
406 g_assert(generic_child->document() == _document);
408 SimpleNode *child=dynamic_cast<SimpleNode *>(generic_child);
409 SimpleNode *ref=dynamic_cast<SimpleNode *>(previous_node(child));
411 g_assert(child->_parent == this);
413 Debug::EventTracker<DebugRemoveChild> tracker(*this, *child);
415 SimpleNode *next = child->_next;
416 if (ref) {
417 ref->_next = next;
418 } else {
419 _first_child = next;
420 }
421 if (!next) { // removing the last child?
422 _last_child = ref;
423 } else {
424 // removing any other child invalidates the cached positions
425 _cached_positions_valid = false;
426 }
428 child->_next = NULL;
429 child->_setParent(NULL);
430 _child_count--;
432 _document->logger()->notifyChildRemoved(*this, *child, ref);
433 _observers.notifyChildRemoved(*this, *child, ref);
434 }
436 void SimpleNode::changeOrder(Node *generic_child, Node *generic_ref) {
437 g_assert(generic_child);
438 g_assert(generic_child->document() == this->_document);
439 g_assert(!generic_ref || generic_ref->document() == this->_document);
441 SimpleNode *const child=dynamic_cast<SimpleNode *>(generic_child);
442 SimpleNode *const ref=dynamic_cast<SimpleNode *>(generic_ref);
444 g_return_if_fail(child->parent() == this);
445 g_return_if_fail(child != ref);
446 g_return_if_fail(!ref || ref->parent() == this);
448 SimpleNode *const prev=dynamic_cast<SimpleNode *>(previous_node(child));
450 Debug::EventTracker<DebugSetChildPosition> tracker(*this, *child, prev, ref);
452 if (prev == ref) { return; }
454 SimpleNode *next;
456 /* Remove from old position. */
457 next = child->_next;
458 if (prev) {
459 prev->_next = next;
460 } else {
461 _first_child = next;
462 }
463 if (!next) {
464 _last_child = prev;
465 }
467 /* Insert at new position. */
468 if (ref) {
469 next = ref->_next;
470 ref->_next = child;
471 } else {
472 next = _first_child;
473 _first_child = child;
474 }
475 child->_next = next;
476 if (!next) {
477 _last_child = child;
478 }
480 _cached_positions_valid = false;
482 _document->logger()->notifyChildOrderChanged(*this, *child, prev, ref);
483 _observers.notifyChildOrderChanged(*this, *child, prev, ref);
484 }
486 void SimpleNode::setPosition(int pos) {
487 g_return_if_fail(_parent != NULL);
489 // a position beyond the end of the list means the end of the list;
490 // a negative position is the same as an infinitely large position
492 SimpleNode *ref=NULL;
493 for ( SimpleNode *sibling = _parent->_first_child ;
494 sibling && pos ; sibling = sibling->_next )
495 {
496 if ( sibling != this ) {
497 ref = sibling;
498 pos--;
499 }
500 }
502 _parent->changeOrder(this, ref);
503 }
505 namespace {
507 void child_added(Node *node, Node *child, Node *ref, void *data) {
508 reinterpret_cast<NodeObserver *>(data)->notifyChildAdded(*node, *child, ref);
509 }
511 void child_removed(Node *node, Node *child, Node *ref, void *data) {
512 reinterpret_cast<NodeObserver *>(data)->notifyChildRemoved(*node, *child, ref);
513 }
515 void content_changed(Node *node, gchar const *old_content, gchar const *new_content, void *data) {
516 reinterpret_cast<NodeObserver *>(data)->notifyContentChanged(*node, Util::share_unsafe((const char *)old_content), Util::share_unsafe((const char *)new_content));
517 }
519 void attr_changed(Node *node, gchar const *name, gchar const *old_value, gchar const *new_value, bool /*is_interactive*/, void *data) {
520 reinterpret_cast<NodeObserver *>(data)->notifyAttributeChanged(*node, g_quark_from_string(name), Util::share_unsafe((const char *)old_value), Util::share_unsafe((const char *)new_value));
521 }
523 void order_changed(Node *node, Node *child, Node *old_ref, Node *new_ref, void *data) {
524 reinterpret_cast<NodeObserver *>(data)->notifyChildOrderChanged(*node, *child, old_ref, new_ref);
525 }
527 const NodeEventVector OBSERVER_EVENT_VECTOR = {
528 &child_added,
529 &child_removed,
530 &attr_changed,
531 &content_changed,
532 &order_changed
533 };
535 };
537 void SimpleNode::synthesizeEvents(NodeEventVector const *vector, void *data) {
538 if (vector->attr_changed) {
539 for ( List<AttributeRecord const> iter = _attributes ;
540 iter ; ++iter )
541 {
542 vector->attr_changed(this, g_quark_to_string(iter->key), NULL, iter->value, false, data);
543 }
544 }
545 if (vector->child_added) {
546 SimpleNode *ref = NULL;
547 for ( SimpleNode *child = this->_first_child ;
548 child ; child = child->_next )
549 {
550 vector->child_added(this, child, ref, data);
551 ref = child;
552 }
553 }
554 if (vector->content_changed) {
555 vector->content_changed(this, NULL, this->_content, data);
556 }
557 }
559 void SimpleNode::synthesizeEvents(NodeObserver &observer) {
560 synthesizeEvents(&OBSERVER_EVENT_VECTOR, &observer);
561 }
563 Node *SimpleNode::root() {
564 Node *parent=this;
565 while (parent->parent()) {
566 parent = parent->parent();
567 }
569 if ( parent->type() == DOCUMENT_NODE ) {
570 for ( Node *child = _document->firstChild() ;
571 child ; child = child->next() )
572 {
573 if ( child->type() == ELEMENT_NODE ) {
574 return child;
575 }
576 }
577 return NULL;
578 } else if ( parent->type() == ELEMENT_NODE ) {
579 return parent;
580 } else {
581 return NULL;
582 }
583 }
585 void SimpleNode::mergeFrom(Node const *src, gchar const *key) {
586 g_return_if_fail(src != NULL);
587 g_return_if_fail(key != NULL);
588 g_assert(src != this);
590 setContent(src->content());
592 for ( Node const *child = src->firstChild() ; child != NULL ; child = child->next() )
593 {
594 gchar const *id = child->attribute(key);
595 if (id) {
596 Node *rch=sp_repr_lookup_child(this, key, id);
597 if (rch) {
598 rch->mergeFrom(child, key);
599 } else {
600 rch = child->duplicate(_document);
601 appendChild(rch);
602 rch->release();
603 }
604 } else {
605 Node *rch=child->duplicate(_document);
606 appendChild(rch);
607 rch->release();
608 }
609 }
611 for ( List<AttributeRecord const> iter = src->attributeList() ;
612 iter ; ++iter )
613 {
614 setAttribute(g_quark_to_string(iter->key), iter->value);
615 }
616 }
618 }
620 }
622 /*
623 Local Variables:
624 mode:c++
625 c-file-style:"stroustrup"
626 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
627 indent-tabs-mode:nil
628 fill-column:99
629 End:
630 */
631 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :