Line data Source code
1 : /* Copyright (C) 2023 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"
24 : #include "scriptinterface/ScriptTypes.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><a b="c"><d/></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 : */
150 1341 : class CParamNode
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 : * Returns the name of this node.
268 : */
269 : const std::string& GetName() const;
270 :
271 : /**
272 : * Escapes a string so that it is well-formed XML content/attribute text.
273 : * (Replaces "&" with "&" etc)
274 : */
275 : static std::string EscapeXMLString(const std::string& str);
276 :
277 : std::string m_Name;
278 : u32 m_Index;
279 :
280 : private:
281 :
282 : /**
283 : * Overlays the specified data onto this node. See class documentation for the concept and examples.
284 : *
285 : * @param xmb Representation of the XMB file containing an element with the data to apply.
286 : * @param element Element inside the specified @p xmb file containing the data to apply.
287 : * @param sourceIdentifier Optional; string you can pass along to indicate the source of
288 : * the data getting applied. Used for output to log messages if an error occurs.
289 : */
290 : void ApplyLayer(const XMBData& xmb, const XMBElement& element, const wchar_t* sourceIdentifier = NULL);
291 :
292 : void ResetScriptVal();
293 :
294 : void ConstructJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret) const;
295 :
296 : std::string m_Value;
297 : ChildrenMap m_Childs;
298 : bool m_IsOk;
299 :
300 : /**
301 : * Caches the ToJSVal script representation of this node.
302 : */
303 : mutable std::shared_ptr<JS::PersistentRootedValue> m_ScriptVal;
304 : };
305 :
306 : #endif // INCLUDED_PARAMNODE
|