/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_PARAMNODE #define INCLUDED_PARAMNODE #include "lib/file/vfs/vfs_path.h" #include "maths/Fixed.h" #include "ps/Errors.h" #include "scriptinterface/ScriptTypes.h" #include #include class XMBData; class XMBElement; class CStrIntern; class ScriptRequest; /** * An entity initialisation parameter node. * Each node has a text value, plus a number of named child nodes (in a tree structure). * Child nodes are unordered, and there cannot be more than one with the same name. * Nodes are immutable. * * Nodes can be initialised from XML files. Child elements are mapped onto child nodes. * Attributes are mapped onto child nodes with names prefixed by "@" * (e.g. the XML <a b="c"><d/></a> is loaded as a node with two * child nodes, one called "@b" and one called "d"). * * They can also be initialised from @em multiple XML files, * which is used by ICmpTemplateManager for entity template inheritance. * Loading one XML file like: * @code * * * text * * * * * * * * * one two three * * * * * test * * * example * * * * @endcode * then a second like: * @code * * * example * new * * * * new * * * four * -two * * * * example * * * * text * * * * @endcode * is equivalent to loading a single file like: * @code * * * example * new * * * new * * * one three four * * * * test * example * * * text * * * * @endcode * * Parameter nodes can be translated to JavaScript objects. The previous example will become the object: * @code * { "Entity": { * "Example1": { * "A": { "@attr": "value", "_string": "example" }, * "D": "new" * }, * "Example3": { * "D": "new" * }, * "Example4": { "@datatype": "tokens", "_string": "one three four" }, * "Example5": { * "F": { * "I": "test", * "K": "example" * }, * "H": { * "J": "text" * } * } * } * } * @endcode * (Note the special @c _string for the hopefully-rare cases where a node contains both child nodes and text.) */ class CParamNode { public: typedef std::map ChildrenMap; /** * Constructs a new, empty node. */ CParamNode(bool isOk = true); /** * Loads the XML data specified by @a file into the node @a ret. * Any existing data in @a ret will be overwritten or else kept, so this * can be called multiple times to build up a node from multiple inputs. * * @param sourceIdentifier Optional; string you can pass along to indicate the source of * the data getting loaded. Used for output to log messages if an error occurs. */ static void LoadXML(CParamNode& ret, const XMBData& xmb, const wchar_t* sourceIdentifier = NULL); /** * Loads the XML data specified by @a path into the node @a ret. * Any existing data in @a ret will be overwritten or else kept, so this * can be called multiple times to build up a node from multiple inputs. */ static void LoadXML(CParamNode& ret, const VfsPath& path, const std::string& validatorName); /** * See LoadXML, but parses the XML string @a xml. * @return error code if parsing failed, else @c PSRETURN_OK * * @param sourceIdentifier Optional; string you can pass along to indicate the source of * the data getting loaded. Used for output to log messages if an error occurs. */ static PSRETURN LoadXMLString(CParamNode& ret, const char* xml, const wchar_t* sourceIdentifier = NULL); /** * Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none. */ const CParamNode& GetChild(const char* name) const; // (Children are returned as const in order to allow future optimisations, where we assume // a node is always modified explicitly and not indirectly via its children, e.g. to cache JS::Values) /** * Returns the only child node, or a node with IsOk() == false if there is none. * This is mainly useful for the root node. */ const CParamNode& GetOnlyChild() const; /** * Returns true if this is a valid CParamNode, false if it represents a non-existent node */ bool IsOk() const; /** * Returns the content of this node as a wstring */ const std::wstring ToWString() const; /** * Returns the content of this node as an UTF8 string */ const std::string& ToString() const; /** * Returns the content of this node as an internalized 8-bit string. Should only be used for * predictably small and frequently-used strings. */ const CStrIntern ToUTF8Intern() const; /** * Parses the content of this node as an integer */ int ToInt() const; /** * Parses the content of this node as a fixed-point number */ fixed ToFixed() const; /** * Parses the content of this node as a floating-point number */ float ToFloat() const; /** * Parses the content of this node as a boolean ("true" == true, anything else == false) */ bool ToBool() const; /** * Returns the content of this node and its children as an XML string */ std::string ToXMLString() const; /** * Write the content of this node and its children as an XML string, to the stream */ void ToXMLString(std::ostream& strm) const; /** * Returns a JS::Value representation of this node and its children. * If @p cacheValue is true, then the same JS::Value will be returned each time * this is called (regardless of whether you passed the same @p cx - be careful * to only use the cache in one context). * When caching, the lifetime of @p cx must be longer than the lifetime of this node. * The cache will be reset if *this* node is modified (e.g. by LoadXML), * but *not* if any child nodes are modified (so don't do that). */ void ToJSVal(const ScriptRequest& rq, bool cacheValue, JS::MutableHandleValue ret) const; /** * Returns the names/nodes of the children of this node, ordered by name */ const ChildrenMap& GetChildren() const; /** * Escapes a string so that it is well-formed XML content/attribute text. * (Replaces "&" with "&" etc) */ static std::string EscapeXMLString(const std::string& str); std::string m_Name; u32 m_Index; private: /** * Overlays the specified data onto this node. See class documentation for the concept and examples. * * @param xmb Representation of the XMB file containing an element with the data to apply. * @param element Element inside the specified @p xmb file containing the data to apply. * @param sourceIdentifier Optional; string you can pass along to indicate the source of * the data getting applied. Used for output to log messages if an error occurs. */ void ApplyLayer(const XMBData& xmb, const XMBElement& element, const wchar_t* sourceIdentifier = NULL); void ResetScriptVal(); void ConstructJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret) const; std::string m_Value; ChildrenMap m_Childs; bool m_IsOk; /** * Caches the ToJSVal script representation of this node. */ mutable std::shared_ptr m_ScriptVal; }; #endif // INCLUDED_PARAMNODE