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)
164 : Node(), _name(code), _attributes(), _child_count(0),
165 _cached_positions_valid(false)
166 {
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);
173 }
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)
181 {
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);
209 }
211 gchar const *SimpleNode::name() const {
212 return g_quark_to_string(_name);
213 }
215 gchar const *SimpleNode::content() const {
216 return this->_content;
217 }
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;
233 }
235 unsigned SimpleNode::position() const {
236 g_return_val_if_fail(_parent != NULL, 0);
237 return _parent->_childPosition(*this);
238 }
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();
252 }
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;
260 }
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;
275 }
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 }
285 }
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 }
307 }
309 void
310 SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const /*is_interactive*/)
311 {
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 }
361 }
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);
403 }
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 }
415 }
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);
447 }
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);
494 }
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);
513 }
515 namespace {
517 void child_added(Node *node, Node *child, Node *ref, void *data) {
518 reinterpret_cast<NodeObserver *>(data)->notifyChildAdded(*node, *child, ref);
519 }
521 void child_removed(Node *node, Node *child, Node *ref, void *data) {
522 reinterpret_cast<NodeObserver *>(data)->notifyChildRemoved(*node, *child, ref);
523 }
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));
527 }
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));
531 }
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);
535 }
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 }
567 }
569 void SimpleNode::synthesizeEvents(NodeObserver &observer) {
570 synthesizeEvents(&OBSERVER_EVENT_VECTOR, &observer);
571 }
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 }
593 }
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 }
626 }
628 }
630 }
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 :