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 "ConfigurableElement.h"
31#include "MappingData.h"
32#include "SyncerSet.h"
33#include "ConfigurableDomain.h"
34#include "ConfigurationAccessContext.h"
35#include "ConfigurableElementAggregator.h"
36#include "AreaConfiguration.h"
37#include "Iterator.hpp"
38#include "Utility.h"
39#include "XmlParameterSerializingContext.h"
40#include <assert.h>
41
42#define base CElement
43
44CConfigurableElement::CConfigurableElement(const std::string &strName) : base(strName)
45{
46}
47
48bool CConfigurableElement::fromXml(const CXmlElement &xmlElement,
49                                   CXmlSerializingContext &serializingContext)
50{
51    auto &context = static_cast<CXmlParameterSerializingContext &>(serializingContext);
52    auto &accessContext = context.getAccessContext();
53
54    if (accessContext.serializeSettings()) {
55        // As serialization and deserialisation are handled through the *same* function
56        // the (de)serialize object can not be const in `serializeXmlSettings` signature.
57        // As a result a const_cast is unavoidable :(.
58        // Fixme: split serializeXmlSettings in two functions (in and out) to avoid the `const_cast`
59        return serializeXmlSettings(const_cast<CXmlElement &>(xmlElement),
60                                    static_cast<CConfigurationAccessContext &>(accessContext));
61    }
62    return structureFromXml(xmlElement, serializingContext);
63}
64
65void CConfigurableElement::toXml(CXmlElement &xmlElement,
66                                 CXmlSerializingContext &serializingContext) const
67{
68    auto &context = static_cast<CXmlParameterSerializingContext &>(serializingContext);
69    auto &accessContext = context.getAccessContext();
70    if (accessContext.serializeSettings()) {
71
72        serializeXmlSettings(xmlElement, static_cast<CConfigurationAccessContext &>(accessContext));
73    } else {
74
75        structureToXml(xmlElement, serializingContext);
76    }
77}
78
79// XML configuration settings parsing
80bool CConfigurableElement::serializeXmlSettings(
81    CXmlElement &xmlConfigurationSettingsElementContent,
82    CConfigurationAccessContext &configurationAccessContext) const
83{
84    size_t uiNbChildren = getNbChildren();
85
86    if (!configurationAccessContext.serializeOut()) {
87        // Just do basic checks and propagate to children
88        CXmlElement::CChildIterator it(xmlConfigurationSettingsElementContent);
89
90        CXmlElement xmlChildConfigurableElementSettingsElement;
91
92        // Propagate to children
93        for (size_t index = 0; index < uiNbChildren; index++) {
94
95            // Get child
96            const CConfigurableElement *pChildConfigurableElement =
97                static_cast<const CConfigurableElement *>(getChild(index));
98
99            if (!it.next(xmlChildConfigurableElementSettingsElement)) {
100
101                // Structure error
102                configurationAccessContext.setError(
103                    "Configuration settings parsing: missing child node " +
104                    pChildConfigurableElement->getXmlElementName() + " (name:" +
105                    pChildConfigurableElement->getName() + ") in " + getName());
106
107                return false;
108            }
109
110            // Check element type matches in type
111            if (xmlChildConfigurableElementSettingsElement.getType() !=
112                pChildConfigurableElement->getXmlElementName()) {
113
114                // "Component" tag has been renamed to "ParameterBlock", but retro-compatibility
115                // shall be ensured.
116                //
117                // So checking if this case occurs, i.e. element name is "ParameterBlock"
118                // but xml setting name is "Component".
119                bool compatibilityCase =
120                    (pChildConfigurableElement->getXmlElementName() == "ParameterBlock") &&
121                    (xmlChildConfigurableElementSettingsElement.getType() == "Component");
122
123                // Error if the compatibility case does not occur.
124                if (!compatibilityCase) {
125
126                    // Type error
127                    configurationAccessContext.setError(
128                        "Configuration settings parsing: Settings "
129                        "for configurable element " +
130                        pChildConfigurableElement->getQualifiedPath() +
131                        " does not match expected type: " +
132                        xmlChildConfigurableElementSettingsElement.getType() + " instead of " +
133                        pChildConfigurableElement->getKind());
134                    return false;
135                }
136            }
137
138            // Check element type matches in name
139            if (xmlChildConfigurableElementSettingsElement.getNameAttribute() !=
140                pChildConfigurableElement->getName()) {
141
142                // Name error
143                configurationAccessContext.setError(
144                    "Configuration settings parsing: Under configurable element " +
145                    getQualifiedPath() + ", expected element name " +
146                    pChildConfigurableElement->getName() + " but found " +
147                    xmlChildConfigurableElementSettingsElement.getNameAttribute() + " instead");
148
149                return false;
150            }
151
152            // Parse child configurable element's settings
153            if (!pChildConfigurableElement->serializeXmlSettings(
154                    xmlChildConfigurableElementSettingsElement, configurationAccessContext)) {
155
156                return false;
157            }
158        }
159        // There should remain no configurable element to parse
160        if (it.next(xmlChildConfigurableElementSettingsElement)) {
161
162            // Structure error
163            configurationAccessContext.setError(
164                "Configuration settings parsing: Unexpected xml element node " +
165                xmlChildConfigurableElementSettingsElement.getType() + " in " + getQualifiedPath());
166
167            return false;
168        }
169    } else {
170        // Handle element name attribute
171        xmlConfigurationSettingsElementContent.setNameAttribute(getName());
172
173        // Propagate to children
174        for (size_t index = 0; index < uiNbChildren; index++) {
175
176            const CConfigurableElement *pChildConfigurableElement =
177                static_cast<const CConfigurableElement *>(getChild(index));
178
179            // Create corresponding child element
180            CXmlElement xmlChildConfigurableElementSettingsElement;
181
182            xmlConfigurationSettingsElementContent.createChild(
183                xmlChildConfigurableElementSettingsElement,
184                pChildConfigurableElement->getXmlElementName());
185
186            // Propagate
187            pChildConfigurableElement->serializeXmlSettings(
188                xmlChildConfigurableElementSettingsElement, configurationAccessContext);
189        }
190    }
191    // Done
192    return true;
193}
194
195// AreaConfiguration creation
196CAreaConfiguration *CConfigurableElement::createAreaConfiguration(
197    const CSyncerSet *pSyncerSet) const
198{
199    return new CAreaConfiguration(this, pSyncerSet);
200}
201
202// Parameter access
203bool CConfigurableElement::accessValue(CPathNavigator &pathNavigator, std::string &strValue,
204                                       bool bSet,
205                                       CParameterAccessContext &parameterAccessContext) const
206{
207    std::string *pStrChildName = pathNavigator.next();
208
209    if (!pStrChildName) {
210
211        parameterAccessContext.setError((bSet ? "Can't set " : "Can't get ") +
212                                        pathNavigator.getCurrentPath() +
213                                        " because it is not a parameter");
214
215        return false;
216    }
217
218    const CConfigurableElement *pChild =
219        static_cast<const CConfigurableElement *>(findChild(*pStrChildName));
220
221    if (!pChild) {
222
223        parameterAccessContext.setError("Path not found: " + pathNavigator.getCurrentPath());
224
225        return false;
226    }
227
228    return pChild->accessValue(pathNavigator, strValue, bSet, parameterAccessContext);
229}
230
231// Whole element access
232void CConfigurableElement::getSettingsAsBytes(std::vector<uint8_t> &bytes,
233                                              CParameterAccessContext &parameterAccessContext) const
234{
235    bytes.resize(getFootPrint());
236
237    parameterAccessContext.getParameterBlackboard()->readBytes(
238        bytes, getOffset() - parameterAccessContext.getBaseOffset());
239}
240
241bool CConfigurableElement::setSettingsAsBytes(const std::vector<uint8_t> &bytes,
242                                              CParameterAccessContext &parameterAccessContext) const
243{
244    CParameterBlackboard *pParameterBlackboard = parameterAccessContext.getParameterBlackboard();
245
246    // Size
247    size_t size = getFootPrint();
248
249    // Check sizes match
250    if (size != bytes.size()) {
251
252        parameterAccessContext.setError(std::string("Wrong size: Expected: ") +
253                                        std::to_string(size) + " Provided: " +
254                                        std::to_string(bytes.size()));
255
256        return false;
257    }
258
259    // Write bytes
260    pParameterBlackboard->writeBytes(bytes, getOffset() - parameterAccessContext.getBaseOffset());
261
262    if (not parameterAccessContext.getAutoSync()) {
263        // Auto sync is not activated, sync will be defered until an explicit request
264        return true;
265    }
266
267    CSyncerSet syncerSet;
268    fillSyncerSet(syncerSet);
269    core::Results res;
270    if (not syncerSet.sync(*parameterAccessContext.getParameterBlackboard(), false, &res)) {
271
272        parameterAccessContext.setError(utility::asString(res));
273        return false;
274    }
275    return true;
276}
277
278std::list<const CConfigurableElement *> CConfigurableElement::getConfigurableElementContext() const
279{
280    std::list<const CConfigurableElement *> configurableElementPath;
281
282    const CElement *element = this;
283    while (element != nullptr and isOfConfigurableElementType(element)) {
284        auto configurableElement = static_cast<const CConfigurableElement *>(element);
285
286        configurableElementPath.push_back(configurableElement);
287        element = element->getParent();
288    }
289
290    return configurableElementPath;
291}
292
293// Used for simulation and virtual subsystems
294void CConfigurableElement::setDefaultValues(CParameterAccessContext &parameterAccessContext) const
295{
296    // Propagate to children
297    size_t uiNbChildren = getNbChildren();
298
299    for (size_t index = 0; index < uiNbChildren; index++) {
300
301        const CConfigurableElement *pConfigurableElement =
302            static_cast<const CConfigurableElement *>(getChild(index));
303
304        pConfigurableElement->setDefaultValues(parameterAccessContext);
305    }
306}
307
308// Element properties
309void CConfigurableElement::showProperties(std::string &strResult) const
310{
311    base::showProperties(strResult);
312
313    strResult += "Total size: " + getFootprintAsString() + "\n";
314}
315
316std::string CConfigurableElement::logValue(utility::ErrorContext &context) const
317{
318    return logValue(static_cast<CParameterAccessContext &>(context));
319}
320
321std::string CConfigurableElement::logValue(CParameterAccessContext & /*ctx*/) const
322{
323    // By default, an element does not have a value. Only leaf elements have
324    // one. This method could be pure virtual but then, several derived classes
325    // would need to implement it in order to simply return an empty string.
326    return "";
327}
328
329// Offset
330void CConfigurableElement::setOffset(size_t offset)
331{
332    // Assign offset locally
333    _offset = offset;
334
335    // Propagate to children
336    size_t uiNbChildren = getNbChildren();
337
338    for (size_t index = 0; index < uiNbChildren; index++) {
339
340        CConfigurableElement *pConfigurableElement =
341            static_cast<CConfigurableElement *>(getChild(index));
342
343        pConfigurableElement->setOffset(offset);
344
345        offset += pConfigurableElement->getFootPrint();
346    }
347}
348
349size_t CConfigurableElement::getOffset() const
350{
351    return _offset;
352}
353
354// Memory
355size_t CConfigurableElement::getFootPrint() const
356{
357    size_t uiSize = 0;
358    size_t uiNbChildren = getNbChildren();
359
360    for (size_t index = 0; index < uiNbChildren; index++) {
361
362        const CConfigurableElement *pConfigurableElement =
363            static_cast<const CConfigurableElement *>(getChild(index));
364
365        uiSize += pConfigurableElement->getFootPrint();
366    }
367
368    return uiSize;
369}
370
371// Browse parent path to find syncer
372ISyncer *CConfigurableElement::getSyncer() const
373{
374    // Check parent
375    const CElement *pParent = getParent();
376
377    if (isOfConfigurableElementType(pParent)) {
378
379        return static_cast<const CConfigurableElement *>(pParent)->getSyncer();
380    }
381    return NULL;
382}
383
384// Syncer set (me, ascendant or descendant ones)
385void CConfigurableElement::fillSyncerSet(CSyncerSet &syncerSet) const
386{
387    //  Try me or ascendants
388    ISyncer *pMineOrAscendantSyncer = getSyncer();
389
390    if (pMineOrAscendantSyncer) {
391
392        // Provide found syncer object
393        syncerSet += pMineOrAscendantSyncer;
394
395        // Done
396        return;
397    }
398    // Fetch descendant ones
399    fillSyncerSetFromDescendant(syncerSet);
400}
401
402// Syncer set (descendant)
403void CConfigurableElement::fillSyncerSetFromDescendant(CSyncerSet &syncerSet) const
404{
405    // Dig
406    size_t uiNbChildren = getNbChildren();
407
408    for (size_t index = 0; index < uiNbChildren; index++) {
409
410        const CConfigurableElement *pConfigurableElement =
411            static_cast<const CConfigurableElement *>(getChild(index));
412
413        pConfigurableElement->fillSyncerSetFromDescendant(syncerSet);
414    }
415}
416
417// Configurable domain association
418void CConfigurableElement::addAttachedConfigurableDomain(
419    const CConfigurableDomain *pConfigurableDomain)
420{
421    _configurableDomainList.push_back(pConfigurableDomain);
422}
423
424void CConfigurableElement::removeAttachedConfigurableDomain(
425    const CConfigurableDomain *pConfigurableDomain)
426{
427    _configurableDomainList.remove(pConfigurableDomain);
428}
429
430// Belonging domain
431bool CConfigurableElement::belongsTo(const CConfigurableDomain *pConfigurableDomain) const
432{
433    if (containsConfigurableDomain(pConfigurableDomain)) {
434
435        return true;
436    }
437    return belongsToDomainAscending(pConfigurableDomain);
438}
439
440// Belonging domains
441void CConfigurableElement::getBelongingDomains(
442    std::list<const CConfigurableDomain *> &configurableDomainList) const
443{
444    configurableDomainList.insert(configurableDomainList.end(), _configurableDomainList.begin(),
445                                  _configurableDomainList.end());
446
447    // Check parent
448    const CElement *pParent = getParent();
449
450    if (isOfConfigurableElementType(pParent)) {
451
452        static_cast<const CConfigurableElement *>(pParent)->getBelongingDomains(
453            configurableDomainList);
454    }
455}
456
457void CConfigurableElement::listBelongingDomains(std::string &strResult, bool bVertical) const
458{
459    // Get belonging domain list
460    std::list<const CConfigurableDomain *> configurableDomainList;
461
462    getBelongingDomains(configurableDomainList);
463
464    // Fill list
465    listDomains(configurableDomainList, strResult, bVertical);
466}
467
468// Elements with no domains
469void CConfigurableElement::listRogueElements(std::string &strResult) const
470{
471    // Get rogue element aggregate list (no associated domain)
472    std::list<const CConfigurableElement *> rogueElementList;
473
474    CConfigurableElementAggregator configurableElementAggregator(
475        rogueElementList, &CConfigurableElement::hasNoDomainAssociated);
476
477    configurableElementAggregator.aggegate(this);
478
479    // Build list as std::string
480    std::list<const CConfigurableElement *>::const_iterator it;
481
482    for (it = rogueElementList.begin(); it != rogueElementList.end(); ++it) {
483
484        const CConfigurableElement *pConfigurableElement = *it;
485
486        strResult += pConfigurableElement->getPath() + "\n";
487    }
488}
489
490bool CConfigurableElement::isRogue() const
491{
492    // Check not belonging to any domin from current level and towards ascendents
493    if (getBelongingDomainCount() != 0) {
494
495        return false;
496    }
497
498    // Get a list of elements (current + descendants) with no domains associated
499    std::list<const CConfigurableElement *> rogueElementList;
500
501    CConfigurableElementAggregator agregator(rogueElementList,
502                                             &CConfigurableElement::hasNoDomainAssociated);
503
504    agregator.aggegate(this);
505
506    // Check only one element found which ought to be current one
507    return (rogueElementList.size() == 1) && (rogueElementList.front() == this);
508}
509
510// Footprint as string
511std::string CConfigurableElement::getFootprintAsString() const
512{
513    // Get size as string
514    return std::to_string(getFootPrint()) + " byte(s)";
515}
516
517// Matching check for no domain association
518bool CConfigurableElement::hasNoDomainAssociated() const
519{
520    return _configurableDomainList.empty();
521}
522
523// Matching check for no valid associated domains
524bool CConfigurableElement::hasNoValidDomainAssociated() const
525{
526    if (_configurableDomainList.empty()) {
527
528        // No domains associated
529        return true;
530    }
531
532    ConfigurableDomainListConstIterator it;
533
534    // Browse all configurable domains for validity checking
535    for (it = _configurableDomainList.begin(); it != _configurableDomainList.end(); ++it) {
536
537        const CConfigurableDomain *pConfigurableDomain = *it;
538
539        if (pConfigurableDomain->isApplicableConfigurationValid(this)) {
540
541            return false;
542        }
543    }
544
545    return true;
546}
547
548// Owning domains
549void CConfigurableElement::listAssociatedDomains(std::string &strResult, bool bVertical) const
550{
551    // Fill list
552    listDomains(_configurableDomainList, strResult, bVertical);
553}
554
555size_t CConfigurableElement::getBelongingDomainCount() const
556{
557    // Get belonging domain list
558    std::list<const CConfigurableDomain *> configurableDomainList;
559
560    getBelongingDomains(configurableDomainList);
561
562    return configurableDomainList.size();
563}
564
565void CConfigurableElement::listDomains(
566    const std::list<const CConfigurableDomain *> &configurableDomainList, std::string &strResult,
567    bool bVertical) const
568{
569    // Fill list
570    ConfigurableDomainListConstIterator it;
571    bool bFirst = true;
572
573    // Browse all configurable domains for comparison
574    for (it = configurableDomainList.begin(); it != configurableDomainList.end(); ++it) {
575
576        const CConfigurableDomain *pConfigurableDomain = *it;
577
578        if (!bVertical && !bFirst) {
579
580            strResult += ", ";
581        }
582
583        strResult += pConfigurableDomain->getName();
584
585        if (bVertical) {
586
587            strResult += "\n";
588        } else {
589
590            bFirst = false;
591        }
592    }
593}
594
595bool CConfigurableElement::containsConfigurableDomain(
596    const CConfigurableDomain *pConfigurableDomain) const
597{
598    ConfigurableDomainListConstIterator it;
599
600    // Browse all configurable domains for comparison
601    for (it = _configurableDomainList.begin(); it != _configurableDomainList.end(); ++it) {
602
603        if (pConfigurableDomain == *it) {
604
605            return true;
606        }
607    }
608    return false;
609}
610
611// Belonging domain ascending search
612bool CConfigurableElement::belongsToDomainAscending(
613    const CConfigurableDomain *pConfigurableDomain) const
614{
615    // Check parent
616    const CElement *pParent = getParent();
617
618    if (isOfConfigurableElementType(pParent)) {
619
620        return static_cast<const CConfigurableElement *>(pParent)->belongsTo(pConfigurableDomain);
621    }
622    return false;
623}
624
625// Belonging subsystem
626const CSubsystem *CConfigurableElement::getBelongingSubsystem() const
627{
628    const CElement *pParent = getParent();
629
630    // Stop at system class
631    if (!pParent->getParent()) {
632
633        return NULL;
634    }
635
636    return static_cast<const CConfigurableElement *>(pParent)->getBelongingSubsystem();
637}
638
639// Check element is a parameter
640bool CConfigurableElement::isParameter() const
641{
642    return false;
643}
644
645// Check parent is still of current type (by structure knowledge)
646bool CConfigurableElement::isOfConfigurableElementType(const CElement *pParent) const
647{
648    assert(pParent);
649
650    // Up to system class
651    return !!pParent->getParent();
652}
653