XMLConfiguration.cpp
Go to the documentation of this file.
00001 
00002 //#include <stdlib.h>
00003 
00004 #include <threemxl/platform/io/configuration/XMLConfiguration.h>
00005 #include <string.h>
00006 #include <threemxl/platform/io/logging/Log2.h>
00007 
00008 #ifdef _MSC_VER
00009         #define CONST_PATH_SEPARATOR    '\\'
00010 #else
00011         #define CONST_PATH_SEPARATOR    '/'
00012 #endif
00013 
00014 #define CONST_XML_PATH_SEPARATOR        '/'     // Separator for paths defined inside XML files: <path>node/subnode/subsubnode</path>
00015 
00016 // Define this constant if you want to output two debug files: one before and one after processing the <include> tags
00017 //#define XMLCONF_INCLUDETAG_DEBUG_OUTPUT
00018 
00019 
00020 // The global logger for XML Configuration.
00021 // This avoids a CLog2 in every IConfig, and avoids logging in const functions like toFloat() const;
00022 CLog2   gXmlLog("xml");
00023 
00024 
00025 // ************************************************************************ //
00026 // ************************** IXMLConfigSection *************************** //
00027 // ************************************************************************ //
00028 
00029 IXMLConfigSection::IXMLConfigSection(TiXmlElement* pElement)
00030 {
00031         mPElement = pElement;
00032 }
00033 
00034 IXMLConfigSection::~IXMLConfigSection()
00035 {
00036         //printf("Deleted an IXMLConfigSection.\n");
00037 }
00038 
00039 bool IXMLConfigSection::isSection(TiXmlElement* pElement) const
00040 {
00041         // An element is a section if it has child elements (which are either properties or sections)
00042         return pElement->FirstChildElement() != NULL;
00043 }
00044 
00045 bool IXMLConfigSection::isProperty(TiXmlElement* pElement) const
00046 {
00047         TiXmlNode *firstChild = pElement->FirstChild();
00048         if (firstChild != NULL)
00049                 return pElement->FirstChild()->ToText() != NULL;
00050         else
00051                 return false;
00052 }
00053 
00054 std::string IXMLConfigSection::name() const
00055 {
00056         return mPElement->Value();      //TODO: make this ValueStr()?
00057 }
00058 
00059 bool IXMLConfigSection::hasSection(const std::string& section) const
00060 {
00061         return mPElement->FirstChildElement(section.c_str()) != NULL;
00062 }
00063 
00064 IConfigSection* IXMLConfigSection::parent()
00065 {
00066         if (mPElement->Parent() != NULL)
00067         {
00068                 if (mPElement->Parent()->Type() == TiXmlElement::TINYXML_ELEMENT)
00069                         return (IXMLConfigSection*)registerPendingInterface(new IXMLConfigSection((TiXmlElement*)mPElement->Parent()));
00070                 else
00071                         return NULL;
00072         }
00073         else
00074                 return NULL;
00075 }
00076 
00077 IConfigSection* IXMLConfigSection::section(const std::string& section)
00078 {
00079         TiXmlElement* foundElement = mPElement->FirstChildElement(section.c_str());
00080         if (foundElement)
00081                 return (IXMLConfigSection*)registerPendingInterface(new IXMLConfigSection(foundElement));
00082         else
00083                 return NULL;
00084 }
00085 
00086 IConfigSection* IXMLConfigSection::firstSection()
00087 {
00088         // Search for a section, which is a TiXmlElement in the first place.
00089         TiXmlElement* foundSection = NULL;
00090         TiXmlElement* iElement = mPElement->FirstChildElement();
00091         while (iElement != NULL)
00092         {
00093                 if (isSection(iElement))
00094                 {
00095                         foundSection = iElement;
00096                         break;
00097                 }
00098                 iElement = iElement->NextSiblingElement();
00099         }
00100 
00101         if (foundSection)
00102                 return (IXMLConfigSection*)registerPendingInterface(new IXMLConfigSection(foundSection));
00103         else
00104                 return NULL;
00105 }
00106 
00107 IConfigSection* IXMLConfigSection::nextSection()
00108 {
00109         TiXmlElement* foundSection = NULL;
00110         TiXmlElement* iElement = mPElement->NextSiblingElement();
00111         while (iElement != NULL)
00112         {
00113                 if (isSection(iElement))
00114                 {
00115                         foundSection = iElement;
00116                         break;
00117                 }
00118                 iElement = iElement->NextSiblingElement();
00119         }
00120 
00121         if (foundSection)
00122                 return (IXMLConfigSection*)registerPendingInterface(new IXMLConfigSection(foundSection));
00123         else
00124                 return NULL;
00125 }
00126 
00127 IConfigSection* IXMLConfigSection::nextSimilarSection()
00128 {
00129         TiXmlElement* foundElement = mPElement->NextSiblingElement(mPElement->Value());
00130         if (foundElement)
00131                 return (IXMLConfigSection*)registerPendingInterface(new IXMLConfigSection(foundElement));
00132         else
00133                 return NULL;
00134 }
00135 
00136 /*
00137 bool IXMLConfigSection::get(const std::string& property, std::string *value) const
00138 {
00139         TiXmlElement *element = mPElement->FirstChildElement(property.c_str());
00140         if (element != NULL)
00141         {
00142                 *value = element->GetText();
00143                 return true;
00144         }
00145         else
00146                 return false;
00147 }
00148 
00149 bool IXMLConfigSection::get(const std::string& property, bool *value) const
00150 {
00151         TiXmlElement *element = mPElement->FirstChildElement(property.c_str());
00152         if (element != NULL)
00153         {
00154                 const char* valStr = element->GetText();
00155 
00156                 if (strcasecmp(valStr, "true") == 0)
00157                         *value = true;
00158                 else if (strcasecmp(valStr, "yes") == 0)
00159                         *value = true;
00160                 else if (strcasecmp(valStr, "false") == 0)
00161                         *value = false;
00162                 else if (strcasecmp(valStr, "no") == 0)
00163                         *value = false;
00164                 else
00165                         *value = (bool)atoi(valStr);
00166 
00167                 return true;
00168         }
00169         else
00170                 return false;
00171 }
00172 
00173 bool IXMLConfigSection::get(const std::string& property, char *value) const
00174 {
00175         long temp;
00176         if (get(property, &temp))
00177         {
00178                 *value = (char)temp;
00179                 return true;
00180         }
00181         else
00182                 return false;
00183 }
00184 
00185 bool IXMLConfigSection::get(const std::string& property, unsigned char *value) const
00186 {
00187         unsigned long temp;
00188         if (get(property, &temp))
00189         {
00190                 *value = (unsigned char)temp;
00191                 return true;
00192         }
00193         else
00194                 return false;
00195 }
00196 
00197 bool IXMLConfigSection::get(const std::string& property, short *value) const
00198 {
00199         long temp;
00200         if (get(property, &temp))
00201         {
00202                 *value = (short)temp;
00203                 return true;
00204         }
00205         else
00206                 return false;
00207 }
00208 
00209 bool IXMLConfigSection::get(const std::string& property, unsigned short *value) const
00210 {
00211         unsigned long temp;
00212         if (get(property, &temp))
00213         {
00214                 *value = (unsigned short)temp;
00215                 return true;
00216         }
00217         else
00218                 return false;
00219 }
00220 
00221 bool IXMLConfigSection::get(const std::string& property, int *value) const
00222 {
00223         long temp;
00224         if (get(property, &temp))
00225         {
00226                 *value = (short)temp;
00227                 return true;
00228         }
00229         else
00230                 return false;
00231 }
00232 
00233 bool IXMLConfigSection::get(const std::string& property, unsigned int *value) const
00234 {
00235         unsigned long temp;
00236         if (get(property, &temp))
00237         {
00238                 *value = (unsigned short)temp;
00239                 return true;
00240         }
00241         else
00242                 return false;
00243 }
00244 
00245 bool IXMLConfigSection::get(const std::string& property, long *value) const
00246 {
00247         TiXmlElement *element = mPElement->FirstChildElement(property.c_str());
00248         if (element != NULL)
00249         {
00250                 *value = strtol(element->GetText(), NULL, 10);
00251                 return true;
00252         }
00253         else
00254                 return false;
00255 }
00256 
00257 bool IXMLConfigSection::get(const std::string& property, unsigned long *value) const
00258 {
00259         TiXmlElement *element = mPElement->FirstChildElement(property.c_str());
00260         if (element != NULL)
00261         {
00262                 *value = strtoul(element->GetText(), NULL, 10);
00263                 return true;
00264         }
00265         else
00266                 return false;
00267 }
00268 
00269 bool IXMLConfigSection::get(const std::string& property, double *value) const
00270 {
00271         TiXmlElement *element = mPElement->FirstChildElement(property.c_str());
00272         if (element != NULL)
00273         {
00274                 *value = atof(element->GetText());
00275                 return true;
00276         }
00277         else
00278                 return false;
00279 }
00280 
00281 void IXMLConfigSection::set(const std::string& property, const std::string& value)
00282 {
00283 
00284 }
00285 
00286 */
00287 bool IXMLConfigSection::has(const std::string& property) const
00288 {
00289         return mPElement->FirstChildElement(property.c_str()) != NULL;
00290 }
00291 
00292 /*
00293 bool IXMLConfigSection::isNull() const
00294 {
00295         return (mPElement == NULL);
00296 }
00297 */
00298 
00299 IConfigProperty* IXMLConfigSection::get(const std::string& property)
00300 {
00301         TiXmlElement* foundElement = mPElement->FirstChildElement(property.c_str());
00302         //printf("woohoo, called IXMLConfigSection::get(%s), which is %s\n", property.c_str(), foundElement?"non-null.":"NULL!");
00303         if (foundElement)
00304                 return (IXMLConfigProperty*)registerPendingInterface(new IXMLConfigProperty(foundElement));
00305         else
00306                 return NULL;
00307 }
00308 
00309 IConfigProperty* IXMLConfigSection::firstProperty()
00310 {
00311         // Search for a property, which is a TiXmlElement in the first place.
00312         TiXmlElement* foundProperty = NULL;
00313         TiXmlElement* iElement = mPElement->FirstChildElement();
00314         while (iElement != NULL)
00315         {
00316                 if (isProperty(iElement))
00317                 {
00318                         foundProperty = iElement;
00319                         break;
00320                 }
00321                 iElement = iElement->NextSiblingElement();
00322         }
00323 
00324         if (foundProperty)
00325                 return (IXMLConfigProperty*)registerPendingInterface(new IXMLConfigProperty(foundProperty));
00326         else
00327                 return NULL;
00328 }
00329 
00330 
00331 // ************************************************************************ //
00332 // ************************** IXMLConfigProperty ************************** //
00333 // ************************************************************************ //
00334 
00335 IXMLConfigProperty::IXMLConfigProperty(TiXmlElement* pElement)
00336 {
00337         mPElement = pElement;
00338 }
00339 
00340 IXMLConfigProperty::~IXMLConfigProperty()
00341 {
00342         //printf("Deleted an IXMLConfigProperty.\n");
00343 }
00344 
00345 bool IXMLConfigProperty::isProperty(TiXmlElement* pElement) const
00346 {
00347         if (pElement->FirstChild() != NULL)
00348                 return pElement->FirstChild()->ToText() != NULL;
00349         else
00350                 // No first child at all -> cannot be a property
00351                 return false;
00352 }
00353 
00354 std::string IXMLConfigProperty::name() const
00355 {
00356         return mPElement->Value();      //TODO: make this ValueStr()?
00357 }
00358 
00359 std::string IXMLConfigProperty::toString() const
00360 {
00361         const char* result = mPElement->GetText();
00362         if (result)
00363                 return result;
00364         else
00365                 return "";
00366 }
00367 
00368 void IXMLConfigProperty::set(const std::string& value)
00369 {
00370         TiXmlText* text = mPElement->FirstChild()->ToText();
00371         if (text != NULL)
00372                 text->SetValue(value.c_str());
00373 }
00374 
00375 IConfigProperty* IXMLConfigProperty::nextProperty()
00376 {
00377         // Search for a property, which is a TiXmlElement in the first place.
00378         TiXmlElement* foundProperty = NULL;
00379         TiXmlElement* iElement = mPElement->NextSiblingElement();
00380         while (iElement != NULL)
00381         {
00382                 if (isProperty(iElement))
00383                 {
00384                         foundProperty = iElement;
00385                         break;
00386                 }
00387                 iElement = iElement->NextSiblingElement();
00388         }
00389 
00390         if (foundProperty)
00391                 return (IXMLConfigProperty*)registerPendingInterface(new IXMLConfigProperty(foundProperty));
00392         else
00393                 return NULL;
00394 }
00395 
00396 bool IXMLConfigProperty::isVerbose() const
00397 {
00398         const char* verboseStr = mPElement->Attribute("verbose");
00399         if (verboseStr != NULL)
00400         {
00401                 // Everything other than 'false' or '0' will be regarded as true
00402                 if ((strcasecmp(verboseStr, "false") == 0) || (strcasecmp(verboseStr, "0") == 0))
00403                         return false;
00404                 else
00405                         return true;
00406         }
00407         else
00408                 return false;
00409 }
00410 
00411 /*
00412 bool IXMLConfigProperty::isNull() const
00413 {
00414         return (mPElement == NULL);
00415 }
00416 */
00417 
00418 // ************************************************************************ //
00419 // ************************** CXMLConfiguration *************************** //
00420 // ************************************************************************ //
00421 
00422 CXMLConfiguration::CXMLConfiguration():
00423         mFilename("")
00424 {
00425         mPRootConfigSection = NULL;
00426 }
00427 
00428 CXMLConfiguration::~CXMLConfiguration()
00429 {
00430         if (mPRootConfigSection != NULL)
00431                 delete mPRootConfigSection;
00432 }
00433 
00434 // Searches for the XML nodes that fall into the nodePath (can contain wildcards, slashes, etc).
00435 bool CXMLConfiguration::findXmlNode(TiXmlElement* rootElement, const std::string& nodePath, TiXmlElementList* resultList)
00436 {
00437         std::string::size_type searchPos = nodePath.find(CONST_XML_PATH_SEPARATOR, 0);
00438         std::string::size_type prevSearchPos = searchPos;
00439         std::string::size_type firstNodeLength = (searchPos==std::string::npos)?nodePath.length():searchPos;
00440         std::string nodeName = nodePath.substr(0, firstNodeLength);
00441         TiXmlElement *e = rootElement;
00442         // Keep searching for CONST_XML_PATH_SEPARATOR's
00443         while ((searchPos  != std::string::npos))
00444         {
00445                 // When the path starts with "/" or contains multiple consecutive separators ("///"), the nodeName is empty and e should not be changed
00446                 if (!nodeName.empty())
00447                         e = e->FirstChildElement(nodeName.c_str());
00448                 // Check if node exists
00449                 if (e == NULL)
00450                         return false;
00451 
00452                 // Search for next XML path separator
00453                 searchPos = nodePath.find(CONST_XML_PATH_SEPARATOR, searchPos+1);
00454                 if (searchPos != std::string::npos)
00455                         // We found another path separator; store the new node name so that it can be processed the next iteration
00456                         nodeName = nodePath.substr(prevSearchPos+1, searchPos-prevSearchPos-1);
00457                 else
00458                         // Store the remainder of the path into nodeName - this can result in an empty string if the path ends with a path separator.
00459                         nodeName = nodePath.substr(prevSearchPos+1, nodePath.length()-prevSearchPos-1);
00460 
00461                 prevSearchPos = searchPos;
00462         }
00463 
00464         if (!nodeName.empty())
00465         {
00466                 // Only one child node was specified; add it, if it exists
00467                 e = e->FirstChildElement(nodeName.c_str());
00468                 if (e == NULL)
00469                         return false;
00470                 else
00471                         resultList->push_back(e);
00472         }
00473         else
00474         {
00475                 // Add all subnodes of the found node
00476                 for (TiXmlElement *child = e->FirstChildElement(); child; child = child->NextSiblingElement())
00477                         resultList->push_back(child);
00478         }
00479 
00480         //resultList->push_back(rootNode->FirstChild("ode"));
00481         return true;
00482 //      return rootNode->FirstChild("configuration")->FirstChild("ode");
00483 }
00484 
00485 bool CXMLConfiguration::loadFile(const std::string& filename)
00486 {
00487         mFilename = filename;
00488         bool result = mXMLDocument.LoadFile(filename.c_str());
00489         if (mPRootConfigSection != NULL)
00490                 delete mPRootConfigSection;
00491         TiXmlElement *rootElement = mXMLDocument.FirstChildElement(CONST_ConfRootSectionName);
00492         if (rootElement != NULL)
00493         {
00494                 mPRootConfigSection = new IXMLConfigSection(rootElement);
00495                 // Process includes inside the configuration. Can only come from other configuration files!
00496 
00497 #ifdef XMLCONF_INCLUDETAG_DEBUG_OUTPUT
00498                 mXMLDocument.SaveFile("XmlDEBUG_BeforeIncludes.xml");
00499 #endif
00500 
00501                 std::string filePath = mFilename.substr( 0, mFilename.rfind(CONST_PATH_SEPARATOR)+1);
00502                 processIncludes(rootElement, rootElement, filePath);
00503 
00504 #ifdef XMLCONF_INCLUDETAG_DEBUG_OUTPUT
00505                 mXMLDocument.SaveFile("XmlDEBUG_AfterIncludes.xml");
00506 #endif
00507 
00508                 return result;
00509         }
00510         else
00511                 return false;
00512 }
00513 
00514 
00515 void CXMLConfiguration::processIncludes(TiXmlElement* rootNode, TiXmlElement* node, const std::string& filePath)
00516 {
00517         // Walk through entire document and replace all include nodes
00518         TiXmlElement* nextChild;
00519         for(TiXmlElement* child = node->FirstChildElement(); child; child = nextChild)
00520         {
00521                 nextChild = child->NextSiblingElement();        // Request next child here, because we may delete child if it turns out to be an <include> node!
00522                 if (child->Value() == std::string("include"))
00523                 {
00524                         // Process the include
00525                         TiXmlElement* filenameNode = child->FirstChildElement("filename");
00526                         if (filenameNode != NULL)
00527                         {
00528                                 std::string filename = filenameNode->GetText();
00529                                 // If filename is not absolute, interpret the filename as relative to the main filename (mFilename)
00530                                 if (filename[1] != ':' && filename[0] != '/')
00531                                 {
00532                                         // Add mFilename's path to filename
00533                                         filename = filePath + filename;
00534                                 }
00535                                 mLogDebugLn("Processing XML include file " << filename << " ...");
00536                                 TiXmlDocument includeDoc;
00537                                 if (includeDoc.LoadFile(filename.c_str()))
00538                                 {
00539                                         // We loaded the file, now do the following: 1. Add source paths, 2. Add constants and strings.
00540                                         TiXmlElement *includeRootNode = includeDoc.FirstChildElement(CONST_ConfRootSectionName);
00541                                         if (includeRootNode != NULL)
00542                                         {
00543                                                 // 1) Add all provided paths to the original file, but find nested includes in the includeDoc itself first.
00544                                                 //    Paths are assumed to be defined relative to the "configuration" root node of the file
00545                                                 // Also process this root node
00546                                                 std::string includeFilePath = filename.substr( 0, filename.rfind(CONST_PATH_SEPARATOR)+1);
00547                                                 processIncludes(includeRootNode, includeRootNode, includeFilePath);
00548                                                 // Process all path nodes
00549                                                 TiXmlNode       *insertAfter = child;   // Maintain node order by incrementing insertion point
00550                                                 for (TiXmlElement *pathNode = child->FirstChildElement("path"); pathNode; pathNode = pathNode->NextSiblingElement("path"))
00551                                                 {
00552                                                         TiXmlElementList resultNodes;
00553                                                         bool searchResult = findXmlNode(includeRootNode, pathNode->GetText(), &resultNodes);
00554                                                         if (searchResult)
00555                                                         {
00556                                                                 for (unsigned int iResNode=0; iResNode<resultNodes.size(); iResNode++)
00557                                                                 {
00558                                                                         // Skip constants and strings sections
00559                                                                         if ((resultNodes[iResNode] != includeRootNode->FirstChildElement(CONST_ConfConstantsSectionName))
00560                                                                                         && (resultNodes[iResNode] != includeRootNode->FirstChildElement(CONST_ConfStringsSectionName)))
00561                                                                         insertAfter = node->InsertAfterChild(insertAfter, *(resultNodes[iResNode]));
00562                                                                 }
00563                                                         }
00564                                                         else
00565                                                                 mLogErrorLn("Could not find path \"" << pathNode->GetText() << "\" in XML file \"" << filename << "\"!");
00566                                                 }
00567                                                 // We're done with this include node; remove it
00568                                                 node->RemoveChild(child);
00569 
00570 
00571                                                 // 2) Add the constants and strings sections of this file automatically to the rootNode
00572                                                 //    If they do not exist in the original document, create such a section. Otherwise, merge.
00573                                                 //
00574                                                 // Constants (CONST_ConfConstantsSectionName)
00575                                                 TiXmlElement *includeConstantsSection = includeRootNode->FirstChildElement(CONST_ConfConstantsSectionName);
00576                                                 if (includeConstantsSection != NULL)
00577                                                 {
00578                                                         TiXmlElement *rootConstantsSection = rootNode->FirstChildElement(CONST_ConfConstantsSectionName);
00579                                                         if (rootConstantsSection == NULL)
00580                                                                 // Insert
00581                                                                 rootNode->InsertEndChild(*includeConstantsSection);
00582                                                         else
00583                                                         {       // Merge
00584                                                                 for (TiXmlElement *constElement = includeConstantsSection->FirstChildElement(); constElement; constElement = constElement->NextSiblingElement())
00585                                                                         rootConstantsSection->InsertEndChild(*constElement);
00586                                                         }
00587                                                 }
00588                                                 // Strings (CONST_ConfStringsSectionName)
00589                                                 TiXmlElement *includeStringsSection = includeRootNode->FirstChildElement(CONST_ConfStringsSectionName);
00590                                                 if (includeStringsSection != NULL)
00591                                                 {
00592                                                         TiXmlElement *rootStringsSection = rootNode->FirstChildElement(CONST_ConfStringsSectionName);
00593                                                         if (rootStringsSection == NULL)
00594                                                                 // Insert
00595                                                                 rootNode->InsertEndChild(*includeStringsSection);
00596                                                         else
00597                                                         {       // Merge
00598                                                                 for (TiXmlElement *stringElement = includeStringsSection->FirstChildElement(); stringElement; stringElement = stringElement->NextSiblingElement())
00599                                                                         rootStringsSection->InsertEndChild(*stringElement);
00600                                                         }
00601                                                 }
00602                                         }
00603                                         else
00604                                                 mLogErrorLn("Could not find root node \"" << CONST_ConfRootSectionName << "\" in XML file \"" << filename << "\"! Include paths are always relative to this root node.");
00605                                 }
00606                                 else
00607                                         mLogErrorLn("[ERROR] Could not load input XML file \"" << filename << "\"! Error: " << includeDoc.ErrorDesc());
00608                         }
00609                 }
00610                 else
00611                         // Recursive processing
00612                         processIncludes(rootNode, child, filePath);
00613         }
00614 
00615 }
00616 
00617 bool CXMLConfiguration::saveFile(const std::string& filename)
00618 {
00619         if (filename == "")     // Default
00620         {
00621                 // No deviating filename was provided
00622                 if (mFilename == "")
00623                         // If we also don't have an mFilename, return false
00624                         return false;
00625                 else
00626                         return mXMLDocument.SaveFile(mFilename.c_str());
00627         }
00628         else
00629                 // Save using the provided filename
00630                 return mXMLDocument.SaveFile(filename.c_str());
00631 }
00632 
00633 bool CXMLConfiguration::reload()
00634 {
00635         if (mFilename == "")
00636                 return false;
00637         else
00638                 return mXMLDocument.LoadFile(mFilename.c_str());
00639 }
00640 
00641 std::string CXMLConfiguration::errorStr()
00642 {
00643         std::string errStr(mXMLDocument.ErrorDesc());
00644         char errStr2[100];
00645         sprintf(errStr2, " at row %d, col %d", mXMLDocument.ErrorRow(), mXMLDocument.ErrorCol());
00646         return  errStr + errStr2;
00647 }
00648 
00649 void CXMLConfiguration::clear()
00650 {
00651         mXMLDocument.Clear();
00652 }
00653 
00654 
00655 CConfigSection CXMLConfiguration::root()
00656 {
00657         return CConfigSection(mPRootConfigSection);
00658 }
00659 
00660 void CXMLConfiguration::print()
00661 {
00662         mXMLDocument.Print();
00663 }


threemxl
Author(s):
autogenerated on Fri Aug 28 2015 13:21:08