Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-14 08:38:41

0001 /*
0002 Open Asset Import Library (assimp)
0003 ----------------------------------------------------------------------
0004 
0005 Copyright (c) 2006-2025, assimp team
0006 
0007 All rights reserved.
0008 
0009 Redistribution and use of this software in source and binary forms,
0010 with or without modification, are permitted provided that the
0011 following conditions are met:
0012 
0013 * Redistributions of source code must retain the above
0014 copyright notice, this list of conditions and the
0015 following disclaimer.
0016 
0017 * Redistributions in binary form must reproduce the above
0018 copyright notice, this list of conditions and the
0019 following disclaimer in the documentation and/or other
0020 materials provided with the distribution.
0021 
0022 * Neither the name of the assimp team, nor the names of its
0023 contributors may be used to endorse or promote products
0024 derived from this software without specific prior
0025 written permission of the assimp team.
0026 
0027 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0028 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0029 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0030 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0031 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0032 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0033 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0034 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0035 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0036 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0037 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0038 
0039 ----------------------------------------------------------------------
0040 */
0041 
0042 #ifndef INCLUDED_AI_IRRXML_WRAPPER
0043 #define INCLUDED_AI_IRRXML_WRAPPER
0044 
0045 #include <assimp/ai_assert.h>
0046 #include <assimp/StringUtils.h>
0047 #include <assimp/DefaultLogger.hpp>
0048 
0049 #include "BaseImporter.h"
0050 #include "IOStream.hpp"
0051 
0052 #include <pugixml.hpp>
0053 #include <istream>
0054 #include <utility>
0055 #include <vector>
0056 
0057 namespace Assimp {
0058 
0059 /// @brief  Will find a node by its name.
0060 struct find_node_by_name_predicate {
0061     /// @brief The default constructor.
0062     find_node_by_name_predicate() = default;
0063 
0064 
0065     std::string mName; ///< The name to find.
0066     find_node_by_name_predicate(const std::string &name) :
0067             mName(name) {
0068         // empty
0069     }
0070 
0071     bool operator()(pugi::xml_node node) const {
0072         return node.name() == mName;
0073     }
0074 };
0075 
0076 /// @brief  Will convert an attribute to its int value.
0077 /// @tparam[in] TNodeType  The node type.
0078 template <class TNodeType>
0079 struct NodeConverter {
0080 public:
0081     static int to_int(TNodeType &node, const char *attribName) {
0082         ai_assert(nullptr != attribName);
0083         return node.attribute(attribName).to_int();
0084     }
0085 };
0086 
0087 using XmlNode = pugi::xml_node;
0088 using XmlAttribute = pugi::xml_attribute;
0089 
0090 /// @brief The Xml-Parser class.
0091 ///
0092 /// Use this parser if you have to import any kind of xml-format.
0093 ///
0094 /// An example:
0095 /// @code
0096 /// TXmlParser<XmlNode> theParser;
0097 /// if (theParser.parse(fileStream)) {
0098 ///     auto node = theParser.getRootNode();
0099 ///     for ( auto currentNode : node.children()) {
0100 ///         // Will loop over all children
0101 ///     }
0102 /// }
0103 /// @endcode
0104 /// @tparam TNodeType
0105 template <class TNodeType>
0106 class TXmlParser {
0107 public:
0108     /// @brief The default class constructor.
0109     TXmlParser();
0110 
0111     /// @brief  The class destructor.
0112     ~TXmlParser();
0113 
0114     /// @brief  Will clear the parsed xml-file.
0115     void clear();
0116 
0117     /// @brief  Will search for a child-node by its name
0118     /// @param[in] name     The name of the child-node.
0119     /// @return The node instance or nullptr, if nothing was found.
0120     TNodeType *findNode(const std::string &name);
0121 
0122     /// @brief  Will return true, if the node is a child-node.
0123     /// @param[in]  name    The name of the child node to look for.
0124     /// @return true, if the node is a child-node or false if not.
0125     bool hasNode(const std::string &name);
0126 
0127     /// @brief  Will parse an xml-file from a given stream.
0128     /// @param[in] stream      The input stream.
0129     /// @return true, if the parsing was successful, false if not.
0130     bool parse(IOStream *stream);
0131 
0132     /// @brief  Will parse an xml-file from a stringstream.
0133     /// @param[in] str      The input istream (note: not "const" to match pugixml param)
0134     /// @return true, if the parsing was successful, false if not.
0135     bool parse(std::istream &inStream);
0136 
0137     /// @brief  Will return true if a root node is there.
0138     /// @return true in case of an existing root.
0139     bool hasRoot() const;
0140 
0141     /// @brief  Will return the document pointer, is nullptr if no xml-file was parsed.
0142     /// @return The pointer showing to the document.
0143     pugi::xml_document *getDocument() const;
0144 
0145     /// @brief  Will return the root node, const version.
0146     /// @return The root node.
0147     const TNodeType getRootNode() const;
0148 
0149     /// @brief  Will return the root node, non-const version.
0150     /// @return The root node.
0151     TNodeType getRootNode();
0152 
0153     /// @brief Will check if a node with the given name is in.
0154     /// @param[in] node     The node to look in.
0155     /// @param[in] name     The name of the child-node.
0156     /// @return true, if node was found, false if not.
0157     static inline bool hasNode(XmlNode &node, const char *name);
0158 
0159     /// @brief Will check if an attribute is part of the XmlNode.
0160     /// @param[in] xmlNode  The node to search in.
0161     /// @param[in] name     The attribute name to look for.
0162     /// @return true, if the was found, false if not.
0163     static inline bool hasAttribute(XmlNode &xmlNode, const char *name);
0164 
0165     /// @brief Will try to get an unsigned int attribute value.
0166     /// @param[in] xmlNode  The node to search in.
0167     /// @param[in] name     The attribute name to look for.
0168     /// @param[out] val     The unsigned int value from the attribute.
0169     /// @return true, if the node contains an attribute with the given name and if the value is an unsigned int.
0170     static inline bool getUIntAttribute(XmlNode &xmlNode, const char *name, unsigned int &val);
0171 
0172     /// @brief Will try to get an int attribute value.
0173     /// @param[in] xmlNode  The node to search in.
0174     /// @param[in] name     The attribute name to look for.
0175     /// @param[out] val     The int value from the attribute.
0176     /// @return true, if the node contains an attribute with the given name and if the value is an int.
0177     static inline bool getIntAttribute(XmlNode &xmlNode, const char *name, int &val);
0178 
0179     /// @brief Will try to get a real attribute value.
0180     /// @param[in] xmlNode  The node to search in.
0181     /// @param[in] name     The attribute name to look for.
0182     /// @param[out] val     The real value from the attribute.
0183     /// @return true, if the node contains an attribute with the given name and if the value is a real.
0184     static inline bool getRealAttribute(XmlNode &xmlNode, const char *name, ai_real &val);
0185 
0186     /// @brief Will try to get a float attribute value.
0187     /// @param[in] xmlNode  The node to search in.
0188     /// @param[in] name     The attribute name to look for.
0189     /// @param[out] val     The float value from the attribute.
0190     /// @return true, if the node contains an attribute with the given name and if the value is a float.
0191     static inline bool getFloatAttribute(XmlNode &xmlNode, const char *name, float &val);
0192 
0193     /// @brief Will try to get a double attribute value.
0194     /// @param[in] xmlNode  The node to search in.
0195     /// @param[in] name     The attribute name to look for.
0196     /// @param[out] val     The double value from the attribute.
0197     /// @return true, if the node contains an attribute with the given name and if the value is a double.
0198     static inline bool getDoubleAttribute(XmlNode &xmlNode, const char *name, double &val);
0199 
0200     /// @brief Will try to get a std::string attribute value.
0201     /// @param[in] xmlNode  The node to search in.
0202     /// @param[in] name     The attribute name to look for.
0203     /// @param[out] val     The std::string value from the attribute.
0204     /// @return true, if the node contains an attribute with the given name and if the value is a std::string.
0205     static inline bool getStdStrAttribute(XmlNode &xmlNode, const char *name, std::string &val);
0206 
0207     /// @brief Will try to get a bool attribute value.
0208     /// @param[in] xmlNode  The node to search in.
0209     /// @param[in] name     The attribute name to look for.
0210     /// @param[out] val     The bool value from the attribute.
0211     /// @return true, if the node contains an attribute with the given name and if the value is a bool.
0212     static inline bool getBoolAttribute(XmlNode &xmlNode, const char *name, bool &val);
0213 
0214     /// @brief Will try to get the value of the node as a string.
0215     /// @param[in] node     The node to search in.
0216     /// @param[out] text    The value as a text.
0217     /// @return true, if the value can be read out.
0218     static inline bool getValueAsString(XmlNode &node, std::string &text);
0219 
0220     /// @brief Will try to get the value of the node as a real.
0221     /// @param[in]  node   The node to search in.
0222     /// @param[out] v      The value as a ai_real.
0223     /// @return true, if the value can be read out.
0224     static inline bool getValueAsReal(XmlNode &node, ai_real &v);
0225 
0226     /// @brief Will try to get the value of the node as a float.
0227     /// @param[in] node     The node to search in.
0228     /// @param[out]v        The value as a float.
0229     /// @return true, if the value can be read out.
0230     static inline bool getValueAsFloat(XmlNode &node, float &v);
0231 
0232     /// @brief Will try to get the value of the node as an integer.
0233     /// @param[in]  node    The node to search in.
0234     /// @param[out] i       The value as a int.
0235     /// @return true, if the value can be read out.
0236     static inline bool getValueAsInt(XmlNode &node, int &v);
0237 
0238     /// @brief Will try to get the value of the node as an bool.
0239     /// @param[in]  node    The node to search in.
0240     /// @param[out] v       The value as a bool.
0241     /// @return true, if the value can be read out.
0242     static inline bool getValueAsBool(XmlNode &node, bool &v);
0243 
0244 private:
0245     pugi::xml_document *mDoc;
0246     TNodeType mCurrent;
0247     std::vector<char> mData;
0248 };
0249 
0250 template <class TNodeType>
0251 inline TXmlParser<TNodeType>::TXmlParser() :
0252         mDoc(nullptr),
0253         mData() {
0254     // empty
0255 }
0256 
0257 template <class TNodeType>
0258 inline TXmlParser<TNodeType>::~TXmlParser() {
0259     clear();
0260 }
0261 
0262 template <class TNodeType>
0263 inline void TXmlParser<TNodeType>::clear() {
0264     if (mData.empty()) {
0265         if (mDoc) {
0266             delete mDoc;
0267         }
0268         mDoc = nullptr;
0269         return;
0270     }
0271 
0272     mData.clear();
0273     delete mDoc;
0274     mDoc = nullptr;
0275 }
0276 
0277 template <class TNodeType>
0278 inline TNodeType *TXmlParser<TNodeType>::findNode(const std::string &name) {
0279     if (name.empty()) {
0280         return nullptr;
0281     }
0282 
0283     if (nullptr == mDoc) {
0284         return nullptr;
0285     }
0286 
0287     find_node_by_name_predicate predicate(name);
0288     mCurrent = mDoc->find_node(std::move(predicate));
0289     if (mCurrent.empty()) {
0290         return nullptr;
0291     }
0292 
0293     return &mCurrent;
0294 }
0295 
0296 template <class TNodeType>
0297 bool TXmlParser<TNodeType>::hasNode(const std::string &name) {
0298     return nullptr != findNode(name);
0299 }
0300 
0301 template <class TNodeType>
0302 bool TXmlParser<TNodeType>::parse(IOStream *stream) {
0303     if (hasRoot()) {
0304         clear();
0305     }
0306 
0307     if (nullptr == stream) {
0308         ASSIMP_LOG_DEBUG("Stream is nullptr.");
0309         return false;
0310     }
0311 
0312     const size_t len = stream->FileSize();
0313     mData.resize(len + 1);
0314     memset(&mData[0], '\0', len + 1);
0315     stream->Read(&mData[0], 1, len);
0316 
0317     mDoc = new pugi::xml_document();
0318     // load_string assumes native encoding (aka always utf-8 per build options)
0319     //pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full);
0320     pugi::xml_parse_result parse_result = mDoc->load_buffer(&mData[0], mData.size(), pugi::parse_full);
0321     if (parse_result.status == pugi::status_ok) {
0322         return true;
0323     }
0324 
0325     ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset);
0326 
0327     return false;
0328 }
0329 
0330 template <class TNodeType>
0331 bool TXmlParser<TNodeType>::parse(std::istream &inStream) {
0332     if (hasRoot()) {
0333         clear();
0334     }
0335     mDoc = new pugi::xml_document();
0336     pugi::xml_parse_result parse_result = mDoc->load(inStream);
0337     if (parse_result.status == pugi::status_ok) {
0338         return true;
0339     }
0340 
0341     ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset);
0342 
0343     return false;
0344 }
0345 
0346 template <class TNodeType>
0347 bool TXmlParser<TNodeType>::hasRoot() const {
0348     return nullptr != mDoc;
0349 }
0350 
0351 template <class TNodeType>
0352 pugi::xml_document *TXmlParser<TNodeType>::getDocument() const {
0353     return mDoc;
0354 }
0355 
0356 template <class TNodeType>
0357 const TNodeType TXmlParser<TNodeType>::getRootNode() const {
0358     static pugi::xml_node none;
0359     if (nullptr == mDoc) {
0360         return none;
0361     }
0362     return mDoc->root();
0363 }
0364 
0365 template <class TNodeType>
0366 TNodeType TXmlParser<TNodeType>::getRootNode() {
0367     static pugi::xml_node none;
0368     if (nullptr == mDoc) {
0369         return none;
0370     }
0371 
0372     return mDoc->root();
0373 }
0374 
0375 template <class TNodeType>
0376 inline bool TXmlParser<TNodeType>::hasNode(XmlNode &node, const char *name) {
0377     pugi::xml_node child = node.find_child(find_node_by_name_predicate(name));
0378     return !child.empty();
0379 }
0380 
0381 template <class TNodeType>
0382 inline bool TXmlParser<TNodeType>::hasAttribute(XmlNode &xmlNode, const char *name) {
0383     pugi::xml_attribute attr = xmlNode.attribute(name);
0384     return !attr.empty();
0385 }
0386 
0387 template <class TNodeType>
0388 inline bool TXmlParser<TNodeType>::getUIntAttribute(XmlNode &xmlNode, const char *name, unsigned int &val) {
0389     pugi::xml_attribute attr = xmlNode.attribute(name);
0390     if (attr.empty()) {
0391         return false;
0392     }
0393 
0394     val = attr.as_uint();
0395     return true;
0396 }
0397 
0398 template <class TNodeType>
0399 inline bool TXmlParser<TNodeType>::getIntAttribute(XmlNode &xmlNode, const char *name, int &val) {
0400     pugi::xml_attribute attr = xmlNode.attribute(name);
0401     if (attr.empty()) {
0402         return false;
0403     }
0404 
0405     val = attr.as_int();
0406     return true;
0407 }
0408 
0409 template <class TNodeType>
0410 inline bool TXmlParser<TNodeType>::getRealAttribute(XmlNode &xmlNode, const char *name, ai_real &val) {
0411     pugi::xml_attribute attr = xmlNode.attribute(name);
0412     if (attr.empty()) {
0413         return false;
0414     }
0415 #ifdef ASSIMP_DOUBLE_PRECISION
0416     val = attr.as_double();
0417 #else
0418     val = attr.as_float();
0419 #endif
0420     return true;
0421 }
0422 
0423 template <class TNodeType>
0424 inline bool TXmlParser<TNodeType>::getFloatAttribute(XmlNode &xmlNode, const char *name, float &val) {
0425     pugi::xml_attribute attr = xmlNode.attribute(name);
0426     if (attr.empty()) {
0427         return false;
0428     }
0429 
0430     val = attr.as_float();
0431 
0432     return true;
0433 }
0434 
0435 template <class TNodeType>
0436 inline bool TXmlParser<TNodeType>::getDoubleAttribute(XmlNode &xmlNode, const char *name, double &val) {
0437     pugi::xml_attribute attr = xmlNode.attribute(name);
0438     if (attr.empty()) {
0439         return false;
0440     }
0441 
0442     val = attr.as_double();
0443 
0444     return true;
0445 }
0446 
0447 template <class TNodeType>
0448 inline bool TXmlParser<TNodeType>::getStdStrAttribute(XmlNode &xmlNode, const char *name, std::string &val) {
0449     pugi::xml_attribute attr = xmlNode.attribute(name);
0450     if (attr.empty()) {
0451         return false;
0452     }
0453 
0454     val = attr.as_string();
0455 
0456     return true;
0457 }
0458 
0459 template <class TNodeType>
0460 inline bool TXmlParser<TNodeType>::getBoolAttribute(XmlNode &xmlNode, const char *name, bool &val) {
0461     pugi::xml_attribute attr = xmlNode.attribute(name);
0462     if (attr.empty()) {
0463         return false;
0464     }
0465 
0466     val = attr.as_bool();
0467 
0468     return true;
0469 }
0470 
0471 template <class TNodeType>
0472 inline bool TXmlParser<TNodeType>::getValueAsString(XmlNode &node, std::string &text) {
0473     text = std::string();
0474     if (node.empty()) {
0475         return false;
0476     }
0477 
0478     text = node.text().as_string();
0479     text = ai_trim(text);
0480 
0481     return true;
0482 }
0483 
0484 template <class TNodeType>
0485 inline bool TXmlParser<TNodeType>::getValueAsReal(XmlNode& node, ai_real& v) {
0486     if (node.empty()) {
0487         return false;
0488     }
0489 
0490     v = node.text().as_float();
0491 
0492     return true;
0493 }
0494 
0495 
0496 template <class TNodeType>
0497 inline bool TXmlParser<TNodeType>::getValueAsFloat(XmlNode &node, float &v) {
0498     if (node.empty()) {
0499         return false;
0500     }
0501 
0502     v = node.text().as_float();
0503 
0504     return true;
0505 }
0506 
0507 template <class TNodeType>
0508 inline bool TXmlParser<TNodeType>::getValueAsInt(XmlNode &node, int &v) {
0509     if (node.empty()) {
0510         return false;
0511     }
0512 
0513     v = node.text().as_int();
0514 
0515     return true;
0516 }
0517 
0518 template <class TNodeType>
0519 inline bool TXmlParser<TNodeType>::getValueAsBool(XmlNode &node, bool &v) {
0520     if (node.empty()) {
0521         return false;
0522     }
0523 
0524     v = node.text().as_bool();
0525 
0526     return true;
0527 }
0528 
0529 using XmlParser = TXmlParser<pugi::xml_node>;
0530 
0531 /// @brief  This class declares an iterator to loop through all children of the root node.
0532 class XmlNodeIterator {
0533 public:
0534     /// @brief The iteration mode.
0535     enum IterationMode {
0536         PreOrderMode, ///< Pre-ordering, get the values, continue the iteration.
0537         PostOrderMode ///< Post-ordering, continue the iteration, get the values.
0538     };
0539     /// @brief  The class constructor
0540     /// @param  parent      [in] The xml parent to to iterate through.
0541     /// @param  mode        [in] The iteration mode.
0542     explicit XmlNodeIterator(XmlNode &parent, IterationMode mode) :
0543             mParent(parent),
0544             mNodes(),
0545             mIndex(0) {
0546         if (mode == PreOrderMode) {
0547             collectChildrenPreOrder(parent);
0548         } else {
0549             collectChildrenPostOrder(parent);
0550         }
0551     }
0552 
0553     /// @brief  The class destructor, default implementation.
0554     ~XmlNodeIterator() = default;
0555 
0556     /// @brief  Will iterate through all children in pre-order iteration.
0557     /// @param  node    [in] The nod to iterate through.
0558     void collectChildrenPreOrder(XmlNode &node) {
0559         if (node != mParent && node.type() == pugi::node_element) {
0560             mNodes.push_back(node);
0561         }
0562         for (XmlNode currentNode : node.children()) {
0563             collectChildrenPreOrder(currentNode);
0564         }
0565     }
0566 
0567     /// @brief  Will iterate through all children in post-order iteration.
0568     /// @param  node    [in] The nod to iterate through.
0569     void collectChildrenPostOrder(XmlNode &node) {
0570         for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
0571             collectChildrenPostOrder(currentNode);
0572         }
0573         if (node != mParent) {
0574             mNodes.push_back(node);
0575         }
0576     }
0577 
0578     /// @brief  Will iterate through all collected nodes.
0579     /// @param  next    The next node, if there is any.
0580     /// @return true, if there is a node left.
0581     bool getNext(XmlNode &next) {
0582         if (mIndex == mNodes.size()) {
0583             return false;
0584         }
0585 
0586         next = mNodes[mIndex];
0587         ++mIndex;
0588 
0589         return true;
0590     }
0591 
0592     /// @brief  Will return the number of collected nodes.
0593     /// @return The number of collected nodes.
0594     size_t size() const {
0595         return mNodes.size();
0596     }
0597 
0598     /// @brief  Returns true, if the node is empty.
0599     /// @return true, if the node is empty, false if not.
0600     bool isEmpty() const {
0601         return mNodes.empty();
0602     }
0603 
0604     /// @brief  Will clear all collected nodes.
0605     void clear() {
0606         if (mNodes.empty()) {
0607             return;
0608         }
0609 
0610         mNodes.clear();
0611         mIndex = 0;
0612     }
0613 
0614 private:
0615     XmlNode &mParent;
0616     std::vector<XmlNode> mNodes;
0617     size_t mIndex;
0618 };
0619 
0620 } // namespace Assimp
0621 
0622 #endif // !! INCLUDED_AI_IRRXML_WRAPPER