1 // XMLNode_as.h:  ActionScript 3 "XMLNode" class, for Gnash.
2 //
3 //   Copyright (C) 2009, 2010, 2011. 2012 Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 //
19 
20 #ifndef GNASH_ASOBJ3_XMLNODE_H
21 #define GNASH_ASOBJ3_XMLNODE_H
22 
23 #include <list>
24 #include <string>
25 #include <cassert>
26 
27 #include "Relay.h"
28 
29 namespace gnash {
30     class as_object;
31     class Global_as;
32     struct ObjectURI;
33 }
34 
35 namespace gnash {
36 
37 
38 /// A node in an XML tree.
39 //
40 /// This class has various complications to reduce memory usage when parsing
41 /// very large XML documents.
42 //
43 /// 1. It is a Relay class that can be attached to an as_object.
44 /// 2. It does not have to have an associated object. This is only created
45 ///    once the XMLNode is accessed in ActionScript.
46 /// 3. The top node of an XML tree is always accessible in ActionScript, either
47 ///    as an XML_as or a user-created XMLNode_as.
48 /// 4. XMLNodes consequently mark their children as reachable, but not their
49 ///    parent.
50 /// 5. When an XMLNode is destroyed, any children without an associated object
51 ///    are also deleted. Children with an associated object will be destroyed
52 ///    when the GC destroys the object.
53 class XMLNode_as : public Relay
54 {
55 public:
56 
57     enum NodeType {
58         Element = 1,
59         Attribute = 2,
60         Text = 3,
61         Cdata = 4,
62         EntityRef = 5,
63         Entity = 6,
64         ProcInstr = 7,
65         Comment = 8,
66         Document = 9,
67         DocType = 10,
68         DocFragment = 11,
69         Notation = 12
70     };
71 
72     XMLNode_as(Global_as& gl);
73 
74     virtual ~XMLNode_as();
75 
length()76     size_t length() const { return _children.size(); }
77 
nodeName()78     const std::string& nodeName() const { return _name; }
79 
nodeValue()80     const std::string& nodeValue() const { return _value; }
81 
82     /// Get the type of an XML Node.
nodeType()83     NodeType nodeType() const { return _type; }
84 
85     /// Set the type of an XML Node.
nodeTypeSet(NodeType type)86     void nodeTypeSet(NodeType type) {
87 	    _type = type;
88     }
89 
90     /// Set name of this node
nodeNameSet(const std::string & name)91     void nodeNameSet(const std::string& name) { _name = name; }
92 
93     bool extractPrefix(std::string& prefix) const;
94 
95     /// Set value of this node
nodeValueSet(const std::string & value)96     void nodeValueSet(const std::string& value) { _value = value; }
97 
98     /// Performs a recursive search of node attributes to find a match
99     void getNamespaceForPrefix(const std::string& prefix, std::string& ns)
100         const;
101 
102     /// Performs a recursive search of node attributes to find a match
103     //
104     /// @return false if no match found.
105     bool getPrefixForNamespace(const std::string& ns, std::string& prefix)
106         const;
107 
setNamespaceURI(const std::string & value)108     void setNamespaceURI(const std::string& value) {
109         _namespaceURI = value;
110     }
111 
getNamespaceURI()112     const std::string& getNamespaceURI() const {
113         return _namespaceURI;
114     }
115 
116     /// Returns true if 'this' descends from the specified node.
117     bool descendsFrom(XMLNode_as* node) const;
118 
119     ///  Returns true if the specified node has child nodes; otherwise,
120     ///  returns false.
121     bool hasChildNodes() const;
122 
123     XMLNode_as* firstChild() const;
124     XMLNode_as* lastChild() const;
125 
126     // Use a list for quick erasing
127     typedef std::list<XMLNode_as*> Children;
128 
129     as_object* childNodes();
130 
131     XMLNode_as* previousSibling() const;
132     XMLNode_as* nextSibling() const;
133 
134     /// Copy a node
135     //
136     /// Method; constructs and returns a new XML node of the same type,
137     /// name, value, and attributes as the specified XML object. If deep
138     /// is set to true, all child nodes are recursively cloned, resulting
139     /// in an exact copy of the original object's document tree.
140     XMLNode_as* cloneNode(bool deep) const;
141 
142     /// Append a child node to this XML object
143     //
144     /// The child node's parent is set to this object, the node is added to
145     /// this object's children.
146     //
147     /// The childNodes array will be updated if it exists.
148     //
149     /// @param node     The node to add as a child
150 	void appendChild(XMLNode_as* node);
151 
152     /// Remove a child node from this XML object
153     //
154     /// The child node's parent is set to 0, the node is removed from
155     /// this object's children.
156     //
157     /// The childNodes array will be updated if it exists.
158     //
159     /// @param node     The node to remove.
160     void removeChild(XMLNode_as* node);
161 
162     /// Get the parent XMLNode_as of this node. Can be 0.
getParent()163     XMLNode_as* getParent() const {
164         return _parent;
165     }
166 
167     /// Insert a node before a node
168     //
169     /// Method; inserts a new child node into the XML object's child
170     /// list, before the beforeNode node. If the beforeNode parameter is
171     /// undefined or null, the node is added using the appendChild()
172     /// method. If beforeNode is not a child of my_xml, the insertion
173     /// fails.
174     ///
175     /// @param newnode
176     ///     The node to insert, moving from its current tree
177     ///
178     /// @param pos
179     ///     The node before which to insert the new one.
180     ///     Must be a child of this XMLNode or the operation will fail.
181     ///
182     void insertBefore(XMLNode_as* newnode, XMLNode_as* pos);
183 
184     /// Convert the XMLNode to a string
185     //
186     /// @param o        The ostream to write the string to.
187     /// @param encode   Whether to URL encode the node values. This
188     ///                 is false by default, as it is only necessary
189     ///                 for XML.sendAndLoad.
190     virtual void toString(std::ostream& str, bool encode = false) const;
191 
192     /// Return the attributes object associated with this node.
getAttributes()193     as_object* getAttributes() const { return _attributes; }
194 
195     /// Set a named attribute to a value.
196     //
197     /// @param name     The name of the attribute to set. If already present,
198     ///                 the value is changed. If not present, the attribute is
199     ///                 added.
200     /// @param value    The value to set the named attribute to.
201     void setAttribute(const std::string& name, const std::string& value);
202 
203     /// Associate an as_object with this XMLNode_as.
204     //
205     /// An XMLNode_as with an associated object is regarded as being owned
206     /// by that object, so make sure it is! Using as_object::setRelay will
207     /// achieve that.
setObject(as_object * o)208     void setObject(as_object* o) {
209         assert(!_object);
210         assert(o);
211         _object = o;
212     }
213 
214     /// Return the object associated with this XMLNode_as.
215     //
216     /// The object will be created if it does not already exist.
217     as_object* object();
218 
219 protected:
220 
221     /// Mark reachable elements
222     //
223     /// These are: children, attributes object, associated as_object.
224 	virtual void setReachable();
225 
226     Global_as& _global;
227 
228     /// Clear all children, making sure unreferenced children are deleted.
229     //
230     /// AS-referenced child nodes will no longer be marked as reachable, so
231     /// the GC will remove them on the next run.
232     void clearChildren();
233 
234 private:
235 
236     /// Set the parent XMLNode_as of this node.
237     //
238     /// @param node     The new parent of this node. May be 0.
setParent(XMLNode_as * node)239     void setParent(XMLNode_as* node) { _parent = node; }
240 
241     /// Reset the array of childNodes to match the actual children.
242     //
243     /// Only called when the XML structure changes, and only once the
244     /// childNodes array has been created. Before this point it is not
245     /// referenceable, so we don't need to do anything.
246     void updateChildNodes();
247 
248     /// A non-trivial copy-constructor for cloning nodes.
249     XMLNode_as(const XMLNode_as &node, bool deep);
250 
251     Children _children;
252 
253     as_object* _object;
254 
255     XMLNode_as* _parent;
256 
257     as_object* _attributes;
258 
259     as_object* _childNodes;
260 
261     std::string _name;
262 
263     std::string _value;
264 
265     NodeType _type;
266 
267     std::string _namespaceURI;
268 
269     static void stringify(const XMLNode_as& xml, std::ostream& xmlout,
270             bool encode);
271 };
272 
273 // Initialize the global XMLNode class
274 void xmlnode_class_init(as_object& where, const ObjectURI& uri);
275 
276 /// Register ASnative methods
277 void registerXMLNodeNative(as_object& where);
278 
279 } // gnash namespace
280 
281 // GNASH_ASOBJ3_XMLNODE_H
282 #endif
283 
284 // local Variables:
285 // mode: C++
286 // indent-tabs-mode: t
287 // End:
288 
289 
290