Code

Applying fixes for gcc 4.3 build issues (closes LP: #169115)
[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)
164 : Node(), _name(code), _attributes(), _child_count(0),
165   _cached_positions_valid(false)
167     this->_document = NULL;
168     this->_document = NULL;
169     this->_parent = this->_next = NULL;
170     this->_first_child = this->_last_child = NULL;
172     _observers.add(_subtree_observers);
175 SimpleNode::SimpleNode(SimpleNode const &node)
176 : Node(),
177   _cached_position(node._cached_position),
178   _name(node._name), _attributes(), _content(node._content),
179   _child_count(node._child_count),
180   _cached_positions_valid(node._cached_positions_valid)
182     _document = NULL;
183     _parent = _next = NULL;
184     _first_child = _last_child = NULL;
186     for ( Node *child = node._first_child ;
187           child != NULL ; child = child->next() )
188     {
189         Node *child_copy=child->duplicate(NULL); // FIXME
191         child_copy->_setParent(this);
192         if (_last_child) {
193             _last_child->_setNext(child_copy);
194         } else {
195             _first_child = child_copy;
196         }
197         _last_child = child_copy;
199         child_copy->release(); // release to avoid a leak
200     }
202     for ( List<AttributeRecord const> iter = node._attributes ;
203           iter ; ++iter )
204     {
205         _attributes = cons(*iter, _attributes);
206     }
208     _observers.add(_subtree_observers);
211 gchar const *SimpleNode::name() const {
212     return g_quark_to_string(_name);
215 gchar const *SimpleNode::content() const {
216     return this->_content;
219 gchar const *SimpleNode::attribute(gchar const *name) const {
220     g_return_val_if_fail(name != NULL, NULL);
222     GQuark const key = g_quark_from_string(name);
224     for ( List<AttributeRecord const> iter = _attributes ;
225           iter ; ++iter )
226     {
227         if ( iter->key == key ) {
228             return iter->value;
229         }
230     }
232     return NULL;
235 unsigned SimpleNode::position() const {
236     g_return_val_if_fail(_parent != NULL, 0);
237     return _parent->_childPosition(*this);
240 unsigned SimpleNode::_childPosition(Node const &child) const {
241     if (!_cached_positions_valid) {
242         unsigned position=0;
243         for ( Node *sibling = _first_child ;
244               sibling ; sibling = sibling->next() )
245         {
246             sibling->_setCachedPosition(position);
247             position++;
248         }
249         _cached_positions_valid = true;
250     }
251     return child._cachedPosition();
254 Node *SimpleNode::nthChild(unsigned index) {
255     Node *child = _first_child;
256     for ( ; index > 0 && child ; child = child->next() ) {
257         index--;
258     }
259     return child;
262 bool SimpleNode::matchAttributeName(gchar const *partial_name) const {
263     g_return_val_if_fail(partial_name != NULL, false);
265     for ( List<AttributeRecord const> iter = _attributes ;
266           iter ; ++iter )
267     {
268         gchar const *name = g_quark_to_string(iter->key);
269         if (std::strstr(name, partial_name)) {
270             return true;
271         }
272     }
274     return false;
277 void SimpleNode::_setParent(Node *parent) {
278    if (_parent) {
279         _subtree_observers.remove(_parent->_subtreeObservers());
280     }
281     _parent = parent;
282     if (parent) {
283         _subtree_observers.add(parent->_subtreeObservers());
284     }
287 void SimpleNode::setContent(gchar const *content) {
288     ptr_shared<char> old_content=_content;
289     ptr_shared<char> new_content = ( content ? share_string(content) : ptr_shared<char>() );
291     Debug::EventTracker<> tracker;
292     if (new_content) {
293         tracker.set<DebugSetContent>(*this, new_content);
294     } else {
295         tracker.set<DebugClearContent>(*this);
296     }
298     _content = new_content;
300     if ( _content != old_content ) {
301         if (_document) {
302             _document->logger()->notifyContentChanged(*this, old_content, _content);
303         }
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         if (_document) {
356             _document->logger()->notifyAttributeChanged(*this, key, old_value, new_value);
357         }
359         _observers.notifyAttributeChanged(*this, key, old_value, new_value);
360     }
363 void SimpleNode::addChild(Node *child, Node *ref) {
364     g_assert(child);
365     g_assert(!ref || ref->parent() == this);
366     g_assert(!child->parent());
368     Debug::EventTracker<DebugAddChild> tracker(*this, *child, ref);
370     Node *next;
371     if (ref) {
372         next = ref->next();
373         ref->_setNext(child);
374     } else {
375         next = _first_child;
376         _first_child = child;
377     }
378     if (!next) { // appending?
379         _last_child = child;
380         // set cached position if possible when appending
381         if (!ref) {
382             // if !next && !ref, child is sole child
383             child->_setCachedPosition(0);
384             _cached_positions_valid = true;
385         } else if (_cached_positions_valid) {
386             child->_setCachedPosition(ref->_cachedPosition() + 1);
387         }
388     } else {
389         // invalidate cached positions otherwise
390         _cached_positions_valid = false;
391     }
393     child->_setParent(this);
394     child->_setNext(next);
395     _child_count++;
397     if (_document) {
398         child->_bindDocument(*_document);
399         _document->logger()->notifyChildAdded(*this, *child, ref);
400     }
402     _observers.notifyChildAdded(*this, *child, ref);
405 void SimpleNode::_bindDocument(Document &document) {
406     g_assert(!_document || _document == &document);
408     if (!_document) {
409         _document = &document;
411         for ( Node *child = _first_child ; child != NULL ; child = child->next() ) {
412             child->_bindDocument(document);
413         }
414     }
417 void SimpleNode::removeChild(Node *child) {
418     g_assert(child);
419     g_assert(child->parent() == this);
421     Node *ref = ( child != _first_child ? previous_node(child) : NULL );
423     Debug::EventTracker<DebugRemoveChild> tracker(*this, *child);
425     Node *next = child->next();
426     if (ref) {
427         ref->_setNext(next);
428     } else {
429         _first_child = next;
430     }
431     if (!next) { // removing the last child?
432         _last_child = ref;
433     } else {
434         // removing any other child invalidates the cached positions
435         _cached_positions_valid = false;
436     }
438     child->_setNext(NULL);
439     child->_setParent(NULL);
440     _child_count--;
442     if (_document) {
443         _document->logger()->notifyChildRemoved(*this, *child, ref);
444     }
446     _observers.notifyChildRemoved(*this, *child, ref);
449 void SimpleNode::changeOrder(Node *child, Node *ref) {
450     g_return_if_fail(child);
451     g_return_if_fail(child->parent() == this);
452     g_return_if_fail(child != ref);
453     g_return_if_fail(!ref || ref->parent() == this);
455     Node *const prev = previous_node(child);
457     Debug::EventTracker<DebugSetChildPosition> tracker(*this, *child, prev, ref);
459     if (prev == ref) { return; }
461     Node *next;
463     /* Remove from old position. */
464     next=child->next();
465     if (prev) {
466         prev->_setNext(next);
467     } else {
468         _first_child = next;
469     }
470     if (!next) {
471         _last_child = prev;
472     }
474     /* Insert at new position. */
475     if (ref) {
476         next = ref->next();
477         ref->_setNext(child);
478     } else {
479         next = _first_child;
480         _first_child = child;
481     }
482     child->_setNext(next);
483     if (!next) {
484         _last_child = child;
485     }
487     _cached_positions_valid = false;
489     if (_document) {
490         _document->logger()->notifyChildOrderChanged(*this, *child, prev, ref);
491     }
493     _observers.notifyChildOrderChanged(*this, *child, prev, ref);
496 void SimpleNode::setPosition(int pos) {
497     g_return_if_fail(_parent != NULL);
499     // a position beyond the end of the list means the end of the list;
500     // a negative position is the same as an infinitely large position
502     Node *ref=NULL;
503     for ( Node *sibling = _parent->firstChild() ;
504           sibling && pos ; sibling = sibling->next() )
505     {
506         if ( sibling != this ) {
507             ref = sibling;
508             pos--;
509         }
510     }
512     _parent->changeOrder(this, ref);
515 namespace {
517 void child_added(Node *node, Node *child, Node *ref, void *data) {
518     reinterpret_cast<NodeObserver *>(data)->notifyChildAdded(*node, *child, ref);
521 void child_removed(Node *node, Node *child, Node *ref, void *data) {
522     reinterpret_cast<NodeObserver *>(data)->notifyChildRemoved(*node, *child, ref);
525 void content_changed(Node *node, gchar const *old_content, gchar const *new_content, void *data) {
526     reinterpret_cast<NodeObserver *>(data)->notifyContentChanged(*node, Util::share_unsafe((const char *)old_content), Util::share_unsafe((const char *)new_content));
529 void attr_changed(Node *node, gchar const *name, gchar const *old_value, gchar const *new_value, bool /*is_interactive*/, void *data) {
530     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));
533 void order_changed(Node *node, Node *child, Node *old_ref, Node *new_ref, void *data) {
534     reinterpret_cast<NodeObserver *>(data)->notifyChildOrderChanged(*node, *child, old_ref, new_ref);
537 const NodeEventVector OBSERVER_EVENT_VECTOR = {
538     &child_added,
539     &child_removed,
540     &attr_changed,
541     &content_changed,
542     &order_changed
543 };
545 };
547 void SimpleNode::synthesizeEvents(NodeEventVector const *vector, void *data) {
548     if (vector->attr_changed) {
549         for ( List<AttributeRecord const> iter = _attributes ;
550               iter ; ++iter )
551         {
552             vector->attr_changed(this, g_quark_to_string(iter->key), NULL, iter->value, false, data);
553         }
554     }
555     if (vector->child_added) {
556         Node *ref = NULL;
557         for ( Node *child = this->_first_child ;
558               child ; child = child->next() )
559         {
560             vector->child_added(this, child, ref, data);
561             ref = child;
562         }
563     }
564     if (vector->content_changed) {
565         vector->content_changed(this, NULL, this->_content, data);
566     }
569 void SimpleNode::synthesizeEvents(NodeObserver &observer) {
570     synthesizeEvents(&OBSERVER_EVENT_VECTOR, &observer);
573 Node *SimpleNode::root() {
574     Node *parent=this;
575     while (parent->parent()) {
576         parent = parent->parent();
577     }
579     if ( parent->type() == DOCUMENT_NODE ) {
580         for ( Node *child = _document->firstChild() ;
581               child ; child = child->next() )
582         {
583             if ( child->type() == ELEMENT_NODE ) {
584                 return child;
585             }
586         }
587         return NULL;
588     } else if ( parent->type() == ELEMENT_NODE ) {
589         return parent;
590     } else {
591         return NULL;
592     }
595 void SimpleNode::mergeFrom(Node const *src, gchar const *key) {
596     g_return_if_fail(src != NULL);
597     g_return_if_fail(key != NULL);
598     g_assert(src != this);
600     setContent(src->content());
602     for ( Node const *child = src->firstChild() ; child != NULL ; child = child->next() )
603     {
604         gchar const *id = child->attribute(key);
605         if (id) {
606             Node *rch=sp_repr_lookup_child(this, key, id);
607             if (rch) {
608                 rch->mergeFrom(child, key);
609             } else {
610                 rch = child->duplicate(_document);
611                 appendChild(rch);
612                 rch->release();
613             }
614         } else {
615             Node *rch=child->duplicate(_document);
616             appendChild(rch);
617             rch->release();
618         }
619     }
621     for ( List<AttributeRecord const> iter = src->attributeList() ;
622           iter ; ++iter )
623     {
624         setAttribute(g_quark_to_string(iter->key), iter->value);
625     }
632 /*
633   Local Variables:
634   mode:c++
635   c-file-style:"stroustrup"
636   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
637   indent-tabs-mode:nil
638   fill-column:99
639   End:
640 */
641 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :