d83e5d54bac2cc72519392543fa5282a75bc876b
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"
25 namespace Inkscape {
27 namespace XML {
29 namespace {
31 Util::shared_ptr<char> stringify_node(Node const &node) {
32 gchar *string;
33 switch (node.type()) {
34 case ELEMENT_NODE: {
35 char const *id=node.attribute("id");
36 if (id) {
37 string = g_strdup_printf("element(%p)=%s(#%s)", &node, node.name(), id);
38 } else {
39 string = g_strdup_printf("element(%p)=%s", &node, node.name());
40 }
41 } break;
42 case TEXT_NODE:
43 string = g_strdup_printf("text(%p)=%s", &node, node.content());
44 break;
45 case COMMENT_NODE:
46 string = g_strdup_printf("comment(%p)=<!--%s-->", &node, node.content());
47 break;
48 case DOCUMENT_NODE:
49 string = g_strdup_printf("document(%p)", &node);
50 break;
51 default:
52 string = g_strdup_printf("unknown(%p)", &node);
53 }
54 Util::shared_ptr<char> result=Util::share_string(string);
55 g_free(string);
56 return result;
57 }
59 Util::shared_ptr<char> stringify_unsigned(unsigned n) {
60 gchar *string = g_strdup_printf("%u", n);
61 Util::shared_ptr<char> result=Util::share_string(string);
62 g_free(string);
63 return result;
64 }
66 }
68 class DebugAddChild : public Debug::Event {
69 public:
70 DebugAddChild(Node const &node, Node const &child, Node const *prev)
71 : _parent(stringify_node(node)),
72 _child(stringify_node(child)),
73 _position(prev ? prev->position() + 1 : 0)
74 {}
76 static Category category() { return XML; }
78 Util::shared_ptr<char> name() const {
79 return Util::share_static_string("add-child");
80 }
81 unsigned propertyCount() const { return 3; }
82 PropertyPair property(unsigned i) const {
83 switch (i) {
84 case 0:
85 return PropertyPair("parent", _parent);
86 case 1:
87 return PropertyPair("child", _child);
88 case 2:
89 return PropertyPair("position", stringify_unsigned(_position));
90 default:
91 return PropertyPair();
92 }
93 }
94 private:
95 Util::shared_ptr<char> _parent;
96 Util::shared_ptr<char> _child;
97 unsigned _position;
98 };
100 class DebugRemoveChild : public Debug::Event {
101 public:
102 DebugRemoveChild(Node const &node, Node const &child, Node const *prev)
103 : _parent(stringify_node(node)),
104 _child(stringify_node(child))
105 {}
107 static Category category() { return XML; }
109 Util::shared_ptr<char> name() const {
110 return Util::share_static_string("remove-child");
111 }
112 unsigned propertyCount() const { return 2; }
113 PropertyPair property(unsigned i) const {
114 switch (i) {
115 case 0:
116 return PropertyPair("parent", _parent);
117 case 1:
118 return PropertyPair("child", _child);
119 default:
120 return PropertyPair();
121 }
122 }
123 private:
124 Util::shared_ptr<char> _parent;
125 Util::shared_ptr<char> _child;
126 };
128 class DebugSetChildPosition : public Debug::Event {
129 public:
130 DebugSetChildPosition(Node const &node, Node const &child, Node const *old_prev, Node const *new_prev)
131 : _parent(stringify_node(node)),
132 _child(stringify_node(child))
133 {
134 unsigned old_position = ( old_prev ? old_prev->position() : 0 );
135 _position = ( new_prev ? new_prev->position() : 0 );
136 if ( _position > old_position ) {
137 --_position;
138 }
139 }
141 static Category category() { return XML; }
143 Util::shared_ptr<char> name() const {
144 return Util::share_static_string("set-child-position");
145 }
146 unsigned propertyCount() const { return 3; }
147 PropertyPair property(unsigned i) const {
148 switch (i) {
149 case 0:
150 return PropertyPair("parent", _parent);
151 case 1:
152 return PropertyPair("child", _child);
153 case 2:
154 return PropertyPair("position", stringify_unsigned(_position));
155 default:
156 return PropertyPair();
157 }
158 }
159 private:
160 Util::shared_ptr<char> _parent;
161 Util::shared_ptr<char> _child;
162 unsigned _position;
163 };
165 class DebugSetContent : public Debug::Event {
166 public:
167 DebugSetContent(Node const &node,
168 Util::shared_ptr<char> old_content,
169 Util::shared_ptr<char> new_content)
170 : _node(stringify_node(node)), _content(new_content) {}
172 static Category category() { return XML; }
174 Util::shared_ptr<char> name() const {
175 if (_content) {
176 return Util::share_static_string("set-content");
177 } else {
178 return Util::share_static_string("clear-content");
179 }
180 }
181 unsigned propertyCount() const {
182 if (_content) {
183 return 2;
184 } else {
185 return 1;
186 }
187 }
188 PropertyPair property(unsigned i) const {
189 switch (i) {
190 case 0:
191 return PropertyPair("node", _node);
192 case 1:
193 return PropertyPair("content", _content);
194 default:
195 return PropertyPair();
196 }
197 }
198 private:
199 Util::shared_ptr<char> _node;
200 Util::shared_ptr<char> _content;
201 };
203 class DebugSetAttribute : public Debug::Event {
204 public:
205 DebugSetAttribute(Node const &node, GQuark name,
206 Util::shared_ptr<char> old_value,
207 Util::shared_ptr<char> new_value)
208 : _node(stringify_node(node)),
209 _name(Util::share_unsafe(g_quark_to_string(name))),
210 _value(new_value) {}
212 static Category category() { return XML; }
214 Util::shared_ptr<char> name() const {
215 if (_value) {
216 return Util::share_static_string("set-attribute");
217 } else {
218 return Util::share_static_string("clear-attribute");
219 }
220 }
221 unsigned propertyCount() const {
222 if (_value) {
223 return 3;
224 } else {
225 return 2;
226 }
227 }
228 PropertyPair property(unsigned i) const {
229 switch (i) {
230 case 0:
231 return PropertyPair("node", _node);
232 case 1:
233 return PropertyPair("name", _name);
234 case 2:
235 return PropertyPair("value", _value);
236 default:
237 return PropertyPair();
238 }
239 }
241 private:
242 Util::shared_ptr<char> _node;
243 Util::shared_ptr<char> _name;
244 Util::shared_ptr<char> _value;
245 };
247 using Inkscape::Util::shared_ptr;
248 using Inkscape::Util::share_string;
249 using Inkscape::Util::share_unsafe;
250 using Inkscape::Util::share_static_string;
251 using Inkscape::Util::List;
252 using Inkscape::Util::MutableList;
253 using Inkscape::Util::cons;
254 using Inkscape::Util::rest;
255 using Inkscape::Util::set_rest;
257 SimpleNode::SimpleNode(int code)
258 : Node(), _name(code), _attributes(), _child_count(0),
259 _cached_positions_valid(false)
260 {
261 this->_logger = NULL;
262 this->_document = NULL;
263 this->_parent = this->_next = NULL;
264 this->_first_child = this->_last_child = NULL;
265 }
267 SimpleNode::SimpleNode(SimpleNode const &node)
268 : Node(),
269 _cached_position(node._cached_position),
270 _name(node._name), _attributes(), _content(node._content),
271 _child_count(node._child_count),
272 _cached_positions_valid(node._cached_positions_valid)
273 {
274 _logger = NULL;
275 _document = NULL;
276 _parent = _next = NULL;
277 _first_child = _last_child = NULL;
279 for ( Node *child = node._first_child ;
280 child != NULL ; child = child->next() )
281 {
282 Node *child_copy=child->duplicate();
284 child_copy->_setParent(this);
285 if (_last_child) {
286 _last_child->_setNext(child_copy);
287 } else {
288 _first_child = child_copy;
289 }
290 _last_child = child_copy;
292 child_copy->release(); // release to avoid a leak
293 }
295 for ( List<AttributeRecord const> iter = node._attributes ;
296 iter ; ++iter )
297 {
298 _attributes = cons(*iter, _attributes);
299 }
300 }
302 gchar const *SimpleNode::name() const {
303 return g_quark_to_string(_name);
304 }
306 gchar const *SimpleNode::content() const {
307 return this->_content;
308 }
310 gchar const *SimpleNode::attribute(gchar const *name) const {
311 g_return_val_if_fail(name != NULL, NULL);
313 GQuark const key = g_quark_from_string(name);
315 for ( List<AttributeRecord const> iter = _attributes ;
316 iter ; ++iter )
317 {
318 if ( iter->key == key ) {
319 return iter->value;
320 }
321 }
323 return NULL;
324 }
326 unsigned SimpleNode::position() const {
327 g_return_val_if_fail(_parent != NULL, 0);
328 return _parent->_childPosition(*this);
329 }
331 unsigned SimpleNode::_childPosition(Node const &child) const {
332 if (!_cached_positions_valid) {
333 unsigned position=0;
334 for ( Node *sibling = _first_child ;
335 sibling ; sibling = sibling->next() )
336 {
337 sibling->_setCachedPosition(position);
338 position++;
339 }
340 _cached_positions_valid = true;
341 }
342 return child._cachedPosition();
343 }
345 Node *SimpleNode::nthChild(unsigned index) {
346 Node *child = _first_child;
347 for ( ; index > 0 && child ; child = child->next() ) {
348 index--;
349 }
350 return child;
351 }
353 bool SimpleNode::matchAttributeName(gchar const *partial_name) const {
354 g_return_val_if_fail(partial_name != NULL, false);
356 for ( List<AttributeRecord const> iter = _attributes ;
357 iter ; ++iter )
358 {
359 gchar const *name = g_quark_to_string(iter->key);
360 if (std::strstr(name, partial_name)) {
361 return true;
362 }
363 }
365 return false;
366 }
368 void SimpleNode::setContent(gchar const *content) {
369 shared_ptr<char> old_content=_content;
370 shared_ptr<char> new_content = ( content ? share_string(content) : shared_ptr<char>() );
372 Debug::EventTracker<DebugSetContent> tracker(
373 *this, old_content, new_content
374 );
376 _content = new_content;
378 if ( _content != old_content ) {
379 if (_logger) {
380 _logger->notifyContentChanged(*this, old_content, _content);
381 }
383 _observers.notifyContentChanged(*this, old_content, _content);
384 }
385 }
387 void
388 SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const is_interactive)
389 {
390 g_return_if_fail(name && *name);
392 GQuark const key = g_quark_from_string(name);
394 MutableList<AttributeRecord> ref;
395 MutableList<AttributeRecord> existing;
396 for ( existing = _attributes ; existing ; ++existing ) {
397 if ( existing->key == key ) {
398 break;
399 }
400 ref = existing;
401 }
403 Debug::EventTracker<> tracker;
405 shared_ptr<char> old_value=( existing ? existing->value : shared_ptr<char>() );
407 shared_ptr<char> new_value=shared_ptr<char>();
408 if (value) {
409 new_value = share_string(value);
410 tracker.set<DebugSetAttribute>(*this, key, old_value, new_value);
411 if (!existing) {
412 if (ref) {
413 set_rest(ref, MutableList<AttributeRecord>(AttributeRecord(key, new_value)));
414 } else {
415 _attributes = MutableList<AttributeRecord>(AttributeRecord(key, new_value));
416 }
417 } else {
418 existing->value = new_value;
419 }
420 } else {
421 tracker.set<DebugSetAttribute>(*this, key, old_value, new_value);
422 if (existing) {
423 if (ref) {
424 set_rest(ref, rest(existing));
425 } else {
426 _attributes = rest(existing);
427 }
428 set_rest(existing, MutableList<AttributeRecord>());
429 }
430 }
432 if ( new_value != old_value && (!old_value || !new_value || strcmp(old_value, new_value))) {
433 if (_logger) {
434 _logger->notifyAttributeChanged(*this, key, old_value, new_value);
435 }
437 _observers.notifyAttributeChanged(*this, key, old_value, new_value);
438 }
439 }
441 void SimpleNode::addChild(Node *child, Node *ref) {
442 g_assert(child);
443 g_assert(!ref || ref->parent() == this);
444 g_assert(!child->parent());
446 Debug::EventTracker<DebugAddChild> tracker(*this, *child, ref);
448 Node *next;
449 if (ref) {
450 next = ref->next();
451 ref->_setNext(child);
452 } else {
453 next = _first_child;
454 _first_child = child;
455 }
456 if (!next) { // appending?
457 _last_child = child;
458 // set cached position if possible when appending
459 if (!ref) {
460 // if !next && !ref, child is sole child
461 child->_setCachedPosition(0);
462 _cached_positions_valid = true;
463 } else if (_cached_positions_valid) {
464 child->_setCachedPosition(ref->_cachedPosition() + 1);
465 }
466 } else {
467 // invalidate cached positions otherwise
468 _cached_positions_valid = false;
469 }
471 child->_setParent(this);
472 child->_setNext(next);
473 _child_count++;
475 if (_document) {
476 child->_bindDocument(*_document);
477 }
478 if (_logger) {
479 child->_bindLogger(*_logger);
480 _logger->notifyChildAdded(*this, *child, ref);
481 }
483 _observers.notifyChildAdded(*this, *child, ref);
484 }
486 void SimpleNode::_bindDocument(Document &document) {
487 g_assert(!_document || _document == &document);
489 if (!_document) {
490 _document = &document;
492 for ( Node *child = _first_child ; child != NULL ; child = child->next() ) {
493 child->_bindDocument(document);
494 }
495 }
496 }
498 void SimpleNode::_bindLogger(TransactionLogger &logger) {
499 g_assert(!_logger || _logger == &logger);
501 if (!_logger) {
502 _logger = &logger;
504 for ( Node *child = _first_child ; child != NULL ; child = child->next() ) {
505 child->_bindLogger(logger);
506 }
507 }
508 }
510 void SimpleNode::removeChild(Node *child) {
511 g_assert(child);
512 g_assert(child->parent() == this);
514 Node *ref = ( child != _first_child ? previous_node(child) : NULL );
516 Debug::EventTracker<DebugRemoveChild> tracker(*this, *child, ref);
518 Node *next = child->next();
519 if (ref) {
520 ref->_setNext(next);
521 } else {
522 _first_child = next;
523 }
524 if (!next) { // removing the last child?
525 _last_child = ref;
526 } else {
527 // removing any other child invalidates the cached positions
528 _cached_positions_valid = false;
529 }
531 child->_setNext(NULL);
532 child->_setParent(NULL);
533 _child_count--;
535 if (_logger) {
536 _logger->notifyChildRemoved(*this, *child, ref);
537 }
539 _observers.notifyChildRemoved(*this, *child, ref);
540 }
542 void SimpleNode::changeOrder(Node *child, Node *ref) {
543 g_return_if_fail(child);
544 g_return_if_fail(child->parent() == this);
545 g_return_if_fail(child != ref);
546 g_return_if_fail(!ref || ref->parent() == this);
548 Node *const prev = previous_node(child);
550 Debug::EventTracker<DebugSetChildPosition> tracker(*this, *child, prev, ref);
552 if (prev == ref) { return; }
554 Node *next;
556 /* Remove from old position. */
557 next=child->next();
558 if (prev) {
559 prev->_setNext(next);
560 } else {
561 _first_child = next;
562 }
563 if (!next) {
564 _last_child = prev;
565 }
567 /* Insert at new position. */
568 if (ref) {
569 next = ref->next();
570 ref->_setNext(child);
571 } else {
572 next = _first_child;
573 _first_child = child;
574 }
575 child->_setNext(next);
576 if (!next) {
577 _last_child = child;
578 }
580 _cached_positions_valid = false;
582 if (_logger) {
583 _logger->notifyChildOrderChanged(*this, *child, prev, ref);
584 }
586 _observers.notifyChildOrderChanged(*this, *child, prev, ref);
587 }
589 void SimpleNode::setPosition(int pos) {
590 g_return_if_fail(_parent != NULL);
592 // a position beyond the end of the list means the end of the list;
593 // a negative position is the same as an infinitely large position
595 Node *ref=NULL;
596 for ( Node *sibling = _parent->firstChild() ;
597 sibling && pos ; sibling = sibling->next() )
598 {
599 if ( sibling != this ) {
600 ref = sibling;
601 pos--;
602 }
603 }
605 _parent->changeOrder(this, ref);
606 }
608 namespace {
610 void child_added(Node *node, Node *child, Node *ref, void *data) {
611 reinterpret_cast<NodeObserver *>(data)->notifyChildAdded(*node, *child, ref);
612 }
614 void child_removed(Node *node, Node *child, Node *ref, void *data) {
615 reinterpret_cast<NodeObserver *>(data)->notifyChildRemoved(*node, *child, ref);
616 }
618 void content_changed(Node *node, gchar const *old_content, gchar const *new_content, void *data) {
619 reinterpret_cast<NodeObserver *>(data)->notifyContentChanged(*node, Util::share_unsafe((const char *)old_content), Util::share_unsafe((const char *)new_content));
620 }
622 void attr_changed(Node *node, gchar const *name, gchar const *old_value, gchar const *new_value, bool is_interactive, void *data) {
623 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));
624 }
626 void order_changed(Node *node, Node *child, Node *old_ref, Node *new_ref, void *data) {
627 reinterpret_cast<NodeObserver *>(data)->notifyChildOrderChanged(*node, *child, old_ref, new_ref);
628 }
630 const NodeEventVector OBSERVER_EVENT_VECTOR = {
631 &child_added,
632 &child_removed,
633 &attr_changed,
634 &content_changed,
635 &order_changed
636 };
638 };
640 void SimpleNode::synthesizeEvents(NodeEventVector const *vector, void *data) {
641 if (vector->attr_changed) {
642 for ( List<AttributeRecord const> iter = _attributes ;
643 iter ; ++iter )
644 {
645 vector->attr_changed(this, g_quark_to_string(iter->key), NULL, iter->value, false, data);
646 }
647 }
648 if (vector->child_added) {
649 Node *ref = NULL;
650 for ( Node *child = this->_first_child ;
651 child ; child = child->next() )
652 {
653 vector->child_added(this, child, ref, data);
654 ref = child;
655 }
656 }
657 if (vector->content_changed) {
658 vector->content_changed(this, NULL, this->_content, data);
659 }
660 }
662 void SimpleNode::synthesizeEvents(NodeObserver &observer) {
663 synthesizeEvents(&OBSERVER_EVENT_VECTOR, &observer);
664 }
666 Node *SimpleNode::root() {
667 Node *parent=this;
668 while (parent->parent()) {
669 parent = parent->parent();
670 }
672 if ( parent->type() == DOCUMENT_NODE ) {
673 for ( Node *child = _document->firstChild() ;
674 child ; child = child->next() )
675 {
676 if ( child->type() == ELEMENT_NODE ) {
677 return child;
678 }
679 }
680 return NULL;
681 } else if ( parent->type() == ELEMENT_NODE ) {
682 return parent;
683 } else {
684 return NULL;
685 }
686 }
688 void SimpleNode::mergeFrom(Node const *src, gchar const *key) {
689 g_return_if_fail(src != NULL);
690 g_return_if_fail(key != NULL);
691 g_assert(src != this);
693 setContent(src->content());
695 for ( Node const *child = src->firstChild() ; child != NULL ; child = child->next() )
696 {
697 gchar const *id = child->attribute(key);
698 if (id) {
699 Node *rch=sp_repr_lookup_child(this, key, id);
700 if (rch) {
701 rch->mergeFrom(child, key);
702 } else {
703 rch = child->duplicate();
704 appendChild(rch);
705 rch->release();
706 }
707 } else {
708 Node *rch=child->duplicate();
709 appendChild(rch);
710 rch->release();
711 }
712 }
714 for ( List<AttributeRecord const> iter = src->attributeList() ;
715 iter ; ++iter )
716 {
717 setAttribute(g_quark_to_string(iter->key), iter->value);
718 }
719 }
721 }
723 }
725 /*
726 Local Variables:
727 mode:c++
728 c-file-style:"stroustrup"
729 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
730 indent-tabs-mode:nil
731 fill-column:99
732 End:
733 */
734 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :