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 "ConfigurableDomain.h"
31#include "DomainConfiguration.h"
32#include "ConfigurableElement.h"
33#include "ConfigurationAccessContext.h"
34#include "XmlDomainSerializingContext.h"
35#include "XmlDomainImportContext.h"
36#include "XmlDomainExportContext.h"
37#include "Utility.h"
38#include "AlwaysAssert.hpp"
39#include <cassert>
40
41#define base CElement
42
43using std::string;
44
45CConfigurableDomain::CConfigurableDomain(const string &strName) : base(strName)
46{
47}
48
49CConfigurableDomain::~CConfigurableDomain()
50{
51    // Remove all configurable elements
52    ConfigurableElementListIterator it;
53
54    for (it = _configurableElementList.begin(); it != _configurableElementList.end(); ++it) {
55
56        CConfigurableElement *pConfigurableElement = *it;
57
58        // Remove from configurable element
59        pConfigurableElement->removeAttachedConfigurableDomain(this);
60    }
61
62    // Remove all associated syncer sets
63    ConfigurableElementToSyncerSetMapIterator mapIt;
64
65    for (mapIt = _configurableElementToSyncerSetMap.begin();
66         mapIt != _configurableElementToSyncerSetMap.end(); ++mapIt) {
67
68        delete mapIt->second;
69    }
70}
71
72string CConfigurableDomain::getKind() const
73{
74    return "ConfigurableDomain";
75}
76
77bool CConfigurableDomain::childrenAreDynamic() const
78{
79    return true;
80}
81
82// Content dumping
83string CConfigurableDomain::logValue(utility::ErrorContext & /*ctx*/) const
84{
85    return string("{") +
86
87           "Sequence aware: " + (_bSequenceAware ? "yes" : "no") +
88
89           ", Last applied configuration: " +
90           (_pLastAppliedConfiguration ? _pLastAppliedConfiguration->getName() : "<none>") +
91
92           "}";
93}
94
95// Sequence awareness
96void CConfigurableDomain::setSequenceAwareness(bool bSequenceAware)
97{
98    if (_bSequenceAware != bSequenceAware) {
99
100        _bSequenceAware = bSequenceAware;
101    }
102}
103
104bool CConfigurableDomain::getSequenceAwareness() const
105{
106    return _bSequenceAware;
107}
108
109// From IXmlSource
110void CConfigurableDomain::toXml(CXmlElement &xmlElement,
111                                CXmlSerializingContext &serializingContext) const
112{
113    base::toXml(xmlElement, serializingContext);
114
115    // Sequence awareness
116    xmlElement.setAttribute("SequenceAware", _bSequenceAware);
117}
118
119void CConfigurableDomain::childrenToXml(CXmlElement &xmlElement,
120                                        CXmlSerializingContext &serializingContext) const
121{
122    // Configurations
123    composeDomainConfigurations(xmlElement, serializingContext);
124
125    // Configurable Elements
126    composeConfigurableElements(xmlElement);
127
128    // Settings
129    composeSettings(xmlElement, static_cast<CXmlDomainExportContext &>(serializingContext));
130}
131
132// XML composing
133void CConfigurableDomain::composeDomainConfigurations(
134    CXmlElement &xmlElement, CXmlSerializingContext &serializingContext) const
135{
136    // Create Configurations element
137    CXmlElement xmlConfigurationsElement;
138
139    xmlElement.createChild(xmlConfigurationsElement, "Configurations");
140
141    // Delegate to base
142    base::childrenToXml(xmlConfigurationsElement, serializingContext);
143}
144
145void CConfigurableDomain::composeConfigurableElements(CXmlElement &xmlElement) const
146{
147    // Create ConfigurableElements element
148    CXmlElement xmlConfigurableElementsElement;
149
150    xmlElement.createChild(xmlConfigurableElementsElement, "ConfigurableElements");
151
152    // Serialize out all configurable elements settings
153    ConfigurableElementListIterator it;
154
155    for (it = _configurableElementList.begin(); it != _configurableElementList.end(); ++it) {
156
157        const CConfigurableElement *pConfigurableElement = *it;
158
159        // Create corresponding XML child element
160        CXmlElement xmlChildConfigurableElement;
161
162        xmlConfigurableElementsElement.createChild(xmlChildConfigurableElement,
163                                                   "ConfigurableElement");
164
165        // Set Path attribute
166        xmlChildConfigurableElement.setAttribute("Path", pConfigurableElement->getPath());
167    }
168}
169
170void CConfigurableDomain::composeSettings(CXmlElement &xmlElement,
171                                          CXmlDomainExportContext &context) const
172{
173    if (!context.withSettings()) {
174
175        return;
176    }
177
178    // Create Settings element
179    CXmlElement xmlSettingsElement;
180
181    xmlElement.createChild(xmlSettingsElement, "Settings");
182
183    // Serialize out all configurations settings
184    size_t uiNbConfigurations = getNbChildren();
185    size_t uiChildConfiguration;
186
187    for (uiChildConfiguration = 0; uiChildConfiguration < uiNbConfigurations;
188         uiChildConfiguration++) {
189
190        const CDomainConfiguration *pDomainConfiguration =
191            static_cast<const CDomainConfiguration *>(getChild(uiChildConfiguration));
192
193        // Create child xml element for that configuration
194        CXmlElement xmlConfigurationSettingsElement;
195
196        xmlSettingsElement.createChild(xmlConfigurationSettingsElement,
197                                       pDomainConfiguration->getXmlElementName());
198
199        // Set its name attribute
200        xmlConfigurationSettingsElement.setNameAttribute(pDomainConfiguration->getName());
201
202        // Serialize out configuration settings
203        pDomainConfiguration->composeSettings(xmlConfigurationSettingsElement, context);
204    }
205}
206
207// From IXmlSink
208bool CConfigurableDomain::fromXml(const CXmlElement &xmlElement,
209                                  CXmlSerializingContext &serializingContext)
210{
211    // Context
212    CXmlDomainImportContext &xmlDomainImportContext =
213        static_cast<CXmlDomainImportContext &>(serializingContext);
214
215    // Sequence awareness (optional)
216    xmlElement.getAttribute("SequenceAware", _bSequenceAware);
217
218    std::string name;
219    xmlElement.getAttribute("Name", name);
220    setName(name);
221
222    // Local parsing. Do not dig
223    if (!parseDomainConfigurations(xmlElement, xmlDomainImportContext) ||
224        !parseConfigurableElements(xmlElement, xmlDomainImportContext) ||
225        !parseSettings(xmlElement, xmlDomainImportContext)) {
226
227        return false;
228    }
229
230    // All provided configurations are parsed
231    // Attempt validation on areas of non provided configurations for all configurable elements if
232    // required
233    if (xmlDomainImportContext.autoValidationRequired()) {
234
235        autoValidateAll();
236    }
237
238    return true;
239}
240
241// XML parsing
242bool CConfigurableDomain::parseDomainConfigurations(const CXmlElement &xmlElement,
243                                                    CXmlDomainImportContext &serializingContext)
244{
245    // We're supposedly clean
246    assert(_configurableElementList.empty());
247
248    // Get Configurations element
249    CXmlElement xmlConfigurationsElement;
250
251    xmlElement.getChildElement("Configurations", xmlConfigurationsElement);
252
253    // Parse it and create domain configuration objects
254    return base::fromXml(xmlConfigurationsElement, serializingContext);
255}
256
257// Parse configurable elements
258bool CConfigurableDomain::parseConfigurableElements(const CXmlElement &xmlElement,
259                                                    CXmlDomainImportContext &serializingContext)
260{
261    CSystemClass &systemClass = serializingContext.getSystemClass();
262
263    // Get ConfigurableElements element
264    CXmlElement xmlConfigurableElementsElement;
265    xmlElement.getChildElement("ConfigurableElements", xmlConfigurableElementsElement);
266
267    // Parse it and associate found configurable elements to it
268    CXmlElement::CChildIterator it(xmlConfigurableElementsElement);
269
270    CXmlElement xmlConfigurableElementElement;
271
272    while (it.next(xmlConfigurableElementElement)) {
273
274        // Locate configurable element
275        string strConfigurableElementPath;
276        xmlConfigurableElementElement.getAttribute("Path", strConfigurableElementPath);
277
278        CPathNavigator pathNavigator(strConfigurableElementPath);
279        string strError;
280
281        // Is there an element and does it match system class name?
282        if (!pathNavigator.navigateThrough(systemClass.getName(), strError)) {
283
284            serializingContext.setError(
285                "Could not find configurable element of path " + strConfigurableElementPath +
286                " from ConfigurableDomain description " + getName() + " (" + strError + ")");
287
288            return false;
289        }
290        // Browse system class for configurable element
291        CConfigurableElement *pConfigurableElement =
292            static_cast<CConfigurableElement *>(systemClass.findDescendant(pathNavigator));
293
294        if (!pConfigurableElement) {
295
296            serializingContext.setError("Could not find configurable element of path " +
297                                        strConfigurableElementPath +
298                                        " from ConfigurableDomain description " + getName());
299
300            return false;
301        }
302        // Add found element to domain
303        core::Results infos;
304        if (!addConfigurableElement(pConfigurableElement, NULL, infos)) {
305
306            strError = utility::asString(infos);
307            serializingContext.setError(strError);
308
309            return false;
310        }
311    }
312
313    return true;
314}
315
316// Parse settings
317bool CConfigurableDomain::parseSettings(const CXmlElement &xmlElement,
318                                        CXmlDomainImportContext &serializingContext)
319{
320    // Check we actually need to parse configuration settings
321    if (!serializingContext.withSettings()) {
322
323        // No parsing required
324        return true;
325    }
326
327    // Get Settings element
328    CXmlElement xmlSettingsElement;
329    if (!xmlElement.getChildElement("Settings", xmlSettingsElement)) {
330
331        // No settings, bail out successfully
332        return true;
333    }
334
335    // Parse configuration settings
336    CXmlElement::CChildIterator it(xmlSettingsElement);
337
338    CXmlElement xmlConfigurationSettingsElement;
339
340    while (it.next(xmlConfigurationSettingsElement)) {
341        // Get domain configuration
342        CDomainConfiguration *pDomainConfiguration = static_cast<CDomainConfiguration *>(
343            findChild(xmlConfigurationSettingsElement.getNameAttribute()));
344
345        if (!pDomainConfiguration) {
346
347            serializingContext.setError("Could not find domain configuration referred to by"
348                                        " configurable domain \"" +
349                                        getName() + "\".");
350
351            return false;
352        }
353        // Have domain configuration parse settings for all configurable elements
354        if (!pDomainConfiguration->parseSettings(xmlConfigurationSettingsElement,
355                                                 serializingContext)) {
356
357            return false;
358        }
359    }
360
361    return true;
362}
363// Configurable elements association
364bool CConfigurableDomain::addConfigurableElement(CConfigurableElement *pConfigurableElement,
365                                                 const CParameterBlackboard *pMainBlackboard,
366                                                 core::Results &infos)
367{
368    // Already associated?
369    if (containsConfigurableElement(pConfigurableElement)) {
370
371        infos.push_back("Configurable element " + pConfigurableElement->getPath() +
372                        " already associated to configuration domain " + getName());
373
374        return false;
375    }
376
377    // Already owned?
378    if (pConfigurableElement->belongsTo(this)) {
379
380        infos.push_back("Configurable element " + pConfigurableElement->getPath() +
381                        " already owned by configuration domain " + getName());
382
383        return false;
384    }
385
386    // Do add
387    doAddConfigurableElement(pConfigurableElement, infos, pMainBlackboard);
388
389    return true;
390}
391
392bool CConfigurableDomain::removeConfigurableElement(CConfigurableElement *pConfigurableElement,
393                                                    string &strError)
394{
395    // Not associated?
396    if (!containsConfigurableElement(pConfigurableElement)) {
397
398        strError = "Configurable element " + pConfigurableElement->getPath() +
399                   " not associated to configuration domain " + getName();
400
401        return false;
402    }
403
404    // Do remove
405    doRemoveConfigurableElement(pConfigurableElement, true);
406
407    return true;
408}
409
410/**
411* Blackboard Configuration and Base Offset retrieval.
412*
413* This method fetches the Blackboard associated to the ConfigurableElement
414* given in parameter, for a specific Configuration. The ConfigurableElement
415* must belong to the Domain. If a Blackboard is found, the base offset of
416* the ConfigurableElement is returned as well. This base offset corresponds to
417* the offset of the ancestor of the ConfigurableElement associated to the Configuration.
418*
419* @param[in] strConfiguration                           Name of the Configuration.
420* @param[in] pCandidateDescendantConfigurableElement    Pointer to a CConfigurableElement that
421*                                                       belongs to the Domain.
422* @param[out] baseOffset                              The base offset of the CConfigurableElement.
423* @param[out] bIsLastApplied                            Boolean indicating that the Configuration is
424*                                                       the last one applied of the Domain.
425* @param[out] strError                                  Error message
426*
427* return Pointer to the Blackboard of the Configuration.
428*/
429CParameterBlackboard *CConfigurableDomain::findConfigurationBlackboard(
430    const string &strConfiguration,
431    const CConfigurableElement *pCandidateDescendantConfigurableElement, size_t &baseOffset,
432    bool &bIsLastApplied, string &strError) const
433{
434    // Find Configuration
435    const CDomainConfiguration *pDomainConfiguration =
436        static_cast<const CDomainConfiguration *>(findChild(strConfiguration));
437
438    if (!pDomainConfiguration) {
439
440        strError = "Domain configuration " + strConfiguration + " not found";
441
442        return NULL;
443    }
444
445    // Parse all configurable elements
446    ConfigurableElementListIterator it;
447
448    for (it = _configurableElementList.begin(); it != _configurableElementList.end(); ++it) {
449
450        const CConfigurableElement *pAssociatedConfigurableElement = *it;
451
452        // Check if the the associated element is the configurable element or one of its ancestors
453        if ((pCandidateDescendantConfigurableElement == pAssociatedConfigurableElement) ||
454            (pCandidateDescendantConfigurableElement->isDescendantOf(
455                pAssociatedConfigurableElement))) {
456
457            baseOffset = pAssociatedConfigurableElement->getOffset();
458            bIsLastApplied = (pDomainConfiguration == _pLastAppliedConfiguration);
459
460            return pDomainConfiguration->getBlackboard(pAssociatedConfigurableElement);
461        }
462    }
463
464    strError = "Element not associated to the Domain";
465
466    return NULL;
467}
468
469// Domain splitting
470bool CConfigurableDomain::split(CConfigurableElement *pConfigurableElement, core::Results &infos)
471{
472    // Not associated?
473    if (!containsConfigurableElement(pConfigurableElement)) {
474
475        std::string strError = "Configurable element " + pConfigurableElement->getPath() +
476                               " not associated to configuration domain " + getName();
477        infos.push_back(strError);
478
479        return false;
480    }
481
482    // Create sub domain areas for all configurable element's children
483    size_t uiNbConfigurableElementChildren = pConfigurableElement->getNbChildren();
484
485    if (!uiNbConfigurableElementChildren) {
486
487        std::string strError = "Configurable element " + pConfigurableElement->getPath() +
488                               " has no children to split configurable domain to";
489        infos.push_back(strError);
490
491        return false;
492    }
493
494    for (size_t uiChild = 0; uiChild < uiNbConfigurableElementChildren; uiChild++) {
495
496        CConfigurableElement *pChildConfigurableElement =
497            static_cast<CConfigurableElement *>(pConfigurableElement->getChild(uiChild));
498
499        doAddConfigurableElement(pChildConfigurableElement, infos);
500    }
501
502    // Delegate to configurations
503    size_t uiNbConfigurations = getNbChildren();
504
505    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {
506
507        CDomainConfiguration *pDomainConfiguration =
508            static_cast<CDomainConfiguration *>(getChild(uiChild));
509
510        pDomainConfiguration->split(pConfigurableElement);
511    }
512
513    // Remove given configurable element from this domain
514    // Note: we shouldn't need to recompute the sync set in that case, as the splitted element
515    // should include the syncers of its children elements
516    doRemoveConfigurableElement(pConfigurableElement, false);
517
518    return true;
519}
520
521// Check if there is a pending configuration for this domain: i.e. an applicable configuration
522// different from the last applied configuration
523const CDomainConfiguration *CConfigurableDomain::getPendingConfiguration() const
524{
525    const CDomainConfiguration *pApplicableDomainConfiguration =
526        findApplicableDomainConfiguration();
527
528    if (pApplicableDomainConfiguration) {
529
530        // Check not the last one before applying
531        if (!_pLastAppliedConfiguration ||
532            (_pLastAppliedConfiguration != pApplicableDomainConfiguration)) {
533
534            return pApplicableDomainConfiguration;
535        }
536    }
537
538    return NULL;
539}
540
541// Configuration application if required
542void CConfigurableDomain::apply(CParameterBlackboard *pParameterBlackboard, CSyncerSet *pSyncerSet,
543                                bool bForce, std::string &strInfo) const
544{
545    // Apply configuration only if the blackboard will
546    // be synchronized either now or by syncerSet.
547    if (!pSyncerSet ^ _bSequenceAware) {
548        // The configuration can not be syncronised
549        return;
550    }
551
552    if (bForce) {
553        // Force a configuration restore by forgetting about last applied configuration
554        _pLastAppliedConfiguration = NULL;
555    }
556    const CDomainConfiguration *pApplicableDomainConfiguration =
557        findApplicableDomainConfiguration();
558
559    if (pApplicableDomainConfiguration) {
560
561        // Check not the last one before applying
562        if (!_pLastAppliedConfiguration ||
563            _pLastAppliedConfiguration != pApplicableDomainConfiguration) {
564
565            strInfo = "Applying configuration '" + pApplicableDomainConfiguration->getName() +
566                      "' from domain '" + getName() + "'";
567
568            // Check if we need to synchronize during restore
569            bool bSync = !pSyncerSet && _bSequenceAware;
570
571            // Do the restore
572            pApplicableDomainConfiguration->restore(pParameterBlackboard, bSync, NULL);
573
574            // Record last applied configuration
575            _pLastAppliedConfiguration = pApplicableDomainConfiguration;
576
577            // Check we need to provide syncer set to caller
578            if (pSyncerSet && !_bSequenceAware) {
579
580                // Since we applied changes, add our own sync set to the given one
581                *pSyncerSet += _syncerSet;
582            }
583        }
584    }
585}
586
587// Return applicable configuration validity for given configurable element
588bool CConfigurableDomain::isApplicableConfigurationValid(
589    const CConfigurableElement *pConfigurableElement) const
590{
591    const CDomainConfiguration *pApplicableDomainConfiguration =
592        findApplicableDomainConfiguration();
593
594    return pApplicableDomainConfiguration &&
595           pApplicableDomainConfiguration->isValid(pConfigurableElement);
596}
597
598// In case configurable element was removed
599void CConfigurableDomain::computeSyncSet()
600{
601    // Clean sync set first
602    _syncerSet.clear();
603
604    // Add syncer sets for all associated configurable elements
605    ConfigurableElementToSyncerSetMapIterator mapIt;
606
607    for (mapIt = _configurableElementToSyncerSetMap.begin();
608         mapIt != _configurableElementToSyncerSetMap.end(); ++mapIt) {
609
610        const CSyncerSet *pSyncerSet = mapIt->second;
611
612        _syncerSet += *pSyncerSet;
613    }
614}
615
616// Configuration Management
617bool CConfigurableDomain::createConfiguration(const string &strName,
618                                              const CParameterBlackboard *pMainBlackboard,
619                                              string &strError)
620{
621    // Already exists?
622    if (findChild(strName)) {
623
624        strError = "Already existing configuration";
625
626        return false;
627    }
628
629    // Creation
630    CDomainConfiguration *pDomainConfiguration = new CDomainConfiguration(strName);
631
632    // Configurable elements association
633    ConfigurableElementListIterator it;
634
635    for (it = _configurableElementList.begin(); it != _configurableElementList.end(); ++it) {
636
637        const CConfigurableElement *pConfigurableElement = *it;
638        ;
639
640        // Retrieve associated syncer set
641        CSyncerSet *pSyncerSet = getSyncerSet(pConfigurableElement);
642
643        // Associate to configuration
644        pDomainConfiguration->addConfigurableElement(pConfigurableElement, pSyncerSet);
645    }
646
647    // Hierarchy
648    addChild(pDomainConfiguration);
649
650    // Ensure validity of fresh new domain configuration
651    // Attempt auto validation, so that the user gets his/her own settings by defaults
652    if (!autoValidateConfiguration(pDomainConfiguration)) {
653
654        // No valid configuration found to copy in from, validate againt main blackboard (will
655        // concerned remaining invalid parts)
656        pDomainConfiguration->validate(pMainBlackboard);
657    }
658
659    return true;
660}
661
662bool CConfigurableDomain::deleteConfiguration(const string &strName, string &strError)
663{
664    CDomainConfiguration *pDomainConfiguration = findConfiguration(strName, strError);
665
666    if (!pDomainConfiguration) {
667
668        return false;
669    }
670
671    // Was the last applied?
672    if (pDomainConfiguration == _pLastAppliedConfiguration) {
673
674        // Forget about it
675        _pLastAppliedConfiguration = NULL;
676    }
677
678    // Hierarchy
679    removeChild(pDomainConfiguration);
680
681    // Destroy
682    delete pDomainConfiguration;
683
684    return true;
685}
686
687void CConfigurableDomain::listAssociatedToElements(string &strResult) const
688{
689    ConfigurableElementListIterator it;
690
691    // Browse all configurable elements
692    for (it = _configurableElementList.begin(); it != _configurableElementList.end(); ++it) {
693
694        const CConfigurableElement *pConfigurableElement = *it;
695
696        strResult += pConfigurableElement->getPath() + "\n";
697    }
698}
699
700bool CConfigurableDomain::renameConfiguration(const string &strName, const string &strNewName,
701                                              string &strError)
702{
703    CDomainConfiguration *pDomainConfiguration = findConfiguration(strName, strError);
704
705    if (!pDomainConfiguration) {
706
707        return false;
708    }
709
710    // Rename
711    return pDomainConfiguration->rename(strNewName, strError);
712}
713
714bool CConfigurableDomain::restoreConfiguration(const string &configurationName,
715                                               CParameterBlackboard *mainBlackboard, bool autoSync,
716                                               core::Results &errors) const
717{
718    string error;
719
720    const CDomainConfiguration *configuration = findConfiguration(configurationName, error);
721
722    if (configuration == NULL) {
723
724        errors.push_back(error);
725        return false;
726    }
727
728    // Delegate
729    bool bSuccess = configuration->restore(mainBlackboard, autoSync && _bSequenceAware, &errors);
730
731    // Record last applied configuration
732    _pLastAppliedConfiguration = configuration;
733
734    // Synchronize
735    if (autoSync && !_bSequenceAware) {
736
737        bSuccess &= _syncerSet.sync(*mainBlackboard, false, &errors);
738    }
739    return bSuccess;
740}
741
742bool CConfigurableDomain::saveConfiguration(const string &strName,
743                                            const CParameterBlackboard *pMainBlackboard,
744                                            string &strError)
745{
746    // Find Domain configuration
747    CDomainConfiguration *pDomainConfiguration = findConfiguration(strName, strError);
748
749    if (!pDomainConfiguration) {
750
751        return false;
752    }
753
754    // Delegate
755    pDomainConfiguration->save(pMainBlackboard);
756
757    return true;
758}
759
760bool CConfigurableDomain::setElementSequence(const string &strConfiguration,
761                                             const std::vector<string> &astrNewElementSequence,
762                                             string &strError)
763{
764    // Find Domain configuration
765    CDomainConfiguration *pDomainConfiguration = findConfiguration(strConfiguration, strError);
766
767    if (!pDomainConfiguration) {
768
769        return false;
770    }
771
772    // Delegate to configuration
773    return pDomainConfiguration->setElementSequence(astrNewElementSequence, strError);
774}
775
776bool CConfigurableDomain::getElementSequence(const string &strConfiguration,
777                                             string &strResult) const
778{
779    // Find Domain configuration
780    const CDomainConfiguration *pDomainConfiguration =
781        findConfiguration(strConfiguration, strResult);
782
783    if (!pDomainConfiguration) {
784
785        return false;
786    }
787
788    // Delegate to configuration
789    pDomainConfiguration->getElementSequence(strResult);
790
791    return true;
792}
793
794bool CConfigurableDomain::setApplicationRule(
795    const string &strConfiguration, const string &strApplicationRule,
796    const CSelectionCriteriaDefinition *pSelectionCriteriaDefinition, string &strError)
797{
798    // Find Domain configuration
799    CDomainConfiguration *pDomainConfiguration = findConfiguration(strConfiguration, strError);
800
801    if (!pDomainConfiguration) {
802
803        return false;
804    }
805
806    // Delegate to configuration
807    return pDomainConfiguration->setApplicationRule(strApplicationRule,
808                                                    pSelectionCriteriaDefinition, strError);
809}
810
811bool CConfigurableDomain::clearApplicationRule(const string &strConfiguration, string &strError)
812{
813    // Find Domain configuration
814    CDomainConfiguration *pDomainConfiguration = findConfiguration(strConfiguration, strError);
815
816    if (!pDomainConfiguration) {
817
818        return false;
819    }
820
821    // Delegate to configuration
822    pDomainConfiguration->clearApplicationRule();
823
824    return true;
825}
826
827bool CConfigurableDomain::getApplicationRule(const string &strConfiguration,
828                                             string &strResult) const
829{
830    // Find Domain configuration
831    const CDomainConfiguration *pDomainConfiguration =
832        findConfiguration(strConfiguration, strResult);
833
834    if (!pDomainConfiguration) {
835
836        return false;
837    }
838
839    // Delegate to configuration
840    strResult = pDomainConfiguration->getApplicationRule();
841
842    return true;
843}
844
845// Last applied configuration
846string CConfigurableDomain::getLastAppliedConfigurationName() const
847{
848    if (_pLastAppliedConfiguration) {
849
850        return _pLastAppliedConfiguration->getName();
851    }
852    return "<none>";
853}
854
855// Pending configuration
856string CConfigurableDomain::getPendingConfigurationName() const
857{
858    const CDomainConfiguration *pApplicableDomainConfiguration =
859        findApplicableDomainConfiguration();
860
861    if (!pApplicableDomainConfiguration) {
862
863        // No configuration is pending
864        return "<none>";
865    }
866
867    // Check it will be applied
868    if (pApplicableDomainConfiguration != _pLastAppliedConfiguration) {
869
870        // Found config will get applied
871        return pApplicableDomainConfiguration->getName();
872    } else {
873
874        // Same configuration as current
875        return "";
876    }
877}
878
879// Ensure validity on whole domain from main blackboard
880void CConfigurableDomain::validate(const CParameterBlackboard *pMainBlackboard)
881{
882
883    // Propagate
884    size_t uiNbConfigurations = getNbChildren();
885
886    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {
887
888        CDomainConfiguration *pDomainConfiguration =
889            static_cast<CDomainConfiguration *>(getChild(uiChild));
890
891        pDomainConfiguration->validate(pMainBlackboard);
892    }
893}
894
895// Ensure validity on areas related to configurable element
896void CConfigurableDomain::validateAreas(const CConfigurableElement *pConfigurableElement,
897                                        const CParameterBlackboard *pMainBlackboard)
898{
899    // Propagate
900    size_t uiNbConfigurations = getNbChildren();
901
902    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {
903
904        CDomainConfiguration *pDomainConfiguration =
905            static_cast<CDomainConfiguration *>(getChild(uiChild));
906
907        pDomainConfiguration->validate(pConfigurableElement, pMainBlackboard);
908    }
909}
910
911// Attempt validation for all configurable element's areas, relying on already existing valid
912// configuration inside domain
913void CConfigurableDomain::autoValidateAll()
914{
915    // Validate
916    ConfigurableElementListIterator it;
917
918    // Browse all configurable elements for configuration validation
919    for (it = _configurableElementList.begin(); it != _configurableElementList.end(); ++it) {
920
921        const CConfigurableElement *pConfigurableElement = *it;
922
923        // Auto validate element
924        autoValidateAreas(pConfigurableElement);
925    }
926}
927
928// Attempt validation for configurable element's areas, relying on already existing valid
929// configuration inside domain
930void CConfigurableDomain::autoValidateAreas(const CConfigurableElement *pConfigurableElement)
931{
932    // Find first valid configuration for given configurable element
933    const CDomainConfiguration *pValidDomainConfiguration =
934        findValidDomainConfiguration(pConfigurableElement);
935
936    // No valid configuration found, give up
937    if (!pValidDomainConfiguration) {
938
939        return;
940    }
941
942    // Validate all other configurations against found one, if any
943    size_t uiNbConfigurations = getNbChildren();
944
945    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {
946
947        CDomainConfiguration *pDomainConfiguration =
948            static_cast<CDomainConfiguration *>(getChild(uiChild));
949
950        if (pDomainConfiguration != pValidDomainConfiguration &&
951            !pDomainConfiguration->isValid(pConfigurableElement)) {
952            // Validate
953            pDomainConfiguration->validateAgainst(pValidDomainConfiguration, pConfigurableElement);
954        }
955    }
956}
957
958// Attempt configuration validation for all configurable elements' areas, relying on already
959// existing valid configuration inside domain
960bool CConfigurableDomain::autoValidateConfiguration(CDomainConfiguration *pDomainConfiguration)
961{
962    // Find another configuration than this one, that ought to be valid!
963    size_t uiNbConfigurations = getNbChildren();
964
965    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {
966
967        const CDomainConfiguration *pPotententialValidDomainConfiguration =
968            static_cast<const CDomainConfiguration *>(getChild(uiChild));
969
970        if (pPotententialValidDomainConfiguration != pDomainConfiguration) {
971
972            // Validate against it
973            pDomainConfiguration->validateAgainst(pPotententialValidDomainConfiguration);
974
975            return true;
976        }
977    }
978    return false;
979}
980
981// Search for a valid configuration for given configurable element
982const CDomainConfiguration *CConfigurableDomain::findValidDomainConfiguration(
983    const CConfigurableElement *pConfigurableElement) const
984{
985    size_t uiNbConfigurations = getNbChildren();
986
987    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {
988
989        const CDomainConfiguration *pDomainConfiguration =
990            static_cast<const CDomainConfiguration *>(getChild(uiChild));
991
992        if (pDomainConfiguration->isValid(pConfigurableElement)) {
993
994            return pDomainConfiguration;
995        }
996    }
997    return NULL;
998}
999
1000// Search for an applicable configuration
1001const CDomainConfiguration *CConfigurableDomain::findApplicableDomainConfiguration() const
1002{
1003    size_t uiNbConfigurations = getNbChildren();
1004
1005    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {
1006
1007        const CDomainConfiguration *pDomainConfiguration =
1008            static_cast<const CDomainConfiguration *>(getChild(uiChild));
1009
1010        if (pDomainConfiguration->isApplicable()) {
1011
1012            return pDomainConfiguration;
1013        }
1014    }
1015    return NULL;
1016}
1017
1018// Gather set of configurable elements
1019void CConfigurableDomain::gatherConfigurableElements(
1020    std::set<const CConfigurableElement *> &configurableElementSet) const
1021{
1022    // Insert all configurable elements
1023    configurableElementSet.insert(_configurableElementList.begin(), _configurableElementList.end());
1024}
1025
1026// Check configurable element already attached
1027bool CConfigurableDomain::containsConfigurableElement(
1028    const CConfigurableElement *pConfigurableCandidateElement) const
1029{
1030    ConfigurableElementListIterator it;
1031
1032    // Browse all configurable elements for comparison
1033    for (it = _configurableElementList.begin(); it != _configurableElementList.end(); ++it) {
1034
1035        if (pConfigurableCandidateElement == *it) {
1036
1037            return true;
1038        }
1039    }
1040    return false;
1041}
1042
1043// Merge any descended configurable element to this one with this one
1044void CConfigurableDomain::mergeAlreadyAssociatedDescendantConfigurableElements(
1045    CConfigurableElement *newElement, core::Results &infos)
1046{
1047    std::list<CConfigurableElement *> mergedConfigurableElementList;
1048
1049    ConfigurableElementListIterator it;
1050
1051    // Browse all configurable elements (new one not yet in the list!)
1052    for (it = _configurableElementList.begin(); it != _configurableElementList.end(); ++it) {
1053
1054        CConfigurableElement *pConfigurablePotentialDescendantElement = *it;
1055
1056        if (pConfigurablePotentialDescendantElement->isDescendantOf(newElement)) {
1057
1058            infos.push_back("In domain '" + getName() +
1059                            "', merging descendant configurable element's configurations '" +
1060                            pConfigurablePotentialDescendantElement->getName() +
1061                            "' into its ascendant '" + newElement->getName() + "' ones");
1062
1063            // Merge configuration data
1064            mergeConfigurations(newElement, pConfigurablePotentialDescendantElement);
1065
1066            // Keep track for removal
1067            mergedConfigurableElementList.push_back(pConfigurablePotentialDescendantElement);
1068        }
1069    }
1070
1071    // Remove all merged elements (new one not yet in the list!)
1072    for (it = mergedConfigurableElementList.begin(); it != mergedConfigurableElementList.end();
1073         ++it) {
1074
1075        CConfigurableElement *pMergedConfigurableElement = *it;
1076
1077        // Remove merged from configurable element from internal tracking list
1078        // Note: we shouldn't need to recompute the sync set in that case, as the merged to element
1079        // should include the syncers of merged from elements
1080        doRemoveConfigurableElement(pMergedConfigurableElement, false);
1081    }
1082}
1083
1084void CConfigurableDomain::mergeConfigurations(CConfigurableElement *pToConfigurableElement,
1085                                              CConfigurableElement *pFromConfigurableElement)
1086{
1087    // Propagate to domain configurations
1088    size_t uiNbConfigurations = getNbChildren();
1089
1090    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {
1091
1092        CDomainConfiguration *pDomainConfiguration =
1093            static_cast<CDomainConfiguration *>(getChild(uiChild));
1094
1095        // Do the merge.
1096        pDomainConfiguration->merge(pToConfigurableElement, pFromConfigurableElement);
1097    }
1098}
1099
1100// Configurable elements association
1101void CConfigurableDomain::doAddConfigurableElement(CConfigurableElement *pConfigurableElement,
1102                                                   core::Results &infos,
1103                                                   const CParameterBlackboard *pMainBlackboard)
1104{
1105    // Inform configurable element
1106    pConfigurableElement->addAttachedConfigurableDomain(this);
1107
1108    // Create associated syncer set
1109    CSyncerSet *pSyncerSet = new CSyncerSet;
1110
1111    // Add to sync set the configurable element one
1112    pConfigurableElement->fillSyncerSet(*pSyncerSet);
1113
1114    // Store it
1115    _configurableElementToSyncerSetMap[pConfigurableElement] = pSyncerSet;
1116
1117    // Add it to global one
1118    _syncerSet += *pSyncerSet;
1119
1120    // Inform configurations
1121    size_t uiNbConfigurations = getNbChildren();
1122
1123    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {
1124
1125        CDomainConfiguration *pDomainConfiguration =
1126            static_cast<CDomainConfiguration *>(getChild(uiChild));
1127
1128        pDomainConfiguration->addConfigurableElement(pConfigurableElement, pSyncerSet);
1129    }
1130
1131    // Ensure area validity for that configurable element (if main blackboard provided)
1132    if (pMainBlackboard) {
1133
1134        infos.push_back("Validating domain '" + getName() +
1135                        "' against main blackboard for configurable element '" +
1136                        pConfigurableElement->getPath() + "'");
1137        // Need to validate against main blackboard
1138        validateAreas(pConfigurableElement, pMainBlackboard);
1139    }
1140
1141    // Already associated descendend configurable elements need a merge of their configuration data
1142    mergeAlreadyAssociatedDescendantConfigurableElements(pConfigurableElement, infos);
1143
1144    // Add to list
1145    _configurableElementList.push_back(pConfigurableElement);
1146}
1147
1148void CConfigurableDomain::doRemoveConfigurableElement(CConfigurableElement *pConfigurableElement,
1149                                                      bool bRecomputeSyncSet)
1150{
1151    // Remove from list
1152    _configurableElementList.remove(pConfigurableElement);
1153
1154    // Remove associated syncer set
1155    CSyncerSet *pSyncerSet = getSyncerSet(pConfigurableElement);
1156
1157    _configurableElementToSyncerSetMap.erase(pConfigurableElement);
1158
1159    delete pSyncerSet;
1160
1161    // Inform configurable element
1162    pConfigurableElement->removeAttachedConfigurableDomain(this);
1163
1164    // Inform configurations
1165    size_t uiNbConfigurations = getNbChildren();
1166
1167    for (size_t uiChild = 0; uiChild < uiNbConfigurations; uiChild++) {
1168
1169        CDomainConfiguration *pDomainConfiguration =
1170            static_cast<CDomainConfiguration *>(getChild(uiChild));
1171
1172        pDomainConfiguration->removeConfigurableElement(pConfigurableElement);
1173    }
1174    // Recompute our sync set if needed
1175    if (bRecomputeSyncSet) {
1176
1177        computeSyncSet();
1178    }
1179}
1180
1181// Syncer set retrieval from configurable element
1182CSyncerSet *CConfigurableDomain::getSyncerSet(
1183    const CConfigurableElement *pConfigurableElement) const
1184{
1185    ConfigurableElementToSyncerSetMapIterator mapIt =
1186        _configurableElementToSyncerSetMap.find(pConfigurableElement);
1187
1188    ALWAYS_ASSERT(mapIt != _configurableElementToSyncerSetMap.end(),
1189                  "Could not find syncer set for " << getName() << " configurable domain");
1190
1191    return mapIt->second;
1192}
1193
1194// Configuration retrieval
1195CDomainConfiguration *CConfigurableDomain::findConfiguration(const string &strConfiguration,
1196                                                             string &strError)
1197{
1198    CDomainConfiguration *pDomainConfiguration =
1199        static_cast<CDomainConfiguration *>(findChild(strConfiguration));
1200
1201    if (!pDomainConfiguration) {
1202
1203        strError = "Domain configuration " + strConfiguration + " not found";
1204
1205        return NULL;
1206    }
1207    return pDomainConfiguration;
1208}
1209
1210const CDomainConfiguration *CConfigurableDomain::findConfiguration(const string &strConfiguration,
1211                                                                   string &strError) const
1212{
1213    const CDomainConfiguration *pDomainConfiguration =
1214        static_cast<const CDomainConfiguration *>(findChild(strConfiguration));
1215
1216    if (!pDomainConfiguration) {
1217
1218        strError = "Domain configuration " + strConfiguration + " not found";
1219
1220        return NULL;
1221    }
1222    return pDomainConfiguration;
1223}
1224