554752941fbca4c34fc735a66cfb54a2996a02ea
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 };
148 }
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)
163 {
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);
170 }
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)
178 {
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);
206 }
208 gchar const *SimpleNode::name() const {
209 return g_quark_to_string(_name);
210 }
212 gchar const *SimpleNode::content() const {
213 return this->_content;
214 }
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;
230 }
232 unsigned SimpleNode::position() const {
233 g_return_val_if_fail(_parent != NULL, 0);
234 return _parent->_childPosition(*this);
235 }
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();
249 }
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;
257 }
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;
272 }
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 }
282 }
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 }
304 }
306 void
307 SimpleNode::setAttribute(gchar const *name, gchar const *value, bool const is_interactive)
308 {
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 }
358 }
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);
400 }
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 }
412 }
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);
444 }
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);
491 }
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);
510 }
512 namespace {
514 void child_added(Node *node, Node *child, Node *ref, void *data) {
515 reinterpret_cast<NodeObserver *>(data)->notifyChildAdded(*node, *child, ref);
516 }
518 void child_removed(Node *node, Node *child, Node *ref, void *data) {
519 reinterpret_cast<NodeObserver *>(data)->notifyChildRemoved(*node, *child, ref);
520 }
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));
524 }
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));
528 }
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);
532 }
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 }
564 }
566 void SimpleNode::synthesizeEvents(NodeObserver &observer) {
567 synthesizeEvents(&OBSERVER_EVENT_VECTOR, &observer);
568 }
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 }
590 }
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 }
623 }
625 }
627 }
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 :