Code

554752941fbca4c34fc735a66cfb54a2996a02ea
[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 <glib/gstrfuncs.h>
19 #include "xml/simple-node.h"
20 #include "xml/node-event-vector.h"
21 #include "xml/node-fns.h"
22 #include "xml/repr.h"
23 #include "debug/event-tracker.h"
24 #include "debug/simple-event.h"
25 #include "util/share.h"
26 #include "util/format.h"
28 namespace Inkscape {
30 namespace XML {
32 namespace {
34 Util::ptr_shared<char> stringify_node(Node const &node) {
35     gchar *string;
36     switch (node.type()) {
37     case ELEMENT_NODE: {
38         char const *id=node.attribute("id");
39         if (id) {
40             string = g_strdup_printf("element(%p)=%s(#%s)", &node, node.name(), id);
41         } else {
42             string = g_strdup_printf("element(%p)=%s", &node, node.name());
43         }
44     } break;
45     case TEXT_NODE:
46         string = g_strdup_printf("text(%p)=%s", &node, node.content());
47         break;
48     case COMMENT_NODE:
49         string = g_strdup_printf("comment(%p)=<!--%s-->", &node, node.content());
50         break;
51     case DOCUMENT_NODE:
52         string = g_strdup_printf("document(%p)", &node);
53         break;
54     default:
55         string = g_strdup_printf("unknown(%p)", &node);
56     }
57     Util::ptr_shared<char> result=Util::share_string(string);
58     g_free(string);
59     return result;
60 }
62 typedef Debug::SimpleEvent<Debug::Event::XML> DebugXML;
64 class DebugXMLNode : public DebugXML {
65 public:
66     DebugXMLNode(Node const &node, Util::ptr_shared<char> name)
67     : DebugXML(name)
68     {
69         _addProperty("node", stringify_node(node));
70     }
71 };
73 class DebugAddChild : public DebugXMLNode {
74 public:
75     DebugAddChild(Node const &node, Node const &child, Node const *prev)
76     : DebugXMLNode(node, Util::share_static_string("add-child"))
77     {
78         _addProperty("child", stringify_node(child));
79         _addProperty("position", Util::format("%d", ( prev ? prev->position() + 1 : 0 )));
80     }
81 };
83 class DebugRemoveChild : public DebugXMLNode {
84 public:
85     DebugRemoveChild(Node const &node, Node const &child)
86     : DebugXMLNode(node, Util::share_static_string("remove-child"))
87     {
88         _addProperty("child", stringify_node(child));
89     }
90 };
92 class DebugSetChildPosition : public DebugXMLNode {
93 public:
94     DebugSetChildPosition(Node const &node, Node const &child,
95                           Node const *old_prev, Node const *new_prev)
96     : DebugXMLNode(node, Util::share_static_string("set-child-position"))
97     {
98         _addProperty("child", stringify_node(child));
100         unsigned old_position = ( old_prev ? old_prev->position() : 0 );
101         unsigned position = ( new_prev ? new_prev->position() : 0 );
102         if ( position > old_position ) {
103             --position;
104         }
106         _addProperty("position", Util::format("%d", position));
107     }
108 };
110 class DebugSetContent : public DebugXMLNode {
111 public:
112     DebugSetContent(Node const &node,
113                     Util::ptr_shared<char> content)
114     : DebugXMLNode(node, Util::share_static_string("set-content"))
115     {
116         _addProperty("content", content);
117     }
118 };
120 class DebugClearContent : public DebugXMLNode {
121 public:
122     DebugClearContent(Node const &node)
123     : DebugXMLNode(node, Util::share_static_string("clear-content"))
124     {}
125 };
127 class DebugSetAttribute : public DebugXMLNode {
128 public:
129     DebugSetAttribute(Node const &node,
130                       GQuark name,
131                       Util::ptr_shared<char> value)
132     : DebugXMLNode(node, Util::share_static_string("set-attribute"))
133     {
134         _addProperty("name", Util::share_static_string(g_quark_to_string(name)));
135         _addProperty("value", value);
136     }
137 };
139 class DebugClearAttribute : public DebugXMLNode {
140 public:
141     DebugClearAttribute(Node const &node, GQuark name)
142     : DebugXMLNode(node, Util::share_static_string("clear-attribute"))
143     {
144         _addProperty("name", Util::share_static_string(g_quark_to_string(name)));
145     }
146 };
150 using Util::ptr_shared;
151 using Util::share_string;
152 using Util::share_unsafe;
153 using Util::share_static_string;
154 using Util::List;
155 using Util::MutableList;
156 using Util::cons;
157 using Util::rest;
158 using Util::set_rest;
160 SimpleNode::SimpleNode(int code)
161 : Node(), _name(code), _attributes(), _child_count(0),
162   _cached_positions_valid(false)
164     this->_document = NULL;
165     this->_document = NULL;
166     this->_parent = this->_next = NULL;
167     this->_first_child = this->_last_child = NULL;
169     _observers.add(_subtree_observers);
172 SimpleNode::SimpleNode(SimpleNode const &node)
173 : Node(),
174   _cached_position(node._cached_position),
175   _name(node._name), _attributes(), _content(node._content),
176   _child_count(node._child_count),
177   _cached_positions_valid(node._cached_positions_valid)
179     _document = NULL;
180     _parent = _next = NULL;
181     _first_child = _last_child = NULL;
183     for ( Node *child = node._first_child ;
184           child != NULL ; child = child->next() )
185     {
186         Node *child_copy=child->duplicate(NULL); // FIXME
188         child_copy->_setParent(this);
189         if (_last_child) {
190             _last_child->_setNext(child_copy);
191         } else {
192             _first_child = child_copy;
193         }
194         _last_child = child_copy;
196         child_copy->release(); // release to avoid a leak
197     }
199     for ( List<AttributeRecord const> iter = node._attributes ;
200           iter ; ++iter )
201     {
202         _attributes = cons(*iter, _attributes);
203     }
205     _observers.add(_subtree_observers);
208 gchar const *SimpleNode::name() const {
209     return g_quark_to_string(_name);
212 gchar const *SimpleNode::content() const {
213     return this->_content;
216 gchar const *SimpleNode::attribute(gchar const *name) const {
217     g_return_val_if_fail(name != NULL, NULL);
219     GQuark const key = g_quark_from_string(name);
221     for ( List<AttributeRecord const> iter = _attributes ;
222           iter ; ++iter )
223     {
224         if ( iter->key == key ) {
225             return iter->value;
226         }
227     }
229     return NULL;
232 unsigned SimpleNode::position() const {
233     g_return_val_if_fail(_parent != NULL, 0);
234     return _parent->_childPosition(*this);
237 unsigned SimpleNode::_childPosition(Node const &child) const {
238     if (!_cached_positions_valid) {
239         unsigned position=0;
240         for ( Node *sibling = _first_child ;
241               sibling ; sibling = sibling->next() )
242         {
243             sibling->_setCachedPosition(position);
244             position++;
245         }
246         _cached_positions_valid = true;
247     }
248     return child._cachedPosition();
251 Node *SimpleNode::nthChild(unsigned index) {
252     Node *child = _first_child;
253     for ( ; index > 0 && child ; child = child->next() ) {
254         index--;
255     }
256     return child;
259 bool SimpleNode::matchAttributeName(gchar const *partial_name) const {
260     g_return_val_if_fail(partial_name != NULL, false);
262     for ( List<AttributeRecord const> iter = _attributes ;
263           iter ; ++iter )
264     {
265         gchar const *name = g_quark_to_string(iter->key);
266         if (std::strstr(name, partial_name)) {
267             return true;
268         }
269     }
271     return false;
274 void SimpleNode::_setParent(Node *parent) {
275    if (_parent) {
276         _subtree_observers.remove(_parent->_subtreeObservers());
277     }
278     _parent = parent;
279     if (parent) {
280         _subtree_observers.add(parent->_subtreeObservers());
281     }
284 void SimpleNode::setContent(gchar const *content) {
285     ptr_shared<char> old_content=_content;
286     ptr_shared<char> new_content = ( content ? share_string(content) : ptr_shared<char>() );
288     Debug::EventTracker<> tracker;
289     if (new_content) {
290         tracker.set<DebugSetContent>(*this, new_content);
291     } else {
292         tracker.set<DebugClearContent>(*this);
293     }
295     _content = new_content;
297     if ( _content != old_content ) {
298         if (_document) {
299             _document->logger()->notifyContentChanged(*this, old_content, _content);
300         }
302         _observers.notifyContentChanged(*this, old_content, _content);
303     }
306 void
307 SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const is_interactive)
309     g_return_if_fail(name && *name);
311     GQuark const key = g_quark_from_string(name);
313     MutableList<AttributeRecord> ref;
314     MutableList<AttributeRecord> existing;
315     for ( existing = _attributes ; existing ; ++existing ) {
316         if ( existing->key == key ) {
317             break;
318         }
319         ref = existing;
320     }
322     Debug::EventTracker<> tracker;
324     ptr_shared<char> old_value=( existing ? existing->value : ptr_shared<char>() );
326     ptr_shared<char> new_value=ptr_shared<char>();
327     if (value) {
328         new_value = share_string(value);
329         tracker.set<DebugSetAttribute>(*this, key, new_value);
330         if (!existing) {
331             if (ref) {
332                 set_rest(ref, MutableList<AttributeRecord>(AttributeRecord(key, new_value)));
333             } else {
334                 _attributes = MutableList<AttributeRecord>(AttributeRecord(key, new_value));
335             }
336         } else {
337             existing->value = new_value;
338         }
339     } else {
340         tracker.set<DebugClearAttribute>(*this, key);
341         if (existing) {
342             if (ref) {
343                 set_rest(ref, rest(existing));
344             } else {
345                 _attributes = rest(existing);
346             }
347             set_rest(existing, MutableList<AttributeRecord>());
348         }
349     }
351     if ( new_value != old_value && (!old_value || !new_value || strcmp(old_value, new_value))) {
352         if (_document) {
353             _document->logger()->notifyAttributeChanged(*this, key, old_value, new_value);
354         }
356         _observers.notifyAttributeChanged(*this, key, old_value, new_value);
357     }
360 void SimpleNode::addChild(Node *child, Node *ref) {
361     g_assert(child);
362     g_assert(!ref || ref->parent() == this);
363     g_assert(!child->parent());
365     Debug::EventTracker<DebugAddChild> tracker(*this, *child, ref);
367     Node *next;
368     if (ref) {
369         next = ref->next();
370         ref->_setNext(child);
371     } else {
372         next = _first_child;
373         _first_child = child;
374     }
375     if (!next) { // appending?
376         _last_child = child;
377         // set cached position if possible when appending
378         if (!ref) {
379             // if !next && !ref, child is sole child
380             child->_setCachedPosition(0);
381             _cached_positions_valid = true;
382         } else if (_cached_positions_valid) {
383             child->_setCachedPosition(ref->_cachedPosition() + 1);
384         }
385     } else {
386         // invalidate cached positions otherwise
387         _cached_positions_valid = false;
388     }
390     child->_setParent(this);
391     child->_setNext(next);
392     _child_count++;
394     if (_document) {
395         child->_bindDocument(*_document);
396         _document->logger()->notifyChildAdded(*this, *child, ref);
397     }
399     _observers.notifyChildAdded(*this, *child, ref);
402 void SimpleNode::_bindDocument(Document &document) {
403     g_assert(!_document || _document == &document);
405     if (!_document) {
406         _document = &document;
408         for ( Node *child = _first_child ; child != NULL ; child = child->next() ) {
409             child->_bindDocument(document);
410         }
411     }
414 void SimpleNode::removeChild(Node *child) {
415     g_assert(child);
416     g_assert(child->parent() == this);
418     Node *ref = ( child != _first_child ? previous_node(child) : NULL );
420     Debug::EventTracker<DebugRemoveChild> tracker(*this, *child);
422     Node *next = child->next();
423     if (ref) {
424         ref->_setNext(next);
425     } else {
426         _first_child = next;
427     }
428     if (!next) { // removing the last child?
429         _last_child = ref;
430     } else {
431         // removing any other child invalidates the cached positions
432         _cached_positions_valid = false;
433     }
435     child->_setNext(NULL);
436     child->_setParent(NULL);
437     _child_count--;
439     if (_document) {
440         _document->logger()->notifyChildRemoved(*this, *child, ref);
441     }
443     _observers.notifyChildRemoved(*this, *child, ref);
446 void SimpleNode::changeOrder(Node *child, Node *ref) {
447     g_return_if_fail(child);
448     g_return_if_fail(child->parent() == this);
449     g_return_if_fail(child != ref);
450     g_return_if_fail(!ref || ref->parent() == this);
452     Node *const prev = previous_node(child);
454     Debug::EventTracker<DebugSetChildPosition> tracker(*this, *child, prev, ref);
456     if (prev == ref) { return; }
458     Node *next;
460     /* Remove from old position. */
461     next=child->next();
462     if (prev) {
463         prev->_setNext(next);
464     } else {
465         _first_child = next;
466     }
467     if (!next) {
468         _last_child = prev;
469     }
471     /* Insert at new position. */
472     if (ref) {
473         next = ref->next();
474         ref->_setNext(child);
475     } else {
476         next = _first_child;
477         _first_child = child;
478     }
479     child->_setNext(next);
480     if (!next) {
481         _last_child = child;
482     }
484     _cached_positions_valid = false;
486     if (_document) {
487         _document->logger()->notifyChildOrderChanged(*this, *child, prev, ref);
488     }
490     _observers.notifyChildOrderChanged(*this, *child, prev, ref);
493 void SimpleNode::setPosition(int pos) {
494     g_return_if_fail(_parent != NULL);
496     // a position beyond the end of the list means the end of the list;
497     // a negative position is the same as an infinitely large position
499     Node *ref=NULL;
500     for ( Node *sibling = _parent->firstChild() ;
501           sibling && pos ; sibling = sibling->next() )
502     {
503         if ( sibling != this ) {
504             ref = sibling;
505             pos--;
506         }
507     }
509     _parent->changeOrder(this, ref);
512 namespace {
514 void child_added(Node *node, Node *child, Node *ref, void *data) {
515     reinterpret_cast<NodeObserver *>(data)->notifyChildAdded(*node, *child, ref);
518 void child_removed(Node *node, Node *child, Node *ref, void *data) {
519     reinterpret_cast<NodeObserver *>(data)->notifyChildRemoved(*node, *child, ref);
522 void content_changed(Node *node, gchar const *old_content, gchar const *new_content, void *data) {
523     reinterpret_cast<NodeObserver *>(data)->notifyContentChanged(*node, Util::share_unsafe((const char *)old_content), Util::share_unsafe((const char *)new_content));
526 void attr_changed(Node *node, gchar const *name, gchar const *old_value, gchar const *new_value, bool is_interactive, void *data) {
527     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));
530 void order_changed(Node *node, Node *child, Node *old_ref, Node *new_ref, void *data) {
531     reinterpret_cast<NodeObserver *>(data)->notifyChildOrderChanged(*node, *child, old_ref, new_ref);
534 const NodeEventVector OBSERVER_EVENT_VECTOR = {
535     &child_added,
536     &child_removed,
537     &attr_changed,
538     &content_changed,
539     &order_changed
540 };
542 };
544 void SimpleNode::synthesizeEvents(NodeEventVector const *vector, void *data) {
545     if (vector->attr_changed) {
546         for ( List<AttributeRecord const> iter = _attributes ;
547               iter ; ++iter )
548         {
549             vector->attr_changed(this, g_quark_to_string(iter->key), NULL, iter->value, false, data);
550         }
551     }
552     if (vector->child_added) {
553         Node *ref = NULL;
554         for ( Node *child = this->_first_child ;
555               child ; child = child->next() )
556         {
557             vector->child_added(this, child, ref, data);
558             ref = child;
559         }
560     }
561     if (vector->content_changed) {
562         vector->content_changed(this, NULL, this->_content, data);
563     }
566 void SimpleNode::synthesizeEvents(NodeObserver &observer) {
567     synthesizeEvents(&OBSERVER_EVENT_VECTOR, &observer);
570 Node *SimpleNode::root() {
571     Node *parent=this;
572     while (parent->parent()) {
573         parent = parent->parent();
574     }
576     if ( parent->type() == DOCUMENT_NODE ) {
577         for ( Node *child = _document->firstChild() ;
578               child ; child = child->next() )
579         {
580             if ( child->type() == ELEMENT_NODE ) {
581                 return child;
582             }
583         }
584         return NULL;
585     } else if ( parent->type() == ELEMENT_NODE ) {
586         return parent;
587     } else {
588         return NULL;
589     }
592 void SimpleNode::mergeFrom(Node const *src, gchar const *key) {
593     g_return_if_fail(src != NULL);
594     g_return_if_fail(key != NULL);
595     g_assert(src != this);
597     setContent(src->content());
599     for ( Node const *child = src->firstChild() ; child != NULL ; child = child->next() )
600     {
601         gchar const *id = child->attribute(key);
602         if (id) {
603             Node *rch=sp_repr_lookup_child(this, key, id);
604             if (rch) {
605                 rch->mergeFrom(child, key);
606             } else {
607                 rch = child->duplicate(_document);
608                 appendChild(rch);
609                 rch->release();
610             }
611         } else {
612             Node *rch=child->duplicate(_document);
613             appendChild(rch);
614             rch->release();
615         }
616     }
618     for ( List<AttributeRecord const> iter = src->attributeList() ;
619           iter ; ++iter )
620     {
621         setAttribute(g_quark_to_string(iter->key), iter->value);
622     }
629 /*
630   Local Variables:
631   mode:c++
632   c-file-style:"stroustrup"
633   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
634   indent-tabs-mode:nil
635   fill-column:99
636   End:
637 */
638 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :