Pyrogenesis  trunk
ParamNode.h
Go to the documentation of this file.
1 /* Copyright (C) 2022 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef INCLUDED_PARAMNODE
19 #define INCLUDED_PARAMNODE
20 
21 #include "lib/file/vfs/vfs_path.h"
22 #include "maths/Fixed.h"
23 #include "ps/Errors.h"
25 
26 #include <map>
27 #include <string>
28 
29 class XMBData;
30 class XMBElement;
31 
32 class CStrIntern;
33 class ScriptRequest;
34 
35 /**
36  * An entity initialisation parameter node.
37  * Each node has a text value, plus a number of named child nodes (in a tree structure).
38  * Child nodes are unordered, and there cannot be more than one with the same name.
39  * Nodes are immutable.
40  *
41  * Nodes can be initialised from XML files. Child elements are mapped onto child nodes.
42  * Attributes are mapped onto child nodes with names prefixed by "@"
43  * (e.g. the XML <code>&lt;a b="c">&lt;d/>&lt;/a></code> is loaded as a node with two
44  * child nodes, one called "@b" and one called "d").
45  *
46  * They can also be initialised from @em multiple XML files,
47  * which is used by ICmpTemplateManager for entity template inheritance.
48  * Loading one XML file like:
49  * @code
50  * <Entity>
51  * <Example1>
52  * <A attr="value">text</A>
53  * </Example1>
54  * <Example2>
55  * <B/>
56  * </Example2>
57  * <Example3>
58  * <C/>
59  * </Example3>
60  * <Example4 datatype="tokens">
61  * one two three
62  * </Example4>
63  * <Example5>
64  * <E/>
65  * <F>
66  * <I>test</I>
67  * </F>
68  * <H>
69  * <J>example</J>
70  * </H>
71  * </Example5>
72  * </Entity>
73  * @endcode
74  * then a second like:
75  * @code
76  * <Entity>
77  * <Example1>
78  * <A>example</A> <!-- replace the content of the old A element -->
79  * <D>new</D> <!-- add a new child to the old Example1 element -->
80  * </Example1>
81  * <Example2 disable=""/> <!-- delete the old Example2 element -->
82  * <Example3 replace=""> <!-- replace all the old children of the Example3 element -->
83  * <D>new</D>
84  * </Example3>
85  * <Example4 datatype="tokens"> <!-- treat as space-separated lists of tokens to merge -->
86  * four <!-- add a token to the parent's set -->
87  * -two <!-- remove a token from the parent's set -->
88  * </Example4>
89  * <Example5 filtered=""> <!-- drop all children of this node that are not in this file -->
90  * <F merge=""> <!-- only add this element if it is also present in the parent -->
91  * <K>example</K> <!-- if F is present merge its children normally -->
92  * </F>
93  * <G merge=""/> <!-- keep the G element of the parent if it exists -->
94  * <H>
95  * <J>text</J>
96  * </H>
97  * </Example5>
98  * </Entity>
99  * @endcode
100  * is equivalent to loading a single file like:
101  * @code
102  * <Entity>
103  * <Example1>
104  * <A attr="value">example</A>
105  * <D>new</D>
106  * </Example1>
107  * <Example3>
108  * <D>new</D>
109  * </Example3>
110  * <Example4>
111  * one three four
112  * </Example4>
113  * <Example5>
114  * <F>
115  * <I>test</I>
116  * <K>example</K>
117  * </F>
118  * <H>
119  * <J>text</J>
120  * </H>
121  * </Example5>
122  * </Entity>
123  * @endcode
124  *
125  * Parameter nodes can be translated to JavaScript objects. The previous example will become the object:
126  * @code
127  * { "Entity": {
128  * "Example1": {
129  * "A": { "@attr": "value", "_string": "example" },
130  * "D": "new"
131  * },
132  * "Example3": {
133  * "D": "new"
134  * },
135  * "Example4": { "@datatype": "tokens", "_string": "one three four" },
136  * "Example5": {
137  * "F": {
138  * "I": "test",
139  * "K": "example"
140  * },
141  * "H": {
142  * "J": "text"
143  * }
144  * }
145  * }
146  * }
147  * @endcode
148  * (Note the special @c _string for the hopefully-rare cases where a node contains both child nodes and text.)
149  */
151 {
152 public:
153  typedef std::map<std::string, CParamNode> ChildrenMap;
154 
155  /**
156  * Constructs a new, empty node.
157  */
158  CParamNode(bool isOk = true);
159 
160  /**
161  * Loads the XML data specified by @a file into the node @a ret.
162  * Any existing data in @a ret will be overwritten or else kept, so this
163  * can be called multiple times to build up a node from multiple inputs.
164  *
165  * @param sourceIdentifier Optional; string you can pass along to indicate the source of
166  * the data getting loaded. Used for output to log messages if an error occurs.
167  */
168  static void LoadXML(CParamNode& ret, const XMBData& xmb, const wchar_t* sourceIdentifier = NULL);
169 
170  /**
171  * Loads the XML data specified by @a path into the node @a ret.
172  * Any existing data in @a ret will be overwritten or else kept, so this
173  * can be called multiple times to build up a node from multiple inputs.
174  */
175  static void LoadXML(CParamNode& ret, const VfsPath& path, const std::string& validatorName);
176 
177  /**
178  * See LoadXML, but parses the XML string @a xml.
179  * @return error code if parsing failed, else @c PSRETURN_OK
180  *
181  * @param sourceIdentifier Optional; string you can pass along to indicate the source of
182  * the data getting loaded. Used for output to log messages if an error occurs.
183  */
184  static PSRETURN LoadXMLString(CParamNode& ret, const char* xml, const wchar_t* sourceIdentifier = NULL);
185 
186  /**
187  * Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none.
188  */
189  const CParamNode& GetChild(const char* name) const;
190  // (Children are returned as const in order to allow future optimisations, where we assume
191  // a node is always modified explicitly and not indirectly via its children, e.g. to cache JS::Values)
192 
193  /**
194  * Returns the only child node, or a node with IsOk() == false if there is none.
195  * This is mainly useful for the root node.
196  */
197  const CParamNode& GetOnlyChild() const;
198 
199  /**
200  * Returns true if this is a valid CParamNode, false if it represents a non-existent node
201  */
202  bool IsOk() const;
203 
204  /**
205  * Returns the content of this node as a wstring
206  */
207  const std::wstring ToWString() const;
208 
209  /**
210  * Returns the content of this node as an UTF8 string
211  */
212  const std::string& ToString() const;
213 
214  /**
215  * Returns the content of this node as an internalized 8-bit string. Should only be used for
216  * predictably small and frequently-used strings.
217  */
218  const CStrIntern ToUTF8Intern() const;
219 
220  /**
221  * Parses the content of this node as an integer
222  */
223  int ToInt() const;
224 
225  /**
226  * Parses the content of this node as a fixed-point number
227  */
228  fixed ToFixed() const;
229 
230  /**
231  * Parses the content of this node as a floating-point number
232  */
233  float ToFloat() const;
234 
235  /**
236  * Parses the content of this node as a boolean ("true" == true, anything else == false)
237  */
238  bool ToBool() const;
239 
240  /**
241  * Returns the content of this node and its children as an XML string
242  */
243  std::string ToXMLString() const;
244 
245  /**
246  * Write the content of this node and its children as an XML string, to the stream
247  */
248  void ToXMLString(std::ostream& strm) const;
249 
250  /**
251  * Returns a JS::Value representation of this node and its children.
252  * If @p cacheValue is true, then the same JS::Value will be returned each time
253  * this is called (regardless of whether you passed the same @p cx - be careful
254  * to only use the cache in one context).
255  * When caching, the lifetime of @p cx must be longer than the lifetime of this node.
256  * The cache will be reset if *this* node is modified (e.g. by LoadXML),
257  * but *not* if any child nodes are modified (so don't do that).
258  */
259  void ToJSVal(const ScriptRequest& rq, bool cacheValue, JS::MutableHandleValue ret) const;
260 
261  /**
262  * Returns the names/nodes of the children of this node, ordered by name
263  */
264  const ChildrenMap& GetChildren() const;
265 
266  /**
267  * Escapes a string so that it is well-formed XML content/attribute text.
268  * (Replaces "&" with "&amp;" etc)
269  */
270  static std::string EscapeXMLString(const std::string& str);
271 
272  std::string m_Name;
274 
275 private:
276 
277  /**
278  * Overlays the specified data onto this node. See class documentation for the concept and examples.
279  *
280  * @param xmb Representation of the XMB file containing an element with the data to apply.
281  * @param element Element inside the specified @p xmb file containing the data to apply.
282  * @param sourceIdentifier Optional; string you can pass along to indicate the source of
283  * the data getting applied. Used for output to log messages if an error occurs.
284  */
285  void ApplyLayer(const XMBData& xmb, const XMBElement& element, const wchar_t* sourceIdentifier = NULL);
286 
287  void ResetScriptVal();
288 
289  void ConstructJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret) const;
290 
291  std::string m_Value;
292  ChildrenMap m_Childs;
293  bool m_IsOk;
294 
295  /**
296  * Caches the ToJSVal script representation of this node.
297  */
298  mutable std::shared_ptr<JS::PersistentRootedValue> m_ScriptVal;
299 };
300 
301 #endif // INCLUDED_PARAMNODE
const ChildrenMap & GetChildren() const
Returns the names/nodes of the children of this node, ordered by name.
Definition: ParamNode.cpp:304
An entity initialisation parameter node.
Definition: ParamNode.h:150
CParamNode(bool isOk=true)
Constructs a new, empty node.
Definition: ParamNode.cpp:37
A simple fixed-point number class.
Definition: Fixed.h:119
const std::string & ToString() const
Returns the content of this node as an UTF8 string.
Definition: ParamNode.cpp:271
std::string m_Value
Definition: ParamNode.h:291
Definition: XMBData.h:135
int ToInt() const
Parses the content of this node as an integer.
Definition: ParamNode.cpp:281
void ToJSVal(const ScriptRequest &rq, bool cacheValue, JS::MutableHandleValue ret) const
Returns a JS::Value representation of this node and its children.
Definition: ParamNode.cpp:373
std::string ToXMLString() const
Returns the content of this node and its children as an XML string.
Definition: ParamNode.cpp:334
bool m_IsOk
Definition: ParamNode.h:293
void ResetScriptVal()
Definition: ParamNode.cpp:453
float ToFloat() const
Parses the content of this node as a floating-point number.
Definition: ParamNode.cpp:291
ChildrenMap m_Childs
Definition: ParamNode.h:292
uint32_t u32
Definition: types.h:39
Interned 8-bit strings.
Definition: CStrIntern.h:37
u32 PSRETURN
Definition: Errors.h:75
Definition: path.h:79
static PSRETURN LoadXMLString(CParamNode &ret, const char *xml, const wchar_t *sourceIdentifier=NULL)
See LoadXML, but parses the XML string xml.
Definition: ParamNode.cpp:57
Definition: XMBData.h:95
static void LoadXML(CParamNode &ret, const XMBData &xmb, const wchar_t *sourceIdentifier=NULL)
Loads the XML data specified by file into the node ret.
Definition: ParamNode.cpp:42
fixed ToFixed() const
Parses the content of this node as a fixed-point number.
Definition: ParamNode.cpp:286
const std::wstring ToWString() const
Returns the content of this node as a wstring.
Definition: ParamNode.cpp:266
const CParamNode & GetOnlyChild() const
Returns the only child node, or a node with IsOk() == false if there is none.
Definition: ParamNode.cpp:244
bool IsOk() const
Returns true if this is a valid CParamNode, false if it represents a non-existent node...
Definition: ParamNode.cpp:261
const CStrIntern ToUTF8Intern() const
Returns the content of this node as an internalized 8-bit string.
Definition: ParamNode.cpp:276
void ApplyLayer(const XMBData &xmb, const XMBElement &element, const wchar_t *sourceIdentifier=NULL)
Overlays the specified data onto this node.
Definition: ParamNode.cpp:69
u32 m_Index
Definition: ParamNode.h:273
bool ToBool() const
Parses the content of this node as a boolean ("true" == true, anything else == false) ...
Definition: ParamNode.cpp:296
std::shared_ptr< JS::PersistentRootedValue > m_ScriptVal
Caches the ToJSVal script representation of this node.
Definition: ParamNode.h:298
std::string m_Name
Definition: ParamNode.h:272
void ConstructJSVal(const ScriptRequest &rq, JS::MutableHandleValue ret) const
Definition: ParamNode.cpp:387
Spidermonkey maintains some &#39;local&#39; state via the JSContext* object.
Definition: ScriptRequest.h:59
def xml
Definition: tests.py:121
std::map< std::string, CParamNode > ChildrenMap
Definition: ParamNode.h:153
const CParamNode & GetChild(const char *name) const
Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none...
Definition: ParamNode.cpp:253
static std::string EscapeXMLString(const std::string &str)
Escapes a string so that it is well-formed XML content/attribute text.
Definition: ParamNode.cpp:309