1 /*
2  * Copyright (c) 2011-2015, 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 "Element.h"
31 #include "XmlElementSerializingContext.h"
32 #include "ElementLibrary.h"
33 #include "ErrorContext.hpp"
34 #include <algorithm>
35 #include <assert.h>
36 #include <stdio.h>
37 #include <stdarg.h>
38 #include <stdlib.h>
39 
40 using std::string;
41 
42 const std::string CElement::gDescriptionPropertyName = "Description";
43 
CElement(const string & strName)44 CElement::CElement(const string &strName) : _strName(strName)
45 {
46 }
47 
~CElement()48 CElement::~CElement()
49 {
50     removeChildren();
51 }
52 
setDescription(const string & strDescription)53 void CElement::setDescription(const string &strDescription)
54 {
55     _strDescription = strDescription;
56 }
57 
getDescription() const58 const string &CElement::getDescription() const
59 {
60     return _strDescription;
61 }
62 
childrenAreDynamic() const63 bool CElement::childrenAreDynamic() const
64 {
65     // By default, children are searched and not created during xml parsing
66     return false;
67 }
68 
init(string & strError)69 bool CElement::init(string &strError)
70 {
71 
72     for (CElement *child : _childArray) {
73 
74         if (!child->init(strError)) {
75 
76             return false;
77         }
78     }
79 
80     return true;
81 }
82 
dumpContent(utility::ErrorContext & errorContext,const size_t depth) const83 string CElement::dumpContent(utility::ErrorContext &errorContext, const size_t depth) const
84 {
85     string output;
86     string strIndent;
87 
88     // Level
89     size_t indents = depth;
90 
91     while (indents--) {
92 
93         strIndent += "    ";
94     }
95     // Type
96     output += strIndent + "- " + getKind();
97 
98     // Name
99     if (!_strName.empty()) {
100 
101         output += ": " + getName();
102     }
103 
104     // Value
105     string strValue = logValue(errorContext);
106 
107     if (!strValue.empty()) {
108 
109         output += " = " + strValue;
110     }
111 
112     output += "\n";
113 
114     for (CElement *pChild : _childArray) {
115 
116         output += pChild->dumpContent(errorContext, depth + 1);
117     }
118 
119     return output;
120 }
121 
122 // Element properties
showProperties(string & strResult) const123 void CElement::showProperties(string &strResult) const
124 {
125     strResult += "Kind: " + getKind() + "\n";
126     showDescriptionProperty(strResult);
127 }
128 
showDescriptionProperty(std::string & strResult) const129 void CElement::showDescriptionProperty(std::string &strResult) const
130 {
131     if (!getDescription().empty()) {
132         strResult += gDescriptionPropertyName + ": " + getDescription() + "\n";
133     }
134 }
135 
136 // Content dumping
logValue(utility::ErrorContext &) const137 string CElement::logValue(utility::ErrorContext & /*ctx*/) const
138 {
139     return "";
140 }
141 
142 // From IXmlSink
fromXml(const CXmlElement & xmlElement,CXmlSerializingContext & serializingContext)143 bool CElement::fromXml(const CXmlElement &xmlElement, CXmlSerializingContext &serializingContext)
144 {
145     xmlElement.getAttribute(gDescriptionPropertyName, _strDescription);
146 
147     // Propagate through children
148     CXmlElement::CChildIterator childIterator(xmlElement);
149 
150     CXmlElement childElement;
151 
152     while (childIterator.next(childElement)) {
153 
154         CElement *pChild;
155 
156         if (!childrenAreDynamic()) {
157 
158             pChild = findChildOfKind(childElement.getType());
159 
160             if (!pChild) {
161 
162                 serializingContext.setError("Unable to handle XML element: " +
163                                             childElement.getPath());
164 
165                 return false;
166             }
167 
168         } else {
169             // Child needs creation
170             pChild = createChild(childElement, serializingContext);
171 
172             if (!pChild) {
173 
174                 return false;
175             }
176         }
177 
178         // Dig
179         if (!pChild->fromXml(childElement, serializingContext)) {
180 
181             return false;
182         }
183     }
184 
185     return true;
186 }
187 
childrenToXml(CXmlElement & xmlElement,CXmlSerializingContext & serializingContext) const188 void CElement::childrenToXml(CXmlElement &xmlElement,
189                              CXmlSerializingContext &serializingContext) const
190 {
191     // Browse children and propagate
192     for (CElement *pChild : _childArray) {
193 
194         // Create corresponding child element
195         CXmlElement xmlChildElement;
196 
197         xmlElement.createChild(xmlChildElement, pChild->getXmlElementName());
198 
199         // Propagate
200         pChild->toXml(xmlChildElement, serializingContext);
201     }
202 }
203 
toXml(CXmlElement & xmlElement,CXmlSerializingContext & serializingContext) const204 void CElement::toXml(CXmlElement &xmlElement, CXmlSerializingContext &serializingContext) const
205 {
206     setXmlNameAttribute(xmlElement);
207     setXmlDescriptionAttribute(xmlElement);
208     childrenToXml(xmlElement, serializingContext);
209 }
210 
setXmlDescriptionAttribute(CXmlElement & xmlElement) const211 void CElement::setXmlDescriptionAttribute(CXmlElement &xmlElement) const
212 {
213     const string &description = getDescription();
214     if (!description.empty()) {
215         xmlElement.setAttribute(gDescriptionPropertyName, description);
216     }
217 }
218 
setXmlNameAttribute(CXmlElement & xmlElement) const219 void CElement::setXmlNameAttribute(CXmlElement &xmlElement) const
220 {
221     // By default, set Name attribute if any
222     string strName = getName();
223 
224     if (!strName.empty()) {
225 
226         xmlElement.setNameAttribute(strName);
227     }
228 }
229 
230 // Name
setName(const string & strName)231 void CElement::setName(const string &strName)
232 {
233     _strName = strName;
234 }
235 
getName() const236 const string &CElement::getName() const
237 {
238     return _strName;
239 }
240 
rename(const string & strName,string & strError)241 bool CElement::rename(const string &strName, string &strError)
242 {
243     // Check for conflict with brotherhood if relevant
244     if (_pParent && _pParent->childrenAreDynamic()) {
245 
246         for (CElement *pParentChild : _pParent->_childArray) {
247 
248             if (pParentChild != this && pParentChild->getName() == strName) {
249 
250                 // Conflict
251                 strError = "Name conflicts with brother element";
252 
253                 return false;
254             }
255         }
256     }
257     // Change name
258     setName(strName);
259 
260     return true;
261 }
262 
getPathName() const263 string CElement::getPathName() const
264 {
265     if (!_strName.empty()) {
266 
267         return _strName;
268     } else {
269 
270         return getKind();
271     }
272 }
273 
274 // Hierarchy
addChild(CElement * pChild)275 void CElement::addChild(CElement *pChild)
276 {
277     _childArray.push_back(pChild);
278 
279     pChild->_pParent = this;
280 }
281 
getChild(size_t index)282 CElement *CElement::getChild(size_t index)
283 {
284     assert(index <= _childArray.size());
285 
286     return _childArray[index];
287 }
288 
getChild(size_t index) const289 const CElement *CElement::getChild(size_t index) const
290 {
291     assert(index <= _childArray.size());
292 
293     return _childArray[index];
294 }
295 
createChild(const CXmlElement & childElement,CXmlSerializingContext & serializingContext)296 CElement *CElement::createChild(const CXmlElement &childElement,
297                                 CXmlSerializingContext &serializingContext)
298 {
299     // Context
300     CXmlElementSerializingContext &elementSerializingContext =
301         static_cast<CXmlElementSerializingContext &>(serializingContext);
302 
303     // Child needs creation
304     CElement *pChild = elementSerializingContext.getElementLibrary()->createElement(childElement);
305 
306     if (!pChild) {
307 
308         elementSerializingContext.setError("Unable to create XML element " +
309                                            childElement.getPath());
310 
311         return NULL;
312     }
313     // Store created child!
314     addChild(pChild);
315 
316     return pChild;
317 }
318 
removeChild(CElement * pChild)319 bool CElement::removeChild(CElement *pChild)
320 {
321     auto childIt = find(begin(_childArray), end(_childArray), pChild);
322     if (childIt != end(_childArray)) {
323 
324         _childArray.erase(childIt);
325         return true;
326     }
327     return false;
328 }
329 
listChildren(string & strChildList) const330 void CElement::listChildren(string &strChildList) const
331 {
332     // Get list of children names
333     for (CElement *pChild : _childArray) {
334 
335         strChildList += pChild->getName() + "\n";
336     }
337 }
338 
listQualifiedPaths(bool bDive,size_t level) const339 string CElement::listQualifiedPaths(bool bDive, size_t level) const
340 {
341     string strResult;
342 
343     // Dive Will cause only leaf nodes to be printed
344     if (!bDive || !getNbChildren()) {
345 
346         strResult = getQualifiedPath() + "\n";
347     }
348 
349     if (bDive || !level) {
350         // Get list of children paths
351         for (CElement *pChild : _childArray) {
352 
353             strResult += pChild->listQualifiedPaths(bDive, level + 1);
354         }
355     }
356     return strResult;
357 }
358 
listChildrenPaths(string & strChildList) const359 void CElement::listChildrenPaths(string &strChildList) const
360 {
361     // Get list of children paths
362     for (CElement *pChild : _childArray) {
363 
364         strChildList += pChild->getPath() + "\n";
365     }
366 }
367 
getNbChildren() const368 size_t CElement::getNbChildren() const
369 {
370     return _childArray.size();
371 }
372 
getParent() const373 const CElement *CElement::getParent() const
374 {
375     return _pParent;
376 }
377 
getParent()378 CElement *CElement::getParent()
379 {
380     return _pParent;
381 }
382 
clean()383 void CElement::clean()
384 {
385     if (childrenAreDynamic()) {
386 
387         removeChildren();
388     } else {
389         // Just propagate
390         for (CElement *pChild : _childArray) {
391 
392             pChild->clean();
393         }
394     }
395 }
396 
removeChildren()397 void CElement::removeChildren()
398 {
399     // Delete in reverse order
400     ChildArrayReverseIterator it;
401 
402     for (it = _childArray.rbegin(); it != _childArray.rend(); ++it) {
403 
404         delete *it;
405     }
406     _childArray.clear();
407 }
408 
findDescendant(CPathNavigator & pathNavigator) const409 const CElement *CElement::findDescendant(CPathNavigator &pathNavigator) const
410 {
411     string *pStrChildName = pathNavigator.next();
412 
413     if (!pStrChildName) {
414 
415         return this;
416     }
417 
418     const CElement *pChild = findChild(*pStrChildName);
419 
420     if (!pChild) {
421 
422         return NULL;
423     }
424 
425     return pChild->findDescendant(pathNavigator);
426 }
427 
findDescendant(CPathNavigator & pathNavigator)428 CElement *CElement::findDescendant(CPathNavigator &pathNavigator)
429 {
430     string *pStrChildName = pathNavigator.next();
431 
432     if (!pStrChildName) {
433 
434         return this;
435     }
436 
437     CElement *pChild = findChild(*pStrChildName);
438 
439     if (!pChild) {
440 
441         return NULL;
442     }
443 
444     return pChild->findDescendant(pathNavigator);
445 }
446 
isDescendantOf(const CElement * pCandidateAscendant) const447 bool CElement::isDescendantOf(const CElement *pCandidateAscendant) const
448 {
449     if (!_pParent) {
450 
451         return false;
452     }
453     if (_pParent == pCandidateAscendant) {
454 
455         return true;
456     }
457     return _pParent->isDescendantOf(pCandidateAscendant);
458 }
459 
findChild(const string & strName)460 CElement *CElement::findChild(const string &strName)
461 {
462     for (CElement *pChild : _childArray) {
463 
464         if (pChild->getPathName() == strName) {
465 
466             return pChild;
467         }
468     }
469 
470     return NULL;
471 }
472 
findChild(const string & strName) const473 const CElement *CElement::findChild(const string &strName) const
474 {
475     for (CElement *pChild : _childArray) {
476 
477         if (pChild->getPathName() == strName) {
478 
479             return pChild;
480         }
481     }
482 
483     return NULL;
484 }
485 
findChildOfKind(const string & strKind)486 CElement *CElement::findChildOfKind(const string &strKind)
487 {
488     for (CElement *pChild : _childArray) {
489 
490         if (pChild->getKind() == strKind) {
491 
492             return pChild;
493         }
494     }
495 
496     return NULL;
497 }
498 
findChildOfKind(const string & strKind) const499 const CElement *CElement::findChildOfKind(const string &strKind) const
500 {
501     for (CElement *pChild : _childArray) {
502 
503         if (pChild->getKind() == strKind) {
504 
505             return pChild;
506         }
507     }
508 
509     return NULL;
510 }
511 
getPath() const512 string CElement::getPath() const
513 {
514     // Take out root element from the path
515     if (_pParent && _pParent->_pParent) {
516 
517         return _pParent->getPath() + "/" + getPathName();
518     }
519     return "/" + getPathName();
520 }
521 
getQualifiedPath() const522 string CElement::getQualifiedPath() const
523 {
524     return getPath() + " [" + getKind() + "]";
525 }
526 
getXmlElementName() const527 string CElement::getXmlElementName() const
528 {
529     // Default to element kind
530     return getKind();
531 }
532