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 };
151 }
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)
166 {
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);
174 }
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)
182 {
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);
212 }
214 gchar const *SimpleNode::name() const {
215 return g_quark_to_string(_name);
216 }
218 gchar const *SimpleNode::content() const {
219 return this->_content;
220 }
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;
236 }
238 unsigned SimpleNode::position() const {
239 g_return_val_if_fail(_parent != NULL, 0);
240 return _parent->_childPosition(*this);
241 }
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();
255 }
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;
263 }
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;
278 }
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 }
288 }
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 }
310 }
312 void
313 SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const /*is_interactive*/)
314 {
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 }
364 }
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);
406 }
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 }
418 }
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);
450 }
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);
497 }
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);
516 }
518 namespace {
520 void child_added(Node *node, Node *child, Node *ref, void *data) {
521 reinterpret_cast<NodeObserver *>(data)->notifyChildAdded(*node, *child, ref);
522 }
524 void child_removed(Node *node, Node *child, Node *ref, void *data) {
525 reinterpret_cast<NodeObserver *>(data)->notifyChildRemoved(*node, *child, ref);
526 }
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));
530 }
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));
534 }
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);
538 }
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 }
570 }
572 void SimpleNode::synthesizeEvents(NodeObserver &observer) {
573 synthesizeEvents(&OBSERVER_EVENT_VECTOR, &observer);
574 }
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 }
596 }
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 }
629 }
631 }
633 }
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 :