Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ParamNode.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2010 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 #include "precompiled.h"
19 
20 #include "ParamNode.h"
21 
22 #include "lib/utf8.h"
23 #include "ps/CLogger.h"
24 #include "ps/CStr.h"
25 #include "ps/Filesystem.h"
26 #include "ps/XML/Xeromyces.h"
27 
28 #include "js/jsapi.h"
29 
30 #include <sstream>
31 
32 // Disable "'boost::algorithm::detail::is_classifiedF' : assignment operator could not be generated"
33 #if MSC_VERSION
34 #pragma warning(disable:4512)
35 #endif
36 
37 #include <boost/algorithm/string.hpp>
38 #include <boost/algorithm/string/join.hpp> // this isn't in string.hpp in old Boosts
39 
40 static CParamNode g_NullNode(false);
41 
43  m_IsOk(isOk)
44 {
45 }
46 
47 void CParamNode::LoadXML(CParamNode& ret, const XMBFile& xmb, const wchar_t* sourceIdentifier /*= NULL*/)
48 {
49  ret.ApplyLayer(xmb, xmb.GetRoot(), sourceIdentifier);
50 }
51 
52 void CParamNode::LoadXML(CParamNode& ret, const VfsPath& path)
53 {
54  CXeromyces xero;
55  PSRETURN ok = xero.Load(g_VFS, path);
56  if (ok != PSRETURN_OK)
57  return; // (Xeromyces already logged an error)
58 
59  LoadXML(ret, xero, path.string().c_str());
60 }
61 
62 PSRETURN CParamNode::LoadXMLString(CParamNode& ret, const char* xml, const wchar_t* sourceIdentifier /*=NULL*/)
63 {
64  CXeromyces xero;
65  PSRETURN ok = xero.LoadString(xml);
66  if (ok != PSRETURN_OK)
67  return ok;
68 
69  ret.ApplyLayer(xero, xero.GetRoot(), sourceIdentifier);
70 
71  return PSRETURN_OK;
72 }
73 
74 void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const wchar_t* sourceIdentifier /*= NULL*/)
75 {
77 
78  std::string name = xmb.GetElementString(element.GetNodeName()); // TODO: is GetElementString inefficient?
79  CStrW value = element.GetText().FromUTF8();
80 
81  bool hasSetValue = false;
82 
83  // Look for special attributes
84  int at_disable = xmb.GetAttributeID("disable");
85  int at_replace = xmb.GetAttributeID("replace");
86  int at_datatype = xmb.GetAttributeID("datatype");
87  bool replacing = false;
88  {
89  XERO_ITER_ATTR(element, attr)
90  {
91  if (attr.Name == at_disable)
92  {
93  m_Childs.erase(name);
94  return;
95  }
96  else if (attr.Name == at_replace)
97  {
98  m_Childs.erase(name);
99  replacing = true;
100  }
101  }
102  }
103  {
104  XERO_ITER_ATTR(element, attr)
105  {
106  if (attr.Name == at_datatype && std::wstring(attr.Value.begin(), attr.Value.end()) == L"tokens")
107  {
108  CParamNode& node = m_Childs[name];
109 
110  // Split into tokens
111  std::vector<std::wstring> oldTokens;
112  std::vector<std::wstring> newTokens;
113  if (!replacing) // ignore the old tokens if replace="" was given
114  boost::algorithm::split(oldTokens, node.m_Value, boost::algorithm::is_space());
115  boost::algorithm::split(newTokens, value, boost::algorithm::is_space());
116 
117  // Delete empty tokens
118  oldTokens.erase(std::remove_if(oldTokens.begin(), oldTokens.end(), std::mem_fun_ref(&std::wstring::empty)), oldTokens.end());
119  newTokens.erase(std::remove_if(newTokens.begin(), newTokens.end(), std::mem_fun_ref(&std::wstring::empty)), newTokens.end());
120 
121  // Merge the two lists
122  std::vector<std::wstring> tokens = oldTokens;
123  for (size_t i = 0; i < newTokens.size(); ++i)
124  {
125  if (newTokens[i][0] == L'-')
126  {
127  std::vector<std::wstring>::iterator tokenIt = std::find(tokens.begin(), tokens.end(), newTokens[i].substr(1));
128  if (tokenIt != tokens.end())
129  tokens.erase(tokenIt);
130  else
131  LOGWARNING(L"[ParamNode] Could not remove token '%ls' from node '%hs'%ls; not present in list nor inherited (possible typo?)",
132  newTokens[i].substr(1).c_str(), name.c_str(), sourceIdentifier ? (L" in '" + std::wstring(sourceIdentifier) + L"'").c_str() : L"");
133  }
134  else
135  {
136  if (std::find(oldTokens.begin(), oldTokens.end(), newTokens[i]) == oldTokens.end())
137  tokens.push_back(newTokens[i]);
138  }
139  }
140 
141  node.m_Value = boost::algorithm::join(tokens, L" ");
142  hasSetValue = true;
143  break;
144  }
145  }
146  }
147 
148  // Add this element as a child node
149  CParamNode& node = m_Childs[name];
150  if (!hasSetValue)
151  node.m_Value = value;
152 
153  // Recurse through the element's children
154  XERO_ITER_EL(element, child)
155  {
156  node.ApplyLayer(xmb, child, sourceIdentifier);
157  }
158 
159  // Add the element's attributes, prefixing names with "@"
160  XERO_ITER_ATTR(element, attr)
161  {
162  // Skip special attributes
163  if (attr.Name == at_replace) continue;
164  // Add any others
165  std::string attrName = xmb.GetAttributeString(attr.Name);
166  node.m_Childs["@" + attrName].m_Value = attr.Value.FromUTF8();
167  }
168 }
169 
170 void CParamNode::CopyFilteredChildrenOfChild(const CParamNode& src, const char* name, const std::set<std::string>& permitted)
171 {
172  ResetScriptVal();
173 
174  ChildrenMap::iterator dstChild = m_Childs.find(name);
175  ChildrenMap::const_iterator srcChild = src.m_Childs.find(name);
176  if (dstChild == m_Childs.end() || srcChild == src.m_Childs.end())
177  return; // error
178 
179  ChildrenMap::const_iterator it = srcChild->second.m_Childs.begin();
180  for (; it != srcChild->second.m_Childs.end(); ++it)
181  if (permitted.count(it->first))
182  dstChild->second.m_Childs[it->first] = it->second;
183 }
184 
185 const CParamNode& CParamNode::GetChild(const char* name) const
186 {
187  ChildrenMap::const_iterator it = m_Childs.find(name);
188  if (it == m_Childs.end())
189  return g_NullNode;
190  return it->second;
191 }
192 
193 bool CParamNode::IsOk() const
194 {
195  return m_IsOk;
196 }
197 
198 const std::wstring& CParamNode::ToString() const
199 {
200  return m_Value;
201 }
202 
203 const std::string CParamNode::ToUTF8() const
204 {
205  return utf8_from_wstring(m_Value);
206 }
207 
209 {
211 }
212 
213 int CParamNode::ToInt() const
214 {
215  int ret = 0;
216  std::wstringstream strm;
217  strm << m_Value;
218  strm >> ret;
219  return ret;
220 }
221 
223 {
224  return fixed::FromString(CStrW(m_Value));
225 }
226 
227 float CParamNode::ToFloat() const
228 {
229  float ret = 0;
230  std::wstringstream strm;
231  strm << m_Value;
232  strm >> ret;
233  return ret;
234 }
235 
236 bool CParamNode::ToBool() const
237 {
238  if (m_Value == L"true")
239  return true;
240  else
241  return false;
242 }
243 
245 {
246  return m_Childs;
247 }
248 
249 std::wstring CParamNode::EscapeXMLString(const std::wstring& str)
250 {
251  std::wstring ret;
252  ret.reserve(str.size());
253  for (size_t i = 0; i < str.size(); ++i)
254  {
255  wchar_t c = str[i];
256  switch (c)
257  {
258  case '<': ret += L"&lt;"; break;
259  case '>': ret += L"&gt;"; break;
260  case '&': ret += L"&amp;"; break;
261  case '"': ret += L"&quot;"; break;
262  case '\t': ret += L"&#9;"; break;
263  case '\n': ret += L"&#10;"; break;
264  case '\r': ret += L"&#13;"; break;
265  default:
266  if ((0x20 <= c && c <= 0xD7FF) || (0xE000 <= c && c <= 0xFFFD))
267  ret += c;
268  else
269  ret += 0xFFFD;
270  }
271  }
272  return ret;
273 }
274 
275 std::wstring CParamNode::ToXML() const
276 {
277  std::wstringstream strm;
278  ToXML(strm);
279  return strm.str();
280 }
281 
282 void CParamNode::ToXML(std::wostream& strm) const
283 {
284  strm << m_Value;
285 
286  ChildrenMap::const_iterator it = m_Childs.begin();
287  for (; it != m_Childs.end(); ++it)
288  {
289  // Skip attributes here (they were handled when the caller output the tag)
290  if (it->first.length() && it->first[0] == '@')
291  continue;
292 
293  std::wstring name (it->first.begin(), it->first.end());
294 
295  strm << L"<" << name;
296 
297  // Output the child's attributes first
298  ChildrenMap::const_iterator cit = it->second.m_Childs.begin();
299  for (; cit != it->second.m_Childs.end(); ++cit)
300  {
301  if (cit->first.length() && cit->first[0] == '@')
302  {
303  std::wstring attrname (cit->first.begin()+1, cit->first.end());
304  strm << L" " << attrname << L"=\"" << EscapeXMLString(cit->second.m_Value) << L"\"";
305  }
306  }
307 
308  strm << L">";
309 
310  it->second.ToXML(strm);
311 
312  strm << L"</" << name << ">";
313  }
314 }
315 
316 jsval CParamNode::ToJSVal(JSContext* cx, bool cacheValue) const
317 {
318  if (cacheValue && !m_ScriptVal.uninitialised())
319  return m_ScriptVal.get();
320 
321  jsval val = ConstructJSVal(cx);
322 
323  if (cacheValue)
324  m_ScriptVal = CScriptValRooted(cx, val);
325 
326  return val;
327 }
328 
329 jsval CParamNode::ConstructJSVal(JSContext* cx) const
330 {
331  if (m_Childs.empty())
332  {
333  // Empty node - map to undefined
334  if (m_Value.empty())
335  return JSVAL_VOID;
336 
337  // Just a string
338  utf16string text(m_Value.begin(), m_Value.end());
339  JSString* str = JS_InternUCStringN(cx, reinterpret_cast<const jschar*>(text.data()), text.length());
340  if (str)
341  return STRING_TO_JSVAL(str);
342  // TODO: report error
343  return JSVAL_VOID;
344  }
345 
346  // Got child nodes - convert this node into a hash-table-style object:
347 
348  JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
349  if (!obj)
350  return JSVAL_VOID; // TODO: report error
351 
352  for (std::map<std::string, CParamNode>::const_iterator it = m_Childs.begin(); it != m_Childs.end(); ++it)
353  {
354  jsval childVal = it->second.ConstructJSVal(cx);
355  if (!JS_SetProperty(cx, obj, it->first.c_str(), &childVal))
356  return JSVAL_VOID; // TODO: report error
357  }
358 
359  // If the node has a string too, add that as an extra property
360  if (!m_Value.empty())
361  {
362  utf16string text(m_Value.begin(), m_Value.end());
363  JSString* str = JS_InternUCStringN(cx, reinterpret_cast<const jschar*>(text.data()), text.length());
364  if (!str)
365  return JSVAL_VOID; // TODO: report error
366  jsval childVal = STRING_TO_JSVAL(str);
367  if (!JS_SetProperty(cx, obj, "_string", &childVal))
368  return JSVAL_VOID; // TODO: report error
369  }
370 
371  return OBJECT_TO_JSVAL(obj);
372 }
373 
375 {
377 }
An entity initialisation parameter node.
Definition: ParamNode.h:112
CParamNode(bool isOk=true)
Constructs a new, empty node.
Definition: ParamNode.cpp:42
A simple fixed-point number class.
Definition: Fixed.h:115
jsval ConstructJSVal(JSContext *cx) const
Definition: ParamNode.cpp:329
bool uninitialised() const
Returns whether the value is uninitialised.
Definition: ScriptVal.cpp:63
bool ToBool() const
Parses the content of this node as a boolean (&quot;true&quot; == true, anything else == false) ...
Definition: ParamNode.cpp:236
void ApplyLayer(const XMBFile &xmb, const XMBElement &element, const wchar_t *sourceIdentifier=NULL)
Overlays the specified data onto this node.
Definition: ParamNode.cpp:74
static std::wstring EscapeXMLString(const std::wstring &str)
Escapes a string so that it is well-formed XML content/attribute text.
Definition: ParamNode.cpp:249
const std::wstring & ToString() const
Returns the content of this node as a string.
Definition: ParamNode.cpp:198
PSRETURN Load(const PIVFS &vfs, const VfsPath &filename)
Load from an XML file (with invisible XMB caching).
Definition: Xeromyces.cpp:65
#define XERO_ITER_ATTR(parent_element, attribute)
Definition: Xeromyces.h:99
const std::string ToUTF8() const
Returns the content of this node as an 8-bit string.
Definition: ParamNode.cpp:203
const PSRETURN PSRETURN_OK
Definition: Errors.h:103
bool IsOk() const
Returns true if this is a valid CParamNode, false if it represents a non-existent node...
Definition: ParamNode.cpp:193
std::string utf8_from_wstring(const std::wstring &src, Status *err)
opposite of wstring_from_utf8
Definition: utf8.cpp:208
#define XERO_ITER_EL(parent_element, child_element)
Definition: Xeromyces.h:91
int ToInt() const
Parses the content of this node as an integer.
Definition: ParamNode.cpp:213
tuple xml
Definition: tests.py:119
const ChildrenMap & GetChildren() const
Returns the names/nodes of the children of this node, ordered by name.
Definition: ParamNode.cpp:244
bool m_IsOk
Definition: ParamNode.h:254
static CParamNode g_NullNode(false)
#define LOGWARNING
Definition: CLogger.h:34
void ResetScriptVal()
Definition: ParamNode.cpp:374
fixed ToFixed() const
Parses the content of this node as a fixed-point number.
Definition: ParamNode.cpp:222
static void LoadXML(CParamNode &ret, const XMBFile &file, const wchar_t *sourceIdentifier=NULL)
Loads the XML data specified by file into the node ret.
Definition: ParamNode.cpp:47
ChildrenMap m_Childs
Definition: ParamNode.h:253
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:185
CStr8 GetText() const
Definition: XeroXMB.cpp:196
Interned 8-bit strings.
Definition: CStrIntern.h:37
u32 PSRETURN
Definition: Errors.h:75
float ToFloat() const
Parses the content of this node as a floating-point number.
Definition: ParamNode.cpp:227
Definition: path.h:75
static PSRETURN LoadXMLString(CParamNode &ret, const char *xml, const wchar_t *sourceIdentifier=NULL)
See LoadXML, but parses the XML string xml.
Definition: ParamNode.cpp:62
const String & string() const
Definition: path.h:123
std::wstring m_Value
Definition: ParamNode.h:252
PSRETURN LoadString(const char *xml)
Load from an in-memory XML string (with no caching).
Definition: Xeromyces.cpp:167
static CFixed FromString(const CStr8 &s)
Definition: Fixed.cpp:27
std::string GetElementString(const int ID) const
Definition: XeroXMB.cpp:148
std::basic_string< utf16_t, utf16_traits > utf16string
Definition: utf16string.h:109
XMBElement GetRoot() const
Definition: XeroXMB.cpp:84
jsval get() const
Returns the current value (or JSVAL_VOID if uninitialised).
Definition: ScriptVal.cpp:45
void CopyFilteredChildrenOfChild(const CParamNode &src, const char *name, const std::set< std::string > &permitted)
Finds the childs named name from src and from this, and copies the source child&#39;s children which are ...
Definition: ParamNode.cpp:170
int GetAttributeID(const char *Name) const
Definition: XeroXMB.cpp:124
CScriptValRooted m_ScriptVal
Caches the ToJSVal script representation of this node.
Definition: ParamNode.h:259
std::string GetAttributeString(const int ID) const
Definition: XeroXMB.cpp:156
const CStrIntern ToUTF8Intern() const
Returns the content of this node as an internalized 8-bit string.
Definition: ParamNode.cpp:208
std::wstring ToXML() const
Returns the content of this node and its children as an XML string.
Definition: ParamNode.cpp:275
PIVFS g_VFS
Definition: Filesystem.cpp:30
std::map< std::string, CParamNode > ChildrenMap
Definition: ParamNode.h:115
jsval ToJSVal(JSContext *cx, bool cacheValue) const
Returns a jsval representation of this node and its children.
Definition: ParamNode.cpp:316
int GetNodeName() const
Definition: XeroXMB.cpp:166