1 /** @file
2 * @brief Interface for XML nodes
3 */
4 /* Authors:
5 * MenTaLguY <mental@rydia.net>
6 * Krzysztof KosiĆski <tweenk.pl@gmail.com> (documentation)
7 *
8 * Copyright 2005-2008 Authors
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * See the file COPYING for details.
16 */
18 #ifndef SEEN_INKSCAPE_XML_NODE_H
19 #define SEEN_INKSCAPE_XML_NODE_H
21 #include <glib/gtypes.h>
22 #include "gc-anchored.h"
23 #include "util/list.h"
24 #include "xml/xml-forward.h"
26 namespace Inkscape {
27 namespace XML {
29 /**
30 * @brief Enumeration containing all supported node types.
31 */
32 enum NodeType {
33 DOCUMENT_NODE, ///< Top-level document node. Do not confuse with the root node.
34 ELEMENT_NODE, ///< Regular element node, e.g. <group />.
35 TEXT_NODE, ///< Text node, e.g. "Some text" in <group>Some text</group> is represented by a text node.
36 COMMENT_NODE, ///< Comment node, e.g. <!-- some comment -->
37 PI_NODE ///< Processing instruction node, e.g. <?xml version="1.0" encoding="utf-8" standalone="no"?>
38 };
40 // careful; GC::Anchored should only appear once in the inheritance
41 // hierarchy; otherwise there will be leaks
43 /**
44 * @brief Interface for refcounted XML nodes
45 *
46 * This class is an abstract base type for all nodes in an XML document - this includes
47 * everything except attributes. An XML document is also a node itself. This is the main
48 * class used for interfacing with Inkscape's documents. Everything that has to be stored
49 * in the SVG has to go through this class at some point.
50 *
51 * Each node unconditionally has to belong to a document. There no "documentless" nodes,
52 * and it's not possible to move nodes between documents - they have to be duplicated.
53 * Each node can only refer to the nodes in the same document. Name of the node is immutable,
54 * it cannot be changed after its creation. Same goes for the type of the node. To simplify
55 * the use of this class, you can perform all operations on all nodes, but only some of them
56 * make any sense. For example, only element nodes can have attributes, only element and
57 * document nodes can have children, and all nodes except element and document nodes can
58 * have content. Although you can set content for element nodes, it won't make any difference
59 * in the XML output.
60 *
61 * To create new nodes, use the methods of the Inkscape::XML::Document class. You can obtain
62 * the nodes' document using the document() method. To destroy a node, just unparent it
63 * by calling sp_repr_unparent() or node->parent->removeChild() and release any references
64 * to it. The garbage collector will reclaim the memory in the next pass. There are additional
65 * convenience functions defined in @ref xml/repr.h
66 *
67 * In addition to regular DOM manipulations, you can register observer objects that will
68 * receive notifications about changes made to the node. See the NodeObserver class.
69 *
70 * @see Inkscape::XML::Document
71 * @see Inkscape::XML::NodeObserver
72 */
73 class Node : public Inkscape::GC::Anchored {
74 public:
75 Node() {}
76 virtual ~Node() {}
78 /**
79 * @name Retrieve information about the node
80 * @{
81 */
85 /**
86 * @brief Get the type of the node
87 * @return NodeType enumeration member corresponding to the type of the node.
88 */
89 virtual NodeType type() const=0;
91 /**
92 * @brief Get the name of the element node
93 *
94 * This method only makes sense for element nodes. Names are stored as
95 * GQuarks to accelerate conversions.
96 *
97 * @return Name for element nodes, NULL for others
98 */
99 virtual gchar const *name() const=0;
100 /**
101 * @brief Get the integer code corresponding to the node's name
102 * @return GQuark code corresponding to the name
103 */
104 virtual int code() const=0;
106 /**
107 * @brief Get the index of this node in parent's child order
108 *
109 * If this method is used on a node that doesn't have a parent, the method will return 0,
110 * and a warning will be printed on the console.
111 *
112 * @return The node's index, or 0 if the node does not have a parent
113 */
114 virtual unsigned position() const=0;
116 /**
117 * @brief Get the number of children of this node
118 * @return The number of children
119 */
120 virtual unsigned childCount() const=0;
122 /**
123 * @brief Get the content of a text or comment node
124 *
125 * This method makes no sense for element nodes. To retrieve the element node's name,
126 * use the name() method.
127 *
128 * @return The node's content
129 */
130 virtual gchar const *content() const=0;
132 /**
133 * @brief Get the string representation of a node's attribute
134 *
135 * If there is no attribute with the given name, the method will return NULL.
136 * All strings returned by this method are owned by the node and may not be freed.
137 * The returned pointer will become invalid when the attribute changes. If you need
138 * to store the return value, use g_strdup(). To parse the string, use methods
139 * in repr.h
140 *
141 * @param key The name of the node's attribute
142 */
143 virtual gchar const *attribute(gchar const *key) const=0;
145 /**
146 * @brief Get a list of the node's attributes
147 *
148 * The returned list is a functional programming style list rather than a standard one.
149 *
150 * @return A list of AttributeRecord structures describing the attributes
151 * @todo This method should return std::map<Glib::Quark const, gchar const *>
152 * or something similar with a custom allocator
153 */
154 virtual Inkscape::Util::List<AttributeRecord const> attributeList() const=0;
156 /**
157 * @brief Check whether this node has any attribute that matches a string
158 *
159 * This method checks whether this node has any attributes whose names
160 * have @c partial_name as their substrings. The check is done using
161 * the strstr() function of the C library. I don't know what would require that
162 * functionality, because matchAttributeName("id") matches both "identity" and "hidden".
163 *
164 * @param partial_name The string to match against all attributes
165 * @return true if there is such an attribute, false otherwise
166 */
167 virtual bool matchAttributeName(gchar const *partial_name) const=0;
169 /*@}*/
171 /**
172 * @name Modify the node
173 * @{
174 */
176 /**
177 * @brief Set the position of this node in parent's child order
178 *
179 * To move the node to the end of the parent's child order, pass a negative argument.
180 *
181 * @param pos The new position in parent's child order
182 */
183 virtual void setPosition(int pos)=0;
185 /**
186 * @brief Set the content of a text or comment node
187 *
188 * This method doesn't make sense for element nodes.
189 *
190 * @param value The node's new content
191 */
192 virtual void setContent(gchar const *value)=0;
194 /**
195 * @brief Change an attribute of this node
196 *
197 * The strings passed to this method are copied, so you can free them after use.
198 *
199 * @param key Name of the attribute to change
200 * @param value The new value of the attribute
201 * @param is_interactive Ignored
202 */
203 virtual void setAttribute(gchar const *key, gchar const *value, bool is_interactive=false)=0;
205 /**
206 * @brief Directly set the integer GQuark code for the name of the node
207 *
208 * This function is a hack to easily move elements with no namespace to the SVG namespace.
209 * Do not use this function unless you really have a good reason.
210 *
211 * @param code The integer value corresponding to the string to be set as the name of this node
212 */
213 virtual void setCodeUnsafe(int code)=0;
215 /*@}*/
218 /**
219 * @name Traverse the XML tree
220 * @{
221 */
223 //@{
224 /**
225 * @brief Get the node's associated document
226 * @return The document to which the node belongs. Never NULL.
227 */
228 virtual Document *document()=0;
229 virtual Document const *document() const=0;
230 //@}
232 //@{
233 /**
234 * @brief Get the root node of this node's document
235 *
236 * This method works on any node that is part of an XML document, and returns
237 * the root node of the document in which it resides. For detached node hierarchies
238 * (i.e. nodes that are not descendants of a document node) this method
239 * returns the highest-level element node. For detached non-element nodes this method
240 * returns NULL.
241 *
242 * @return A pointer to the root element node, or NULL if the node is detached
243 */
244 virtual Node *root()=0;
245 virtual Node const *root() const=0;
246 //@}
248 //@{
249 /**
250 * @brief Get the parent of this node
251 *
252 * This method will return NULL for detached nodes.
253 *
254 * @return Pointer to the parent, or NULL
255 */
256 virtual Node *parent()=0;
257 virtual Node const *parent() const=0;
258 //@}
260 //@{
261 /**
262 * @brief Get the next sibling of this node
263 *
264 * This method will return NULL if the node is the last sibling element of the parent.
265 * The nodes form a singly-linked list, so there is no "prev()" method. Use the provided
266 * external function for that.
267 *
268 * @return Pointer to the next sibling, or NULL
269 * @see Inkscape::XML::previous_node()
270 */
271 virtual Node *next()=0;
272 virtual Node const *next() const=0;
273 //@}
275 //@{
276 /**
277 * @brief Get the first child of this node
278 *
279 * For nodes without any children, this method returns NULL.
280 *
281 * @return Pointer to the first child, or NULL
282 */
283 virtual Node *firstChild()=0;
284 virtual Node const *firstChild() const=0;
285 //@}
287 //@{
288 /**
289 * @brief Get the last child of this node
290 *
291 * For nodes without any children, this method returns NULL.
292 *
293 * @return Pointer to the last child, or NULL
294 */
295 virtual Node *lastChild()=0;
296 virtual Node const *lastChild() const=0;
297 //@}
299 //@{
300 /**
301 * @brief Get the child of this node with a given index
302 *
303 * If there is no child with the specified index number, this method will return NULL.
304 *
305 * @param index The zero-based index of the child to retrieve
306 * @return Pointer to the appropriate child, or NULL
307 */
308 virtual Node *nthChild(unsigned index)=0;
309 virtual Node const *nthChild(unsigned index) const=0;
310 //@}
312 /*@}*/
314 /**
315 * @name Manipulate the XML tree
316 * @{
317 */
319 /**
320 * @brief Create a duplicate of this node
321 *
322 * The newly created node has no parent, and a refcount equal 1.
323 * You need to manually insert it into the document, using e.g. appendChild().
324 * Afterwards, call Inkscape::GC::release on it, so that it will be
325 * automatically collected when the parent is collected.
326 *
327 * @param doc The document in which the duplicate should be created
328 * @return A pointer to the duplicated node
329 */
330 virtual Node *duplicate(Document *doc) const=0;
332 /**
333 * @brief Insert another node as a child of this node
334 *
335 * When @c after is NULL, the inserted node will be placed as the first child
336 * of this node. @c after must be a child of this node.
337 *
338 * @param child The node to insert
339 * @param after The node after which the inserted node should be placed, or NULL
340 */
341 virtual void addChild(Node *child, Node *after)=0;
343 /**
344 * @brief Append a node as the last child of this node
345 * @param child The node to append
346 */
347 virtual void appendChild(Node *child)=0;
349 /**
350 * @brief Remove a child of this node
351 *
352 * Once the pointer to the removed node disappears from the stack, the removed node
353 * will be collected in the next GC pass, but only as long as its refcount is zero.
354 * You should keep a refcount of zero for all nodes in the document except for
355 * the document node itself, because they will be held in memory by the parent.
356 *
357 * @param child The child to remove
358 */
359 virtual void removeChild(Node *child)=0;
361 /**
362 * @brief Move a given node in this node's child order
363 *
364 * Both @c child and @c after must be children of this node for the method to work.
365 *
366 * @param child The node to move in the order
367 * @param after The sibling node after which the moved node should be placed
368 */
369 virtual void changeOrder(Node *child, Node *after)=0;
371 /**
372 * @brief Merge all children of another node with the current
373 *
374 * This method merges two node hierarchies, where @c src takes precedence.
375 * @c key is the name of the attribute that determines whether two nodes are
376 * corresponding (it must be the same for both, and all of their ancestors). If there is
377 * a corresponding node in @c src hierarchy, their attributes and content override the ones
378 * already present in this node's hierarchy. If there is no corresponding node,
379 * it is copied from @c src to this node. This method is used when merging the user's
380 * preferences file with the defaults, and has little use beyond that.
381 *
382 * @param src The node to merge into this node
383 * @param key The attribute to use as the identity attribute
384 */
385 virtual void mergeFrom(Node const *src, gchar const *key)=0;
387 /*@}*/
390 /**
391 * @name Notify observers about operations on the node
392 * @{
393 */
395 /**
396 * @brief Add an object that will be notified of the changes to this node
397 *
398 * @c observer must be an object deriving from the NodeObserver class.
399 * The virtual methods of this object will be called when a corresponding change
400 * happens to this node. You can also notify the observer of the node's current state
401 * using synthesizeEvents(NodeObserver &).
402 *
403 * @param observer The observer object
404 */
405 virtual void addObserver(NodeObserver &observer)=0;
406 /**
407 * @brief Remove an object from the list of observers
408 * @param observer The object to be removed
409 */
410 virtual void removeObserver(NodeObserver &observer)=0;
411 /**
412 * @brief Generate a sequence of events corresponding to the state of this node
413 *
414 * This function notifies the specified observer of all the events that would
415 * recreate the current state of this node; e.g. the observer is notified of
416 * all the attributes, children and content like they were just created.
417 * This function can greatly simplify observer logic.
418 *
419 * @param observer The node observer to notify of the events
420 */
421 virtual void synthesizeEvents(NodeObserver &observer)=0;
423 /**
424 * @brief Add an object that will be notified of the changes to this node and its descendants
425 *
426 * The difference between adding a regular observer and a subtree observer is that
427 * the subtree observer will also be notified if a change occurs to any of the node's
428 * descendants, while a regular observer will only be notified of changes to the node
429 * it was assigned to.
430 *
431 * @param observer The observer object
432 */
433 virtual void addSubtreeObserver(NodeObserver &observer)=0;
435 /**
436 * @brief Remove an object from the subtree observers list
437 * @param observer The object to be removed
438 */
439 virtual void removeSubtreeObserver(NodeObserver &observer)=0;
441 /**
442 * @brief Add a set node change callbacks with an associated data
443 * @deprecated Use addObserver(NodeObserver &) instead
444 */
445 virtual void addListener(NodeEventVector const *vector, void *data)=0;
446 /**
447 * @brief Remove a set of node change callbacks by their associated data
448 * @deprecated Use removeObserver(NodeObserver &) instead
449 */
450 virtual void removeListenerByData(void *data)=0;
451 /**
452 * @brief Generate a sequence of events corresponding to the state of this node
453 * @deprecated Use synthesizeEvents(NodeObserver &) instead
454 */
455 virtual void synthesizeEvents(NodeEventVector const *vector, void *data)=0;
457 /*@}*/
459 protected:
460 Node(Node const &) : Anchored() {}
461 };
463 }
464 }
466 #endif
467 /*
468 Local Variables:
469 mode:c++
470 c-file-style:"stroustrup"
471 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
472 indent-tabs-mode:nil
473 fill-column:99
474 End:
475 */
476 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :