Code

d4a412ae63e1fb3e803283b37fba6085e1439fa0
[inkscape.git] / src / xml / simple-node.cpp
1 /*
2  * SimpleNode - simple 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  *
16  */
18 #include <cstring>
19 #include <string>
20 #include <glib/gstrfuncs.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 ( Node *child = node._first_child ;
190           child != NULL ; child = child->next() )
191     {
192         Node *child_copy=child->duplicate(document);
194         child_copy->_setParent(this);
195         if (_last_child) {
196             _last_child->_setNext(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(Node const &child) const {
244     if (!_cached_positions_valid) {
245         unsigned position=0;
246         for ( Node *sibling = _first_child ;
247               sibling ; sibling = sibling->next() )
248         {
249             sibling->_setCachedPosition(position);
250             position++;
251         }
252         _cached_positions_valid = true;
253     }
254     return child._cachedPosition();
257 Node *SimpleNode::nthChild(unsigned index) {
258     Node *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(Node *parent) {
281    if (_parent) {
282         _subtree_observers.remove(_parent->_subtreeObservers());
283     }
284     _parent = parent;
285     if (parent) {
286         _subtree_observers.add(parent->_subtreeObservers());
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         if (_document) {
305             _document->logger()->notifyContentChanged(*this, old_content, _content);
306         }
308         _observers.notifyContentChanged(*this, old_content, _content);
309     }
312 void
313 SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const /*is_interactive*/)
315     g_return_if_fail(name && *name);
317     GQuark const key = g_quark_from_string(name);
319     MutableList<AttributeRecord> ref;
320     MutableList<AttributeRecord> existing;
321     for ( existing = _attributes ; existing ; ++existing ) {
322         if ( existing->key == key ) {
323             break;
324         }
325         ref = existing;
326     }
328     Debug::EventTracker<> tracker;
330     ptr_shared<char> old_value=( existing ? existing->value : ptr_shared<char>() );
332     ptr_shared<char> new_value=ptr_shared<char>();
333     if (value) {
334         new_value = share_string(value);
335         tracker.set<DebugSetAttribute>(*this, key, new_value);
336         if (!existing) {
337             if (ref) {
338                 set_rest(ref, MutableList<AttributeRecord>(AttributeRecord(key, new_value)));
339             } else {
340                 _attributes = MutableList<AttributeRecord>(AttributeRecord(key, new_value));
341             }
342         } else {
343             existing->value = new_value;
344         }
345     } else {
346         tracker.set<DebugClearAttribute>(*this, key);
347         if (existing) {
348             if (ref) {
349                 set_rest(ref, rest(existing));
350             } else {
351                 _attributes = rest(existing);
352             }
353             set_rest(existing, MutableList<AttributeRecord>());
354         }
355     }
357     if ( new_value != old_value && (!old_value || !new_value || strcmp(old_value, new_value))) {
358         if (_document) {
359             _document->logger()->notifyAttributeChanged(*this, key, old_value, new_value);
360         }
362         _observers.notifyAttributeChanged(*this, key, old_value, new_value);
363     }
366 void SimpleNode::addChild(Node *child, Node *ref) {
367     g_assert(child);
368     g_assert(!ref || ref->parent() == this);
369     g_assert(!child->parent());
371     Debug::EventTracker<DebugAddChild> tracker(*this, *child, ref);
373     Node *next;
374     if (ref) {
375         next = ref->next();
376         ref->_setNext(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->_setCachedPosition(0);
387             _cached_positions_valid = true;
388         } else if (_cached_positions_valid) {
389             child->_setCachedPosition(ref->_cachedPosition() + 1);
390         }
391     } else {
392         // invalidate cached positions otherwise
393         _cached_positions_valid = false;
394     }
396     child->_setParent(this);
397     child->_setNext(next);
398     _child_count++;
400     if (_document) {
401         child->_bindDocument(*_document);
402         _document->logger()->notifyChildAdded(*this, *child, ref);
403     }
405     _observers.notifyChildAdded(*this, *child, ref);
408 void SimpleNode::_bindDocument(Document &document) {
409     g_assert(!_document || _document == &document);
411     if (!_document) {
412         _document = &document;
414         for ( Node *child = _first_child ; child != NULL ; child = child->next() ) {
415             child->_bindDocument(document);
416         }
417     }
420 void SimpleNode::removeChild(Node *child) {
421     g_assert(child);
422     g_assert(child->parent() == this);
424     Node *ref = ( child != _first_child ? previous_node(child) : NULL );
426     Debug::EventTracker<DebugRemoveChild> tracker(*this, *child);
428     Node *next = child->next();
429     if (ref) {
430         ref->_setNext(next);
431     } else {
432         _first_child = next;
433     }
434     if (!next) { // removing the last child?
435         _last_child = ref;
436     } else {
437         // removing any other child invalidates the cached positions
438         _cached_positions_valid = false;
439     }
441     child->_setNext(NULL);
442     child->_setParent(NULL);
443     _child_count--;
445     if (_document) {
446         _document->logger()->notifyChildRemoved(*this, *child, ref);
447     }
449     _observers.notifyChildRemoved(*this, *child, ref);
452 void SimpleNode::changeOrder(Node *child, Node *ref) {
453     g_return_if_fail(child);
454     g_return_if_fail(child->parent() == this);
455     g_return_if_fail(child != ref);
456     g_return_if_fail(!ref || ref->parent() == this);
458     Node *const prev = previous_node(child);
460     Debug::EventTracker<DebugSetChildPosition> tracker(*this, *child, prev, ref);
462     if (prev == ref) { return; }
464     Node *next;
466     /* Remove from old position. */
467     next=child->next();
468     if (prev) {
469         prev->_setNext(next);
470     } else {
471         _first_child = next;
472     }
473     if (!next) {
474         _last_child = prev;
475     }
477     /* Insert at new position. */
478     if (ref) {
479         next = ref->next();
480         ref->_setNext(child);
481     } else {
482         next = _first_child;
483         _first_child = child;
484     }
485     child->_setNext(next);
486     if (!next) {
487         _last_child = child;
488     }
490     _cached_positions_valid = false;
492     if (_document) {
493         _document->logger()->notifyChildOrderChanged(*this, *child, prev, ref);
494     }
496     _observers.notifyChildOrderChanged(*this, *child, prev, ref);
499 void SimpleNode::setPosition(int pos) {
500     g_return_if_fail(_parent != NULL);
502     // a position beyond the end of the list means the end of the list;
503     // a negative position is the same as an infinitely large position
505     Node *ref=NULL;
506     for ( Node *sibling = _parent->firstChild() ;
507           sibling && pos ; sibling = sibling->next() )
508     {
509         if ( sibling != this ) {
510             ref = sibling;
511             pos--;
512         }
513     }
515     _parent->changeOrder(this, ref);
518 namespace {
520 void child_added(Node *node, Node *child, Node *ref, void *data) {
521     reinterpret_cast<NodeObserver *>(data)->notifyChildAdded(*node, *child, ref);
524 void child_removed(Node *node, Node *child, Node *ref, void *data) {
525     reinterpret_cast<NodeObserver *>(data)->notifyChildRemoved(*node, *child, ref);
528 void content_changed(Node *node, gchar const *old_content, gchar const *new_content, void *data) {
529     reinterpret_cast<NodeObserver *>(data)->notifyContentChanged(*node, Util::share_unsafe((const char *)old_content), Util::share_unsafe((const char *)new_content));
532 void attr_changed(Node *node, gchar const *name, gchar const *old_value, gchar const *new_value, bool /*is_interactive*/, void *data) {
533     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));
536 void order_changed(Node *node, Node *child, Node *old_ref, Node *new_ref, void *data) {
537     reinterpret_cast<NodeObserver *>(data)->notifyChildOrderChanged(*node, *child, old_ref, new_ref);
540 const NodeEventVector OBSERVER_EVENT_VECTOR = {
541     &child_added,
542     &child_removed,
543     &attr_changed,
544     &content_changed,
545     &order_changed
546 };
548 };
550 void SimpleNode::synthesizeEvents(NodeEventVector const *vector, void *data) {
551     if (vector->attr_changed) {
552         for ( List<AttributeRecord const> iter = _attributes ;
553               iter ; ++iter )
554         {
555             vector->attr_changed(this, g_quark_to_string(iter->key), NULL, iter->value, false, data);
556         }
557     }
558     if (vector->child_added) {
559         Node *ref = NULL;
560         for ( Node *child = this->_first_child ;
561               child ; child = child->next() )
562         {
563             vector->child_added(this, child, ref, data);
564             ref = child;
565         }
566     }
567     if (vector->content_changed) {
568         vector->content_changed(this, NULL, this->_content, data);
569     }
572 void SimpleNode::synthesizeEvents(NodeObserver &observer) {
573     synthesizeEvents(&OBSERVER_EVENT_VECTOR, &observer);
576 Node *SimpleNode::root() {
577     Node *parent=this;
578     while (parent->parent()) {
579         parent = parent->parent();
580     }
582     if ( parent->type() == DOCUMENT_NODE ) {
583         for ( Node *child = _document->firstChild() ;
584               child ; child = child->next() )
585         {
586             if ( child->type() == ELEMENT_NODE ) {
587                 return child;
588             }
589         }
590         return NULL;
591     } else if ( parent->type() == ELEMENT_NODE ) {
592         return parent;
593     } else {
594         return NULL;
595     }
598 void SimpleNode::mergeFrom(Node const *src, gchar const *key) {
599     g_return_if_fail(src != NULL);
600     g_return_if_fail(key != NULL);
601     g_assert(src != this);
603     setContent(src->content());
605     for ( Node const *child = src->firstChild() ; child != NULL ; child = child->next() )
606     {
607         gchar const *id = child->attribute(key);
608         if (id) {
609             Node *rch=sp_repr_lookup_child(this, key, id);
610             if (rch) {
611                 rch->mergeFrom(child, key);
612             } else {
613                 rch = child->duplicate(_document);
614                 appendChild(rch);
615                 rch->release();
616             }
617         } else {
618             Node *rch=child->duplicate(_document);
619             appendChild(rch);
620             rch->release();
621         }
622     }
624     for ( List<AttributeRecord const> iter = src->attributeList() ;
625           iter ; ++iter )
626     {
627         setAttribute(g_quark_to_string(iter->key), iter->value);
628     }
635 /*
636   Local Variables:
637   mode:c++
638   c-file-style:"stroustrup"
639   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
640   indent-tabs-mode:nil
641   fill-column:99
642   End:
643 */
644 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :