1 /*
2  * Copyright (c) 2011-2014, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation and/or
13  * other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 #include "XmlElement.h"
31 #include "PfError.hpp"
32 #include <libxml/tree.h>
33 #include "convert.hpp"
34 #include <stdlib.h>
35 #include <stdexcept>
36 
37 using std::string;
38 
CXmlElement(_xmlNode * pXmlElement)39 CXmlElement::CXmlElement(_xmlNode *pXmlElement) : _pXmlElement(pXmlElement)
40 {
41 }
42 
CXmlElement()43 CXmlElement::CXmlElement() : _pXmlElement(nullptr)
44 {
45 }
46 
setXmlElement(_xmlNode * pXmlElement)47 void CXmlElement::setXmlElement(_xmlNode *pXmlElement)
48 {
49     _pXmlElement = pXmlElement;
50 }
51 
getType() const52 string CXmlElement::getType() const
53 {
54     return (const char *)_pXmlElement->name;
55 }
56 
getPath() const57 string CXmlElement::getPath() const
58 {
59     string strPathElement = "/" + getType();
60 
61     if (hasAttribute("Name")) {
62 
63         strPathElement += "[@Name=" + getNameAttribute() + "]";
64     }
65 
66     CXmlElement parentElement;
67 
68     if (getParentElement(parentElement)) {
69 
70         // Done
71         return parentElement.getPath() + strPathElement;
72     }
73     return strPathElement;
74 }
75 
hasAttribute(const string & strAttributeName) const76 bool CXmlElement::hasAttribute(const string &strAttributeName) const
77 {
78     return xmlHasProp(_pXmlElement, (const xmlChar *)strAttributeName.c_str()) != nullptr;
79 }
80 
81 template <>
getAttribute(const string & name,string & value) const82 bool CXmlElement::getAttribute<std::string>(const string &name, string &value) const
83 {
84     string backup = value;
85     xmlChar *pucXmlValue = xmlGetProp((xmlNode *)_pXmlElement, (const xmlChar *)name.c_str());
86     if (pucXmlValue == nullptr) {
87         value = backup;
88         return false;
89     }
90 
91     value = (const char *)pucXmlValue;
92 
93     xmlFree(pucXmlValue);
94 
95     return true;
96 }
97 
98 template <typename T>
getAttribute(const std::string & name,T & value) const99 bool CXmlElement::getAttribute(const std::string &name, T &value) const
100 {
101     std::string rawValue;
102     if (!getAttribute(name, rawValue)) {
103         return false;
104     }
105 
106     T backup = value;
107     if (!convertTo<T>(rawValue, value)) {
108         value = backup;
109         throw PfError("\'" + rawValue + "\' could not be parsed as the requested type.");
110     }
111 
112     return true;
113 }
114 
getNameAttribute() const115 string CXmlElement::getNameAttribute() const
116 {
117     string attribute;
118     getAttribute("Name", attribute);
119     return attribute;
120 }
121 
getTextContent() const122 string CXmlElement::getTextContent() const
123 {
124     xmlChar *pucXmlContent = xmlNodeGetContent(_pXmlElement);
125     if (pucXmlContent == nullptr) {
126         return "";
127     }
128 
129     string strContent((const char *)pucXmlContent);
130 
131     xmlFree(pucXmlContent);
132 
133     return strContent;
134 }
135 
getChildElement(const string & strType,CXmlElement & childElement) const136 bool CXmlElement::getChildElement(const string &strType, CXmlElement &childElement) const
137 {
138     CChildIterator childIterator(*this);
139 
140     while (childIterator.next(childElement)) {
141 
142         if (childElement.getType() == strType) {
143 
144             return true;
145         }
146     }
147     return false;
148 }
149 
getChildElement(const string & strType,const string & strNameAttribute,CXmlElement & childElement) const150 bool CXmlElement::getChildElement(const string &strType, const string &strNameAttribute,
151                                   CXmlElement &childElement) const
152 {
153     CChildIterator childIterator(*this);
154 
155     while (childIterator.next(childElement)) {
156 
157         if ((childElement.getType() == strType) &&
158             (childElement.getNameAttribute() == strNameAttribute)) {
159 
160             return true;
161         }
162     }
163     return false;
164 }
165 
getNbChildElements() const166 size_t CXmlElement::getNbChildElements() const
167 {
168     CXmlElement childElement;
169     size_t uiNbChildren = 0;
170 
171     CChildIterator childIterator(*this);
172 
173     while (childIterator.next(childElement)) {
174 
175         uiNbChildren++;
176     }
177     return uiNbChildren;
178 }
179 
getParentElement(CXmlElement & parentElement) const180 bool CXmlElement::getParentElement(CXmlElement &parentElement) const
181 {
182     _xmlNode *pXmlNode = _pXmlElement->parent;
183 
184     if (pXmlNode->type == XML_ELEMENT_NODE) {
185 
186         parentElement.setXmlElement(pXmlNode);
187 
188         return true;
189     }
190     return false;
191 }
192 
193 template <>
setAttribute(const string & name,const bool & value)194 void CXmlElement::setAttribute<bool>(const string &name, const bool &value)
195 {
196     setAttribute(name, value ? "true" : "false");
197 }
198 
199 template <>
setAttribute(const string & name,const string & value)200 void CXmlElement::setAttribute<std::string>(const string &name, const string &value)
201 {
202     setAttribute(name, value.c_str());
203 }
204 
205 // This method exists for 2 reasons:
206 //  - at link time, all calls to setAttribute(const string&, const char [N])
207 //    for any value of N will all resolve to this method; this prevents the
208 //    need for one template instance per value of N.
209 //  - the libxml2 API takes a C-style string anyway.
setAttribute(const string & name,const char * value)210 void CXmlElement::setAttribute(const string &name, const char *value)
211 {
212     xmlNewProp(_pXmlElement, BAD_CAST name.c_str(), BAD_CAST value);
213 }
214 
215 template <typename T>
setAttribute(const std::string & name,const T & value)216 void CXmlElement::setAttribute(const std::string &name, const T &value)
217 {
218     setAttribute(name, std::to_string(value).c_str());
219 }
220 
setNameAttribute(const string & strValue)221 void CXmlElement::setNameAttribute(const string &strValue)
222 {
223     setAttribute("Name", strValue);
224 }
225 
setTextContent(const string & strContent)226 void CXmlElement::setTextContent(const string &strContent)
227 {
228     xmlAddChild(_pXmlElement, xmlNewText(BAD_CAST strContent.c_str()));
229 }
230 
231 // Child creation
createChild(CXmlElement & childElement,const string & strType)232 void CXmlElement::createChild(CXmlElement &childElement, const string &strType)
233 {
234 #ifdef LIBXML_TREE_ENABLED
235     xmlNodePtr pChildNode = xmlNewChild(_pXmlElement, nullptr, BAD_CAST strType.c_str(), nullptr);
236 
237     childElement.setXmlElement(pChildNode);
238 #endif
239 }
240 
241 // Child iteration
CChildIterator(const CXmlElement & xmlElement)242 CXmlElement::CChildIterator::CChildIterator(const CXmlElement &xmlElement)
243     : _pCurNode(xmlElement._pXmlElement->children)
244 {
245 }
246 
next(CXmlElement & xmlChildElement)247 bool CXmlElement::CChildIterator::next(CXmlElement &xmlChildElement)
248 {
249     while (_pCurNode) {
250 
251         if (_pCurNode->type == XML_ELEMENT_NODE) {
252 
253             xmlChildElement.setXmlElement(_pCurNode);
254 
255             _pCurNode = _pCurNode->next;
256 
257             return true;
258         }
259         _pCurNode = _pCurNode->next;
260     }
261 
262     return false;
263 }
264 
265 template bool CXmlElement::getAttribute(const std::string &name, bool &value) const;
266 template bool CXmlElement::getAttribute(const std::string &name, signed char &value) const;
267 template bool CXmlElement::getAttribute(const std::string &name, unsigned char &value) const;
268 template bool CXmlElement::getAttribute(const std::string &name, short &value) const;
269 template bool CXmlElement::getAttribute(const std::string &name, unsigned short &value) const;
270 template bool CXmlElement::getAttribute(const std::string &name, int &value) const;
271 template bool CXmlElement::getAttribute(const std::string &name, unsigned int &value) const;
272 template bool CXmlElement::getAttribute(const std::string &name, long &value) const;
273 template bool CXmlElement::getAttribute(const std::string &name, unsigned long &value) const;
274 template bool CXmlElement::getAttribute(const std::string &name, long long &value) const;
275 template bool CXmlElement::getAttribute(const std::string &name, unsigned long long &value) const;
276 template bool CXmlElement::getAttribute(const std::string &name, float &value) const;
277 template bool CXmlElement::getAttribute(const std::string &name, double &value) const;
278 
279 template void CXmlElement::setAttribute(const std::string &name, const signed char &value);
280 template void CXmlElement::setAttribute(const std::string &name, const unsigned char &value);
281 template void CXmlElement::setAttribute(const std::string &name, const short &value);
282 template void CXmlElement::setAttribute(const std::string &name, const unsigned short &value);
283 template void CXmlElement::setAttribute(const std::string &name, const int &value);
284 template void CXmlElement::setAttribute(const std::string &name, const unsigned int &value);
285 template void CXmlElement::setAttribute(const std::string &name, const long &value);
286 template void CXmlElement::setAttribute(const std::string &name, const unsigned long &value);
287 template void CXmlElement::setAttribute(const std::string &name, const long long &value);
288 template void CXmlElement::setAttribute(const std::string &name, const unsigned long long &value);
289 template void CXmlElement::setAttribute(const std::string &name, const float &value);
290 template void CXmlElement::setAttribute(const std::string &name, const double &value);
291