Code

* Lots of documentation for the Inkscape::XML namespace
[inkscape.git] / src / xml / simple-node.cpp
index 17339271fc0549135bbf3e97d70bc32332a4e108..3cca393d228366d7b013d9109d7c207ebaddd85f 100644 (file)
@@ -1,7 +1,7 @@
-/*
- * SimpleNode - simple XML node implementation
- *
- * Copyright 2003-2005 MenTaLguY <mental@rydia.net>
+/** @file
+ * @brief Garbage collected XML node implementation
+ */
+/* Copyright 2003-2005 MenTaLguY <mental@rydia.net>
  * Copyright 2003 Nathan Hurst
  * Copyright 1999-2003 Lauris Kaplinski
  * Copyright 2000-2002 Ximian Inc.
  * of the License, or (at your option) any later version.
  *
  * See the file COPYING for details.
- *
  */
 
+#include <cstring>
+#include <string>
 #include <glib/gstrfuncs.h>
+
 #include "xml/simple-node.h"
 #include "xml/node-event-vector.h"
 #include "xml/node-fns.h"
 #include "xml/repr.h"
 #include "debug/event-tracker.h"
+#include "debug/simple-event.h"
+#include "util/share.h"
+#include "util/format.h"
 
 namespace Inkscape {
 
@@ -56,234 +61,138 @@ Util::ptr_shared<char> stringify_node(Node const &node) {
     return result;
 }
 
-Util::ptr_shared<char> stringify_unsigned(unsigned n) {
-    gchar *string = g_strdup_printf("%u", n);
-    Util::ptr_shared<char> result=Util::share_string(string);
-    g_free(string);
-    return result;
-}
+typedef Debug::SimpleEvent<Debug::Event::XML> DebugXML;
 
-}
+class DebugXMLNode : public DebugXML {
+public:
+    DebugXMLNode(Node const &node, Util::ptr_shared<char> name)
+    : DebugXML(name)
+    {
+        _addProperty("node", stringify_node(node));
+    }
+};
 
-class DebugAddChild : public Debug::Event {
+class DebugAddChild : public DebugXMLNode {
 public:
     DebugAddChild(Node const &node, Node const &child, Node const *prev)
-    : _parent(stringify_node(node)),
-      _child(stringify_node(child)),
-      _position(prev ? prev->position() + 1 : 0)
-    {}
-
-    static Category category() { return XML; }
-
-    Util::ptr_shared<char> name() const {
-        return Util::share_static_string("add-child");
-    }
-    unsigned propertyCount() const { return 3; }
-    PropertyPair property(unsigned i) const {
-        switch (i) {
-        case 0:
-            return PropertyPair("parent", _parent);
-        case 1:
-            return PropertyPair("child", _child);
-        case 2:
-            return PropertyPair("position", stringify_unsigned(_position));
-        default:
-            return PropertyPair();
-        }
+    : DebugXMLNode(node, Util::share_static_string("add-child"))
+    {
+        _addProperty("child", stringify_node(child));
+        _addProperty("position", Util::format("%d", ( prev ? prev->position() + 1 : 0 )));
     }
-private:
-    Util::ptr_shared<char> _parent;
-    Util::ptr_shared<char> _child;
-    unsigned _position;
 };
 
-class DebugRemoveChild : public Debug::Event {
+class DebugRemoveChild : public DebugXMLNode {
 public:
-    DebugRemoveChild(Node const &node, Node const &child, Node const *prev)
-    : _parent(stringify_node(node)),
-      _child(stringify_node(child))
-    {}
-
-    static Category category() { return XML; }
-
-    Util::ptr_shared<char> name() const {
-        return Util::share_static_string("remove-child");
-    }
-    unsigned propertyCount() const { return 2; }
-    PropertyPair property(unsigned i) const {
-        switch (i) {
-        case 0:
-            return PropertyPair("parent", _parent);
-        case 1:
-            return PropertyPair("child", _child);
-        default:
-            return PropertyPair();
-        }
+    DebugRemoveChild(Node const &node, Node const &child)
+    : DebugXMLNode(node, Util::share_static_string("remove-child"))
+    {
+        _addProperty("child", stringify_node(child));
     }
-private:
-    Util::ptr_shared<char> _parent;
-    Util::ptr_shared<char> _child;
 };
 
-class DebugSetChildPosition : public Debug::Event {
+class DebugSetChildPosition : public DebugXMLNode {
 public:
-    DebugSetChildPosition(Node const &node, Node const &child, Node const *old_prev, Node const *new_prev)
-    : _parent(stringify_node(node)),
-      _child(stringify_node(child))
+    DebugSetChildPosition(Node const &node, Node const &child,
+                          Node const *old_prev, Node const *new_prev)
+    : DebugXMLNode(node, Util::share_static_string("set-child-position"))
     {
+        _addProperty("child", stringify_node(child));
+
         unsigned old_position = ( old_prev ? old_prev->position() : 0 );
-        _position = ( new_prev ? new_prev->position() : 0 );
-        if ( _position > old_position ) {
-            --_position;
+        unsigned position = ( new_prev ? new_prev->position() : 0 );
+        if ( position > old_position ) {
+            --position;
         }
-    }
-
-    static Category category() { return XML; }
 
-    Util::ptr_shared<char> name() const {
-        return Util::share_static_string("set-child-position");
+        _addProperty("position", Util::format("%d", position));
     }
-    unsigned propertyCount() const { return 3; }
-    PropertyPair property(unsigned i) const {
-        switch (i) {
-        case 0:
-            return PropertyPair("parent", _parent);
-        case 1:
-            return PropertyPair("child", _child);
-        case 2:
-            return PropertyPair("position", stringify_unsigned(_position));
-        default:
-            return PropertyPair();
-        }
-    }
-private:
-    Util::ptr_shared<char> _parent;
-    Util::ptr_shared<char> _child;
-    unsigned _position;
 };
 
-class DebugSetContent : public Debug::Event {
+class DebugSetContent : public DebugXMLNode {
 public:
     DebugSetContent(Node const &node,
-                    Util::ptr_shared<char> old_content,
-                    Util::ptr_shared<char> new_content)
-    : _node(stringify_node(node)), _content(new_content) {}
+                    Util::ptr_shared<char> content)
+    : DebugXMLNode(node, Util::share_static_string("set-content"))
+    {
+        _addProperty("content", content);
+    }
+};
 
-    static Category category() { return XML; }
+class DebugClearContent : public DebugXMLNode {
+public:
+    DebugClearContent(Node const &node)
+    : DebugXMLNode(node, Util::share_static_string("clear-content"))
+    {}
+};
 
-    Util::ptr_shared<char> name() const {
-        if (_content) {
-            return Util::share_static_string("set-content");
-        } else {
-            return Util::share_static_string("clear-content");
-        }
-    }
-    unsigned propertyCount() const {
-        if (_content) {
-            return 2;
-        } else {
-            return 1;
-        }
-    }
-    PropertyPair property(unsigned i) const {
-        switch (i) {
-        case 0:
-            return PropertyPair("node", _node);
-        case 1:
-            return PropertyPair("content", _content);
-        default:
-            return PropertyPair();
-        }
+class DebugSetAttribute : public DebugXMLNode {
+public:
+    DebugSetAttribute(Node const &node,
+                      GQuark name,
+                      Util::ptr_shared<char> value)
+    : DebugXMLNode(node, Util::share_static_string("set-attribute"))
+    {
+        _addProperty("name", Util::share_static_string(g_quark_to_string(name)));
+        _addProperty("value", value);
     }
-private:
-    Util::ptr_shared<char> _node;
-    Util::ptr_shared<char> _content;
 };
 
-class DebugSetAttribute : public Debug::Event {
+class DebugClearAttribute : public DebugXMLNode {
 public:
-    DebugSetAttribute(Node const &node, GQuark name,
-                      Util::ptr_shared<char> old_value,
-                      Util::ptr_shared<char> new_value)
-    : _node(stringify_node(node)),
-      _name(Util::share_unsafe(g_quark_to_string(name))),
-      _value(new_value) {}
-
-    static Category category() { return XML; }
-
-    Util::ptr_shared<char> name() const {
-        if (_value) {
-            return Util::share_static_string("set-attribute");
-        } else {
-            return Util::share_static_string("clear-attribute");
-        }
-    }
-    unsigned propertyCount() const {
-        if (_value) {
-            return 3;
-        } else {
-            return 2;
-        }
-    }
-    PropertyPair property(unsigned i) const {
-        switch (i) {
-        case 0:
-            return PropertyPair("node", _node);
-        case 1:
-            return PropertyPair("name", _name);
-        case 2:
-            return PropertyPair("value", _value);
-        default:
-            return PropertyPair();
-        }
+    DebugClearAttribute(Node const &node, GQuark name)
+    : DebugXMLNode(node, Util::share_static_string("clear-attribute"))
+    {
+        _addProperty("name", Util::share_static_string(g_quark_to_string(name)));
     }
-
-private:
-    Util::ptr_shared<char> _node;
-    Util::ptr_shared<char> _name;
-    Util::ptr_shared<char> _value;
 };
 
-using Inkscape::Util::ptr_shared;
-using Inkscape::Util::share_string;
-using Inkscape::Util::share_unsafe;
-using Inkscape::Util::share_static_string;
-using Inkscape::Util::List;
-using Inkscape::Util::MutableList;
-using Inkscape::Util::cons;
-using Inkscape::Util::rest;
-using Inkscape::Util::set_rest;
-
-SimpleNode::SimpleNode(int code)
+}
+
+using Util::ptr_shared;
+using Util::share_string;
+using Util::share_unsafe;
+using Util::share_static_string;
+using Util::List;
+using Util::MutableList;
+using Util::cons;
+using Util::rest;
+using Util::set_rest;
+
+SimpleNode::SimpleNode(int code, Document *document)
 : Node(), _name(code), _attributes(), _child_count(0),
   _cached_positions_valid(false)
 {
-    this->_logger = NULL;
-    this->_document = NULL;
+    g_assert(document != NULL);
+
+    this->_document = document;
     this->_parent = this->_next = NULL;
     this->_first_child = this->_last_child = NULL;
+
+    _observers.add(_subtree_observers);
 }
 
-SimpleNode::SimpleNode(SimpleNode const &node)
+SimpleNode::SimpleNode(SimpleNode const &node, Document *document)
 : Node(),
   _cached_position(node._cached_position),
   _name(node._name), _attributes(), _content(node._content),
   _child_count(node._child_count),
   _cached_positions_valid(node._cached_positions_valid)
 {
-    _logger = NULL;
-    _document = NULL;
+    g_assert(document != NULL);
+
+    _document = document;
     _parent = _next = NULL;
     _first_child = _last_child = NULL;
 
-    for ( Node *child = node._first_child ;
-          child != NULL ; child = child->next() )
+    for ( SimpleNode *child = node._first_child ;
+          child != NULL ; child = child->_next )
     {
-        Node *child_copy=child->duplicate();
+        SimpleNode *child_copy=dynamic_cast<SimpleNode *>(child->duplicate(document));
 
         child_copy->_setParent(this);
         if (_last_child) {
-            _last_child->_setNext(child_copy);
+            _last_child->_next = child_copy;
         } else {
             _first_child = child_copy;
         }
@@ -297,6 +206,8 @@ SimpleNode::SimpleNode(SimpleNode const &node)
     {
         _attributes = cons(*iter, _attributes);
     }
+
+    _observers.add(_subtree_observers);
 }
 
 gchar const *SimpleNode::name() const {
@@ -328,23 +239,23 @@ unsigned SimpleNode::position() const {
     return _parent->_childPosition(*this);
 }
 
-unsigned SimpleNode::_childPosition(Node const &child) const {
+unsigned SimpleNode::_childPosition(SimpleNode const &child) const {
     if (!_cached_positions_valid) {
         unsigned position=0;
-        for ( Node *sibling = _first_child ;
-              sibling ; sibling = sibling->next() )
+        for ( SimpleNode *sibling = _first_child ;
+              sibling ; sibling = sibling->_next )
         {
-            sibling->_setCachedPosition(position);
+            sibling->_cached_position = position;
             position++;
         }
         _cached_positions_valid = true;
     }
-    return child._cachedPosition();
+    return child._cached_position;
 }
 
 Node *SimpleNode::nthChild(unsigned index) {
-    Node *child = _first_child;
-    for ( ; index > 0 && child ; child = child->next() ) {
+    SimpleNode *child = _first_child;
+    for ( ; index > 0 && child ; child = child->_next ) {
         index--;
     }
     return child;
@@ -365,27 +276,37 @@ bool SimpleNode::matchAttributeName(gchar const *partial_name) const {
     return false;
 }
 
+void SimpleNode::_setParent(SimpleNode *parent) {
+    if (_parent) {
+        _subtree_observers.remove(_parent->_subtree_observers);
+    }
+    _parent = parent;
+    if (parent) {
+        _subtree_observers.add(parent->_subtree_observers);
+    }
+}
+
 void SimpleNode::setContent(gchar const *content) {
     ptr_shared<char> old_content=_content;
     ptr_shared<char> new_content = ( content ? share_string(content) : ptr_shared<char>() );
 
-    Debug::EventTracker<DebugSetContent> tracker(
-        *this, old_content, new_content
-    );
+    Debug::EventTracker<> tracker;
+    if (new_content) {
+        tracker.set<DebugSetContent>(*this, new_content);
+    } else {
+        tracker.set<DebugClearContent>(*this);
+    }
 
     _content = new_content;
 
     if ( _content != old_content ) {
-        if (_logger) {
-            _logger->notifyContentChanged(*this, old_content, _content);
-        }
-
+        _document->logger()->notifyContentChanged(*this, old_content, _content);
         _observers.notifyContentChanged(*this, old_content, _content);
     }
 }
 
 void
-SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const is_interactive)
+SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const /*is_interactive*/)
 {
     g_return_if_fail(name && *name);
 
@@ -407,7 +328,7 @@ SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const is_in
     ptr_shared<char> new_value=ptr_shared<char>();
     if (value) {
         new_value = share_string(value);
-        tracker.set<DebugSetAttribute>(*this, key, old_value, new_value);
+        tracker.set<DebugSetAttribute>(*this, key, new_value);
         if (!existing) {
             if (ref) {
                 set_rest(ref, MutableList<AttributeRecord>(AttributeRecord(key, new_value)));
@@ -418,7 +339,7 @@ SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const is_in
             existing->value = new_value;
         }
     } else {
-        tracker.set<DebugSetAttribute>(*this, key, old_value, new_value);
+        tracker.set<DebugClearAttribute>(*this, key);
         if (existing) {
             if (ref) {
                 set_rest(ref, rest(existing));
@@ -430,25 +351,28 @@ SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const is_in
     }
 
     if ( new_value != old_value && (!old_value || !new_value || strcmp(old_value, new_value))) {
-        if (_logger) {
-            _logger->notifyAttributeChanged(*this, key, old_value, new_value);
-        }
-
+        _document->logger()->notifyAttributeChanged(*this, key, old_value, new_value);
         _observers.notifyAttributeChanged(*this, key, old_value, new_value);
     }
 }
 
-void SimpleNode::addChild(Node *child, Node *ref) {
-    g_assert(child);
-    g_assert(!ref || ref->parent() == this);
-    g_assert(!child->parent());
+void SimpleNode::addChild(Node *generic_child, Node *generic_ref) {
+    g_assert(generic_child);
+    g_assert(generic_child->document() == _document);
+    g_assert(!generic_ref || generic_ref->document() == _document);
+
+    SimpleNode *child=dynamic_cast<SimpleNode *>(generic_child);
+    SimpleNode *ref=dynamic_cast<SimpleNode *>(generic_ref);
+
+    g_assert(!ref || ref->_parent == this);
+    g_assert(!child->_parent);
 
     Debug::EventTracker<DebugAddChild> tracker(*this, *child, ref);
 
-    Node *next;
+    SimpleNode *next;
     if (ref) {
-        next = ref->next();
-        ref->_setNext(child);
+        next = ref->_next;
+        ref->_next = child;
     } else {
         next = _first_child;
         _first_child = child;
@@ -458,10 +382,10 @@ void SimpleNode::addChild(Node *child, Node *ref) {
         // set cached position if possible when appending
         if (!ref) {
             // if !next && !ref, child is sole child
-            child->_setCachedPosition(0);
+            child->_cached_position = 0;
             _cached_positions_valid = true;
         } else if (_cached_positions_valid) {
-            child->_setCachedPosition(ref->_cachedPosition() + 1);
+            child->_cached_position = ref->_cached_position + 1;
         }
     } else {
         // invalidate cached positions otherwise
@@ -469,55 +393,27 @@ void SimpleNode::addChild(Node *child, Node *ref) {
     }
 
     child->_setParent(this);
-    child->_setNext(next);
+    child->_next = next;
     _child_count++;
 
-    if (_document) {
-        child->_bindDocument(*_document);
-    }
-    if (_logger) {
-        child->_bindLogger(*_logger);
-        _logger->notifyChildAdded(*this, *child, ref);
-    }
-
+    _document->logger()->notifyChildAdded(*this, *child, ref);
     _observers.notifyChildAdded(*this, *child, ref);
 }
 
-void SimpleNode::_bindDocument(Document &document) {
-    g_assert(!_document || _document == &document);
-
-    if (!_document) {
-        _document = &document;
+void SimpleNode::removeChild(Node *generic_child) {
+    g_assert(generic_child);
+    g_assert(generic_child->document() == _document);
 
-        for ( Node *child = _first_child ; child != NULL ; child = child->next() ) {
-            child->_bindDocument(document);
-        }
-    }
-}
+    SimpleNode *child=dynamic_cast<SimpleNode *>(generic_child);
+    SimpleNode *ref=dynamic_cast<SimpleNode *>(previous_node(child));
 
-void SimpleNode::_bindLogger(TransactionLogger &logger) {
-    g_assert(!_logger || _logger == &logger);
+    g_assert(child->_parent == this);
 
-    if (!_logger) {
-        _logger = &logger;
+    Debug::EventTracker<DebugRemoveChild> tracker(*this, *child);
 
-        for ( Node *child = _first_child ; child != NULL ; child = child->next() ) {
-            child->_bindLogger(logger);
-        }
-    }
-}
-
-void SimpleNode::removeChild(Node *child) {
-    g_assert(child);
-    g_assert(child->parent() == this);
-
-    Node *ref = ( child != _first_child ? previous_node(child) : NULL );
-
-    Debug::EventTracker<DebugRemoveChild> tracker(*this, *child, ref);
-
-    Node *next = child->next();
+    SimpleNode *next = child->_next;
     if (ref) {
-        ref->_setNext(next);
+        ref->_next = next;
     } else {
         _first_child = next;
     }
@@ -528,35 +424,38 @@ void SimpleNode::removeChild(Node *child) {
         _cached_positions_valid = false;
     }
 
-    child->_setNext(NULL);
+    child->_next = NULL;
     child->_setParent(NULL);
     _child_count--;
 
-    if (_logger) {
-        _logger->notifyChildRemoved(*this, *child, ref);
-    }
-
+    _document->logger()->notifyChildRemoved(*this, *child, ref);
     _observers.notifyChildRemoved(*this, *child, ref);
 }
 
-void SimpleNode::changeOrder(Node *child, Node *ref) {
-    g_return_if_fail(child);
+void SimpleNode::changeOrder(Node *generic_child, Node *generic_ref) {
+    g_assert(generic_child);
+    g_assert(generic_child->document() == this->_document);
+    g_assert(!generic_ref || generic_ref->document() == this->_document);
+
+    SimpleNode *const child=dynamic_cast<SimpleNode *>(generic_child);
+    SimpleNode *const ref=dynamic_cast<SimpleNode *>(generic_ref);
+
     g_return_if_fail(child->parent() == this);
     g_return_if_fail(child != ref);
     g_return_if_fail(!ref || ref->parent() == this);
 
-    Node *const prev = previous_node(child);
+    SimpleNode *const prev=dynamic_cast<SimpleNode *>(previous_node(child));
 
     Debug::EventTracker<DebugSetChildPosition> tracker(*this, *child, prev, ref);
 
     if (prev == ref) { return; }
 
-    Node *next;
+    SimpleNode *next;
 
     /* Remove from old position. */
-    next=child->next();
+    next = child->_next;
     if (prev) {
-        prev->_setNext(next);
+        prev->_next = next;
     } else {
         _first_child = next;
     }
@@ -566,23 +465,20 @@ void SimpleNode::changeOrder(Node *child, Node *ref) {
 
     /* Insert at new position. */
     if (ref) {
-        next = ref->next();
-        ref->_setNext(child);
+        next = ref->_next;
+        ref->_next = child;
     } else {
         next = _first_child;
         _first_child = child;
     }
-    child->_setNext(next);
+    child->_next = next;
     if (!next) {
         _last_child = child;
     }
 
     _cached_positions_valid = false;
 
-    if (_logger) {
-        _logger->notifyChildOrderChanged(*this, *child, prev, ref);
-    }
-
+    _document->logger()->notifyChildOrderChanged(*this, *child, prev, ref);
     _observers.notifyChildOrderChanged(*this, *child, prev, ref);
 }
 
@@ -592,9 +488,9 @@ void SimpleNode::setPosition(int pos) {
     // a position beyond the end of the list means the end of the list;
     // a negative position is the same as an infinitely large position
 
-    Node *ref=NULL;
-    for ( Node *sibling = _parent->firstChild() ;
-          sibling && pos ; sibling = sibling->next() )
+    SimpleNode *ref=NULL;
+    for ( SimpleNode *sibling = _parent->_first_child ;
+          sibling && pos ; sibling = sibling->_next )
     {
         if ( sibling != this ) {
             ref = sibling;
@@ -619,7 +515,7 @@ void content_changed(Node *node, gchar const *old_content, gchar const *new_cont
     reinterpret_cast<NodeObserver *>(data)->notifyContentChanged(*node, Util::share_unsafe((const char *)old_content), Util::share_unsafe((const char *)new_content));
 }
 
-void attr_changed(Node *node, gchar const *name, gchar const *old_value, gchar const *new_value, bool is_interactive, void *data) {
+void attr_changed(Node *node, gchar const *name, gchar const *old_value, gchar const *new_value, bool /*is_interactive*/, void *data) {
     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));
 }
 
@@ -646,9 +542,9 @@ void SimpleNode::synthesizeEvents(NodeEventVector const *vector, void *data) {
         }
     }
     if (vector->child_added) {
-        Node *ref = NULL;
-        for ( Node *child = this->_first_child ;
-              child ; child = child->next() )
+        SimpleNode *ref = NULL;
+        for ( SimpleNode *child = this->_first_child ;
+              child ; child = child->_next )
         {
             vector->child_added(this, child, ref, data);
             ref = child;
@@ -700,12 +596,12 @@ void SimpleNode::mergeFrom(Node const *src, gchar const *key) {
             if (rch) {
                 rch->mergeFrom(child, key);
             } else {
-                rch = child->duplicate();
+                rch = child->duplicate(_document);
                 appendChild(rch);
                 rch->release();
             }
         } else {
-            Node *rch=child->duplicate();
+            Node *rch=child->duplicate(_document);
             appendChild(rch);
             rch->release();
         }
@@ -731,4 +627,4 @@ void SimpleNode::mergeFrom(Node const *src, gchar const *key) {
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :