Code

7ecdc1b8398b004a290fc60be21fa718e59c1290
[inkscape.git] / src / xml / simple-node.cpp
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 };
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)
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);
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)
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);
214 gchar const *SimpleNode::name() const {
215     return g_quark_to_string(_name);
218 gchar const *SimpleNode::content() const {
219     return this->_content;
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;
238 unsigned SimpleNode::position() const {
239     g_return_val_if_fail(_parent != NULL, 0);
240     return _parent->_childPosition(*this);
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;
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;
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;
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     }
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     }
309 void
310 SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const /*is_interactive*/)
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     }
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);
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);
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);
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);
505 namespace {
507 void child_added(Node *node, Node *child, Node *ref, void *data) {
508     reinterpret_cast<NodeObserver *>(data)->notifyChildAdded(*node, *child, ref);
511 void child_removed(Node *node, Node *child, Node *ref, void *data) {
512     reinterpret_cast<NodeObserver *>(data)->notifyChildRemoved(*node, *child, ref);
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));
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));
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);
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     }
559 void SimpleNode::synthesizeEvents(NodeObserver &observer) {
560     synthesizeEvents(&OBSERVER_EVENT_VECTOR, &observer);
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     }
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     }
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 :