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 "DomainConfiguration.h"
31#include "ConfigurableElement.h"
32#include "CompoundRule.h"
33#include "Subsystem.h"
34#include "XmlDomainSerializingContext.h"
35#include "XmlDomainImportContext.h"
36#include "XmlDomainExportContext.h"
37#include "ConfigurationAccessContext.h"
38#include "AlwaysAssert.hpp"
39#include <assert.h>
40#include <cstdlib>
41#include <algorithm>
42#include <numeric>
43#include "RuleParser.h"
44
45#define base CElement
46
47using std::string;
48
49CDomainConfiguration::CDomainConfiguration(const string &strName) : base(strName)
50{
51}
52
53// Class kind
54string CDomainConfiguration::getKind() const
55{
56    return "Configuration";
57}
58
59// Child dynamic creation
60bool CDomainConfiguration::childrenAreDynamic() const
61{
62    return true;
63}
64
65// XML configuration settings parsing
66bool CDomainConfiguration::parseSettings(CXmlElement &xmlConfigurationSettingsElement,
67                                         CXmlDomainImportContext &context)
68{
69    // Parse configurable element's configuration settings
70    CXmlElement::CChildIterator it(xmlConfigurationSettingsElement);
71
72    CXmlElement xmlConfigurableElementSettingsElement;
73    auto insertLocation = begin(mAreaConfigurationList);
74
75    while (it.next(xmlConfigurableElementSettingsElement)) {
76
77        // Retrieve area configuration
78        string configurableElementPath;
79        xmlConfigurableElementSettingsElement.getAttribute("Path", configurableElementPath);
80
81        auto areaConfiguration = findAreaConfigurationByPath(configurableElementPath);
82        if (areaConfiguration == end(mAreaConfigurationList)) {
83
84            context.setError("Configurable Element " + configurableElementPath +
85                             " referred to by Configuration " + getPath() +
86                             " not associated to Domain");
87
88            return false;
89        }
90        // Parse
91        if (!importOneConfigurableElementSettings(areaConfiguration->get(),
92                                                  xmlConfigurableElementSettingsElement, context)) {
93
94            return false;
95        }
96        // Take into account the new configuration order by moving the configuration associated to
97        // the element to the n-th position of the configuration list.
98        // It will result in prepending to the configuration list wit the configuration of all
99        // elements found in XML, keeping the order of the processing of the XML file.
100        mAreaConfigurationList.splice(insertLocation, mAreaConfigurationList, areaConfiguration);
101        // areaConfiguration is still valid, but now refer to the reorderer list
102        insertLocation = std::next(areaConfiguration);
103    }
104    return true;
105}
106
107// XML configuration settings composing
108void CDomainConfiguration::composeSettings(CXmlElement &xmlConfigurationSettingsElement,
109                                           CXmlDomainExportContext &context) const
110{
111    // Go through all are configurations
112    for (auto &areaConfiguration : mAreaConfigurationList) {
113
114        // Retrieve configurable element
115        const CConfigurableElement *pConfigurableElement =
116            areaConfiguration->getConfigurableElement();
117
118        // Create configurable element child element
119        CXmlElement xmlConfigurableElementSettingsElement;
120
121        xmlConfigurationSettingsElement.createChild(xmlConfigurableElementSettingsElement,
122                                                    "ConfigurableElement");
123
124        // Set Path attribute
125        xmlConfigurableElementSettingsElement.setAttribute("Path", pConfigurableElement->getPath());
126
127        // Delegate composing to area configuration
128        exportOneConfigurableElementSettings(areaConfiguration.get(),
129                                             xmlConfigurableElementSettingsElement, context);
130    }
131}
132
133// Serialize one configuration for one configurable element
134bool CDomainConfiguration::importOneConfigurableElementSettings(
135    CAreaConfiguration *areaConfiguration, CXmlElement &xmlConfigurableElementSettingsElement,
136    CXmlDomainImportContext &context)
137{
138    const CConfigurableElement *destination = areaConfiguration->getConfigurableElement();
139
140    // Check structure
141    if (xmlConfigurableElementSettingsElement.getNbChildElements() != 1) {
142
143        // Structure error
144        context.setError("Struture error encountered while parsing settings of " +
145                         destination->getKind() + " " + destination->getName() +
146                         " in Configuration " + getPath());
147
148        return false;
149    }
150
151    // Element content
152    CXmlElement xmlConfigurableElementSettingsElementContent;
153    // Check name and kind
154    if (!xmlConfigurableElementSettingsElement.getChildElement(
155            destination->getXmlElementName(), destination->getName(),
156            xmlConfigurableElementSettingsElementContent)) {
157
158        // "Component" tag has been renamed to "ParameterBlock", but retro-compatibility shall
159        // be ensured.
160        //
161        // So checking if this case occurs, i.e. element name is "ParameterBlock"
162        // but found xml setting name is "Component".
163        bool compatibilityCase =
164            (destination->getXmlElementName() == "ParameterBlock") &&
165            xmlConfigurableElementSettingsElement.getChildElement(
166                "Component", destination->getName(), xmlConfigurableElementSettingsElementContent);
167
168        // Error if the compatibility case does not occur.
169        if (!compatibilityCase) {
170            context.setError("Couldn't find settings for " + destination->getXmlElementName() +
171                             " " + destination->getName() + " for Configuration " + getPath());
172
173            return false;
174        }
175    }
176
177    // Create configuration access context
178    string error;
179    CConfigurationAccessContext configurationAccessContext(error, false);
180
181    // Have domain configuration parse settings for configurable element
182    bool success = areaConfiguration->serializeXmlSettings(
183        xmlConfigurableElementSettingsElementContent, configurationAccessContext);
184
185    context.appendToError(error);
186    return success;
187}
188
189bool CDomainConfiguration::exportOneConfigurableElementSettings(
190    CAreaConfiguration *areaConfiguration, CXmlElement &xmlConfigurableElementSettingsElement,
191    CXmlDomainExportContext &context) const
192{
193    const CConfigurableElement *source = areaConfiguration->getConfigurableElement();
194
195    // Create child XML element
196    CXmlElement xmlConfigurableElementSettingsElementContent;
197    xmlConfigurableElementSettingsElement.createChild(xmlConfigurableElementSettingsElementContent,
198                                                      source->getXmlElementName());
199
200    // Create configuration access context
201    string error;
202    CConfigurationAccessContext configurationAccessContext(error, true);
203    configurationAccessContext.setValueSpaceRaw(context.valueSpaceIsRaw());
204    configurationAccessContext.setOutputRawFormat(context.outputRawFormatIsHex());
205
206    // Have domain configuration parse settings for configurable element
207    bool success = areaConfiguration->serializeXmlSettings(
208        xmlConfigurableElementSettingsElementContent, configurationAccessContext);
209
210    context.appendToError(error);
211    return success;
212}
213
214void CDomainConfiguration::addConfigurableElement(const CConfigurableElement *configurableElement,
215                                                  const CSyncerSet *syncerSet)
216{
217    mAreaConfigurationList.emplace_back(configurableElement->createAreaConfiguration(syncerSet));
218}
219
220void CDomainConfiguration::removeConfigurableElement(
221    const CConfigurableElement *pConfigurableElement)
222{
223    auto &areaConfigurationToRemove = getAreaConfiguration(pConfigurableElement);
224
225    mAreaConfigurationList.remove(areaConfigurationToRemove);
226}
227
228bool CDomainConfiguration::setElementSequence(const std::vector<string> &newElementSequence,
229                                              string &error)
230{
231    std::vector<string> elementSequenceSet;
232    auto insertLocation = begin(mAreaConfigurationList);
233
234    for (const std::string &elementPath : newElementSequence) {
235
236        auto areaConfiguration = findAreaConfigurationByPath(elementPath);
237        if (areaConfiguration == end(mAreaConfigurationList)) {
238
239            error = "Element " + elementPath + " not found in domain";
240
241            return false;
242        }
243        auto it = find(begin(elementSequenceSet), end(elementSequenceSet), elementPath);
244        if (it != end(elementSequenceSet)) {
245            error = "Element " + elementPath + " provided more than once";
246            return false;
247        }
248        elementSequenceSet.push_back(elementPath);
249        // Take into account the new configuration order by moving the configuration associated to
250        // the element to the n-th position of the configuration list.
251        // It will result in prepending to the configuration list wit the configuration of all
252        // elements found in XML, keeping the order of the processing of the XML file.
253        mAreaConfigurationList.splice(insertLocation, mAreaConfigurationList, areaConfiguration);
254        // areaConfiguration is still valid, but now refer to the reorderer list
255        insertLocation = std::next(areaConfiguration);
256    }
257    return true;
258}
259
260void CDomainConfiguration::getElementSequence(string &strResult) const
261{
262    // List configurable element paths out of ordered area configuration list
263    strResult = accumulate(begin(mAreaConfigurationList), end(mAreaConfigurationList), string("\n"),
264                           [](const string &a, const AreaConfiguration &conf) {
265                               return a + conf->getConfigurableElement()->getPath() + "\n";
266                           });
267}
268
269// Application rule
270bool CDomainConfiguration::setApplicationRule(
271    const string &strApplicationRule,
272    const CSelectionCriteriaDefinition *pSelectionCriteriaDefinition, string &strError)
273{
274    // Parser
275    CRuleParser ruleParser(strApplicationRule, pSelectionCriteriaDefinition);
276
277    // Attempt to parse it
278    if (!ruleParser.parse(NULL, strError)) {
279
280        return false;
281    }
282    // Replace compound rule
283    setRule(ruleParser.grabRootRule());
284
285    return true;
286}
287
288void CDomainConfiguration::clearApplicationRule()
289{
290    // Replace compound rule
291    setRule(NULL);
292}
293
294string CDomainConfiguration::getApplicationRule() const
295{
296    const CCompoundRule *pRule = getRule();
297    return pRule ? pRule->dump() : "<none>";
298}
299
300/**
301 * Get the Configuration Blackboard.
302 *
303 * Fetch the Configuration Blackboard related to the ConfigurableElement given in parameter. This
304 * Element is used to retrieve the correct AreaConfiguration where the Blackboard is stored.
305 *
306 * @param[in] pConfigurableElement      A pointer to a ConfigurableElement that is part of the
307 *                                      Domain. This must have been checked previously, as an
308 *                                      assertion is performed.
309 *
310 * return Pointer to the Blackboard of the Configuration.
311 */
312CParameterBlackboard *CDomainConfiguration::getBlackboard(
313    const CConfigurableElement *pConfigurableElement) const
314{
315    const auto &it = find_if(begin(mAreaConfigurationList), end(mAreaConfigurationList),
316                             [&](const AreaConfiguration &conf) {
317                                 return conf != nullptr &&
318                                        conf->getConfigurableElement() == pConfigurableElement;
319                             });
320    ALWAYS_ASSERT(it != end(mAreaConfigurationList), "Configurable Element "
321                                                         << pConfigurableElement->getName()
322                                                         << " not found in any area Configuration");
323    return &(*it)->getBlackboard();
324}
325
326// Save data from current
327void CDomainConfiguration::save(const CParameterBlackboard *pMainBlackboard)
328{
329    // Just propagate to areas
330    for (auto &areaConfiguration : mAreaConfigurationList) {
331        areaConfiguration->save(pMainBlackboard);
332    }
333}
334
335// Apply data to current
336bool CDomainConfiguration::restore(CParameterBlackboard *pMainBlackboard, bool bSync,
337                                   core::Results *errors) const
338{
339    return std::accumulate(begin(mAreaConfigurationList), end(mAreaConfigurationList), true,
340                           [&](bool accumulator, const AreaConfiguration &conf) {
341                               return conf->restore(pMainBlackboard, bSync, errors) && accumulator;
342                           });
343}
344
345// Ensure validity for configurable element area configuration
346void CDomainConfiguration::validate(const CConfigurableElement *pConfigurableElement,
347                                    const CParameterBlackboard *pMainBlackboard)
348{
349    auto &areaConfigurationToValidate = getAreaConfiguration(pConfigurableElement);
350
351    // Delegate
352    areaConfigurationToValidate->validate(pMainBlackboard);
353}
354
355// Ensure validity of all area configurations
356void CDomainConfiguration::validate(const CParameterBlackboard *pMainBlackboard)
357{
358    for (auto &areaConfiguration : mAreaConfigurationList) {
359        areaConfiguration->validate(pMainBlackboard);
360    }
361}
362
363// Return configuration validity for given configurable element
364bool CDomainConfiguration::isValid(const CConfigurableElement *pConfigurableElement) const
365{
366    // Get child configurable elemnt's area configuration
367    auto &areaConfiguration = getAreaConfiguration(pConfigurableElement);
368
369    ALWAYS_ASSERT(areaConfiguration != nullptr, "Configurable Element "
370                                                    << pConfigurableElement->getName()
371                                                    << " not found in any area Configuration");
372
373    return areaConfiguration->isValid();
374}
375
376// Ensure validity of configurable element's area configuration by copying in from a valid one
377void CDomainConfiguration::validateAgainst(const CDomainConfiguration *pValidDomainConfiguration,
378                                           const CConfigurableElement *pConfigurableElement)
379{
380    // Retrieve related area configurations
381    auto &areaConfigurationToValidate = getAreaConfiguration(pConfigurableElement);
382    const auto &areaConfigurationToValidateAgainst =
383        pValidDomainConfiguration->getAreaConfiguration(pConfigurableElement);
384
385    // Delegate to area
386    areaConfigurationToValidate->validateAgainst(areaConfigurationToValidateAgainst.get());
387}
388
389void CDomainConfiguration::validateAgainst(const CDomainConfiguration *validDomainConfiguration)
390{
391    ALWAYS_ASSERT(mAreaConfigurationList.size() ==
392                      validDomainConfiguration->mAreaConfigurationList.size(),
393                  "Cannot validate domain configuration "
394                      << getPath() << " since area configuration list does not have the same size"
395                                      "than the configuration list to check against");
396    for (const auto &configurationToValidateAgainst :
397         validDomainConfiguration->mAreaConfigurationList) {
398        // Get the area configuration associated to the configurable element of the
399        // valid area configuration, it will assert if none found.
400        auto configurableElement = configurationToValidateAgainst->getConfigurableElement();
401        auto &configurationToValidate = getAreaConfiguration(configurableElement);
402        // Delegate to area
403        configurationToValidate->validateAgainst(configurationToValidateAgainst.get());
404    }
405}
406
407// Dynamic data application
408bool CDomainConfiguration::isApplicable() const
409{
410    const CCompoundRule *pRule = getRule();
411
412    return pRule && pRule->matches();
413}
414
415// Merge existing configurations to given configurable element ones
416void CDomainConfiguration::merge(CConfigurableElement *pToConfigurableElement,
417                                 CConfigurableElement *pFromConfigurableElement)
418{
419    // Retrieve related area configurations
420    auto &areaConfigurationToMergeTo = getAreaConfiguration(pToConfigurableElement);
421    const auto &areaConfigurationToMergeFrom = getAreaConfiguration(pFromConfigurableElement);
422
423    // Do the merge
424    areaConfigurationToMergeFrom->copyToOuter(areaConfigurationToMergeTo.get());
425}
426
427// Domain splitting
428void CDomainConfiguration::split(CConfigurableElement *pFromConfigurableElement)
429{
430    // Retrieve related area configuration
431    const auto &areaConfigurationToSplitFrom = getAreaConfiguration(pFromConfigurableElement);
432
433    // Go through children areas to copy configuration data to them
434    size_t uiNbConfigurableElementChildren = pFromConfigurableElement->getNbChildren();
435    size_t uiChild;
436
437    for (uiChild = 0; uiChild < uiNbConfigurableElementChildren; uiChild++) {
438
439        CConfigurableElement *pToChildConfigurableElement =
440            static_cast<CConfigurableElement *>(pFromConfigurableElement->getChild(uiChild));
441
442        // Get child configurable elemnt's area configuration
443        auto &childAreaConfiguration = getAreaConfiguration(pToChildConfigurableElement);
444
445        // Do the copy
446        childAreaConfiguration->copyFromOuter(areaConfigurationToSplitFrom.get());
447    }
448}
449
450const CDomainConfiguration::AreaConfiguration &CDomainConfiguration::getAreaConfiguration(
451    const CConfigurableElement *pConfigurableElement) const
452{
453    const auto &it = find_if(begin(mAreaConfigurationList), end(mAreaConfigurationList),
454                             [&](const AreaConfiguration &conf) {
455                                 return conf->getConfigurableElement() == pConfigurableElement;
456                             });
457    ALWAYS_ASSERT(it != end(mAreaConfigurationList),
458                  "Configurable Element " << pConfigurableElement->getName()
459                                          << " not found in Domain Configuration list");
460    return *it;
461}
462
463CDomainConfiguration::AreaConfigurations::iterator CDomainConfiguration::
464    findAreaConfigurationByPath(const std::string &configurableElementPath)
465{
466    auto areaConfiguration =
467        find_if(begin(mAreaConfigurationList), end(mAreaConfigurationList),
468                [&](const AreaConfiguration &conf) {
469                    return conf->getConfigurableElement()->getPath() == configurableElementPath;
470                });
471    return areaConfiguration;
472}
473
474// Rule
475const CCompoundRule *CDomainConfiguration::getRule() const
476{
477    if (getNbChildren()) {
478        // Rule created
479        return static_cast<const CCompoundRule *>(getChild(ECompoundRule));
480    }
481    return NULL;
482}
483
484CCompoundRule *CDomainConfiguration::getRule()
485{
486    if (getNbChildren()) {
487        // Rule created
488        return static_cast<CCompoundRule *>(getChild(ECompoundRule));
489    }
490    return NULL;
491}
492
493void CDomainConfiguration::setRule(CCompoundRule *pRule)
494{
495    CCompoundRule *pOldRule = getRule();
496
497    if (pOldRule) {
498        // Remove previous rule
499        removeChild(pOldRule);
500
501        delete pOldRule;
502    }
503
504    // Set new one
505    if (pRule) {
506        // Chain
507        addChild(pRule);
508    }
509}
510