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 "Subsystem.h"
31#include "ComponentLibrary.h"
32#include "InstanceDefinition.h"
33#include "XmlParameterSerializingContext.h"
34#include "ParameterAccessContext.h"
35#include "ConfigurationAccessContext.h"
36#include "SubsystemObjectCreator.h"
37#include "MappingData.h"
38#include <assert.h>
39#include <sstream>
40
41#define base CConfigurableElement
42
43using std::string;
44using std::list;
45
46CSubsystem::CSubsystem(const string &strName, core::log::Logger &logger)
47    : base(strName), _pComponentLibrary(new CComponentLibrary),
48      _pInstanceDefinition(new CInstanceDefinition), _logger(logger)
49{
50    // Note: A subsystem contains instance components
51    // InstanceDefintion and ComponentLibrary objects are then not chosen to be children
52    // They'll be delt with locally
53}
54
55CSubsystem::~CSubsystem()
56{
57    // FIXME use unique_ptr, would make this method empty
58
59    for (auto *subsystemObject : _subsystemObjectList) {
60
61        delete subsystemObject;
62    }
63
64    // Remove susbsystem creators
65    for (auto *subsystemObjectCreator : _subsystemObjectCreatorArray) {
66
67        delete subsystemObjectCreator;
68    }
69
70    // Order matters!
71    delete _pInstanceDefinition;
72    delete _pComponentLibrary;
73
74    delete _pMappingData;
75}
76
77string CSubsystem::getKind() const
78{
79    return "Subsystem";
80}
81
82// Susbsystem sanity
83bool CSubsystem::isAlive() const
84{
85    return true;
86}
87
88// Resynchronization after subsystem restart needed
89bool CSubsystem::needResync(bool /*bClear*/)
90{
91    return false;
92}
93
94bool CSubsystem::structureFromXml(const CXmlElement &xmlElement,
95                                  CXmlSerializingContext &serializingContext)
96{
97    // Subsystem class does not rely on generic fromXml algorithm of Element class.
98    // So, setting here the description if found as XML attribute.
99    string description;
100    xmlElement.getAttribute(gDescriptionPropertyName, description);
101    setDescription(description);
102
103    // Context
104    CXmlParameterSerializingContext &parameterBuildContext =
105        static_cast<CXmlParameterSerializingContext &>(serializingContext);
106
107    // Install temporary component library for further component creation
108    parameterBuildContext.setComponentLibrary(_pComponentLibrary);
109
110    CXmlElement childElement;
111
112    // Manage mapping attribute
113    string rawMapping;
114    xmlElement.getAttribute("Mapping", rawMapping);
115    if (!rawMapping.empty()) {
116
117        std::string error;
118        _pMappingData = new CMappingData;
119        if (!_pMappingData->init(rawMapping, error)) {
120
121            serializingContext.setError("Invalid Mapping data from XML element '" +
122                                        xmlElement.getPath() + "': " + error);
123            return false;
124        }
125    }
126
127    // XML populate ComponentLibrary
128    xmlElement.getChildElement("ComponentLibrary", childElement);
129
130    if (!_pComponentLibrary->fromXml(childElement, serializingContext)) {
131
132        return false;
133    }
134
135    // XML populate InstanceDefintion
136    xmlElement.getChildElement("InstanceDefintion", childElement);
137    if (!_pInstanceDefinition->fromXml(childElement, serializingContext)) {
138
139        return false;
140    }
141
142    // Create components
143    _pInstanceDefinition->createInstances(this);
144
145    // Execute mapping to create subsystem mapping entities
146    string strError;
147    if (!mapSubsystemElements(strError)) {
148
149        serializingContext.setError(strError);
150
151        return false;
152    }
153
154    return true;
155}
156
157bool CSubsystem::mapSubsystemElements(string &strError)
158{
159    // Default mapping context
160    CMappingContext context(_contextMappingKeyArray.size());
161    // Add Subsystem-level mapping data, which will be propagated to all children
162    handleMappingContext(this, context, strError);
163
164    _contextStack.push(context);
165
166    // Map all instantiated subelements in subsystem
167    size_t nbChildren = getNbChildren();
168
169    for (size_t child = 0; child < nbChildren; child++) {
170
171        CInstanceConfigurableElement *pInstanceConfigurableChildElement =
172            static_cast<CInstanceConfigurableElement *>(getChild(child));
173
174        if (!pInstanceConfigurableChildElement->map(*this, strError)) {
175
176            return false;
177        }
178    }
179    return true;
180}
181
182// Formats the mapping of the ConfigurableElements
183string CSubsystem::formatMappingDataList(
184    const list<const CConfigurableElement *> &configurableElementPath) const
185{
186    // The list is parsed in reverse order because it has been filled from the leaf to the trunk
187    // of the tree. When formatting the mapping, we want to start from the subsystem level
188    std::list<string> mappings;
189    list<const CConfigurableElement *>::const_reverse_iterator it;
190    for (it = configurableElementPath.rbegin(); it != configurableElementPath.rend(); ++it) {
191
192        auto maybeMapping = (*it)->getFormattedMapping();
193        if (not maybeMapping.empty()) {
194            mappings.push_back(maybeMapping);
195        }
196    }
197
198    return utility::asString(mappings, ", ");
199}
200
201// Find the CSubystemObject containing a specific CInstanceConfigurableElement
202const CSubsystemObject *CSubsystem::findSubsystemObjectFromConfigurableElement(
203    const CInstanceConfigurableElement *pInstanceConfigurableElement) const
204{
205
206    list<CSubsystemObject *>::const_iterator it;
207    for (it = _subsystemObjectList.begin(); it != _subsystemObjectList.end(); ++it) {
208
209        // Check if one of the SubsystemObjects is associated with a ConfigurableElement
210        // corresponding to the expected one
211        const CSubsystemObject *pSubsystemObject = *it;
212
213        if (pSubsystemObject->getConfigurableElement() == pInstanceConfigurableElement) {
214
215            return pSubsystemObject;
216        }
217    }
218
219    return nullptr;
220}
221
222void CSubsystem::findSubsystemLevelMappingKeyValue(
223    const CInstanceConfigurableElement *pInstanceConfigurableElement, string &strMappingKey,
224    string &strMappingValue) const
225{
226    // Find creator to get key name
227    std::vector<CSubsystemObjectCreator *>::const_iterator it;
228    for (it = _subsystemObjectCreatorArray.begin(); it != _subsystemObjectCreatorArray.end();
229         ++it) {
230
231        const CSubsystemObjectCreator *pSubsystemObjectCreator = *it;
232
233        strMappingKey = pSubsystemObjectCreator->getMappingKey();
234
235        // Check if the ObjectCreator MappingKey corresponds to the element mapping data
236        const string *pStrValue;
237        if (pInstanceConfigurableElement->getMappingData(strMappingKey, pStrValue)) {
238
239            strMappingValue = *pStrValue;
240            return;
241        }
242    }
243    assert(0);
244}
245
246// Formats the mapping data as a comma separated list of key value pairs
247string CSubsystem::getFormattedSubsystemMappingData(
248    const CInstanceConfigurableElement *pInstanceConfigurableElement) const
249{
250    // Find the SubsystemObject related to pInstanceConfigurableElement
251    const CSubsystemObject *pSubsystemObject =
252        findSubsystemObjectFromConfigurableElement(pInstanceConfigurableElement);
253
254    // Exit if node does not correspond to a SubsystemObject
255    if (pSubsystemObject == NULL) {
256
257        return "";
258    }
259
260    // Find SubsystemCreator mapping key
261    string strMappingKey;
262    string strMappingValue; // mapping value where amends are not replaced by their value
263    findSubsystemLevelMappingKeyValue(pInstanceConfigurableElement, strMappingKey, strMappingValue);
264
265    // Find SubSystemObject mapping value (with amends replaced by their value)
266    return strMappingKey + ":" + pSubsystemObject->getFormattedMappingValue();
267}
268
269string CSubsystem::getMapping(list<const CConfigurableElement *> &configurableElementPath) const
270{
271    if (configurableElementPath.empty()) {
272
273        return "";
274    }
275
276    // Get the first element, which is the element containing the amended mapping
277    const CInstanceConfigurableElement *pInstanceConfigurableElement =
278        static_cast<const CInstanceConfigurableElement *>(configurableElementPath.front());
279
280    // Format context mapping data
281    string strValue = formatMappingDataList(configurableElementPath);
282
283    // Print the mapping of the first node, which corresponds to a SubsystemObject
284    auto subsystemObjectAmendedMapping =
285        getFormattedSubsystemMappingData(pInstanceConfigurableElement);
286    if (not subsystemObjectAmendedMapping.empty()) {
287        strValue += ", " + subsystemObjectAmendedMapping;
288    }
289
290    return strValue;
291}
292
293// Used for simulation and virtual subsystems
294void CSubsystem::setDefaultValues(CParameterAccessContext &parameterAccessContext) const
295{
296    base::setDefaultValues(parameterAccessContext);
297}
298
299// Belonging subsystem
300const CSubsystem *CSubsystem::getBelongingSubsystem() const
301{
302    return this;
303}
304
305// Subsystem context mapping keys publication
306void CSubsystem::addContextMappingKey(const string &strMappingKey)
307{
308    _contextMappingKeyArray.push_back(strMappingKey);
309}
310
311// Subsystem object creator publication (strong reference)
312void CSubsystem::addSubsystemObjectFactory(CSubsystemObjectCreator *pSubsystemObjectCreator)
313{
314    _subsystemObjectCreatorArray.push_back(pSubsystemObjectCreator);
315}
316
317// Generic error handling from derived subsystem classes
318string CSubsystem::getMappingError(const string &strKey, const string &strMessage,
319                                   const CConfigurableElement *pConfigurableElement) const
320{
321    return getName() + " " + getKind() + " " + "mapping:\n" + strKey + " " + "error: \"" +
322           strMessage + "\" " + "for element " + pConfigurableElement->getPath();
323}
324
325bool CSubsystem::getMappingData(const std::string &strKey, const std::string *&pStrValue) const
326{
327    if (_pMappingData) {
328
329        return _pMappingData->getValue(strKey, pStrValue);
330    }
331    return false;
332}
333
334// Returns the formatted mapping
335std::string CSubsystem::getFormattedMapping() const
336{
337    if (!_pMappingData) {
338        return "";
339    }
340    return _pMappingData->asString();
341}
342
343// Mapping generic context handling
344bool CSubsystem::handleMappingContext(const CConfigurableElement *pConfigurableElement,
345                                      CMappingContext &context, string &strError) const
346{
347    // Feed context with found mapping data
348    for (size_t item = 0; item < _contextMappingKeyArray.size(); item++) {
349
350        const string &strKey = _contextMappingKeyArray[item];
351        const string *pStrValue;
352
353        if (pConfigurableElement->getMappingData(strKey, pStrValue)) {
354            // Assign item to context
355            if (!context.setItem(item, &strKey, pStrValue)) {
356
357                strError = getMappingError(strKey, "Already set", pConfigurableElement);
358
359                return false;
360            }
361        }
362    }
363    return true;
364}
365
366// Subsystem object creation handling
367bool CSubsystem::handleSubsystemObjectCreation(
368    CInstanceConfigurableElement *pInstanceConfigurableElement, CMappingContext &context,
369    bool &bHasCreatedSubsystemObject, string &strError)
370{
371    bHasCreatedSubsystemObject = false;
372
373    for (const auto *pSubsystemObjectCreator : _subsystemObjectCreatorArray) {
374
375        // Mapping key
376        string strKey = pSubsystemObjectCreator->getMappingKey();
377        // Object id
378        const string *pStrValue;
379
380        if (pInstanceConfigurableElement->getMappingData(strKey, pStrValue)) {
381
382            // First check context consistency
383            // (required ancestors must have been set prior to object creation)
384            uint32_t uiAncestorMask = pSubsystemObjectCreator->getAncestorMask();
385
386            for (size_t uiAncestorKey = 0; uiAncestorKey < _contextMappingKeyArray.size();
387                 uiAncestorKey++) {
388
389                if (!((1 << uiAncestorKey) & uiAncestorMask)) {
390                    // Ancestor not required
391                    continue;
392                }
393                // Check ancestor was provided
394                if (!context.iSet(uiAncestorKey)) {
395
396                    strError =
397                        getMappingError(strKey, _contextMappingKeyArray[uiAncestorKey] + " not set",
398                                        pInstanceConfigurableElement);
399
400                    return false;
401                }
402            }
403
404            // Then check configurable element size is correct
405            if (pInstanceConfigurableElement->getFootPrint() >
406                pSubsystemObjectCreator->getMaxConfigurableElementSize()) {
407
408                string strSizeError =
409                    "Size should not exceed " +
410                    std::to_string(pSubsystemObjectCreator->getMaxConfigurableElementSize());
411
412                strError = getMappingError(strKey, strSizeError, pInstanceConfigurableElement);
413
414                return false;
415            }
416
417            // Do create object and keep its track
418            _subsystemObjectList.push_back(pSubsystemObjectCreator->objectCreate(
419                *pStrValue, pInstanceConfigurableElement, context, _logger));
420
421            // Indicate subsytem creation to caller
422            bHasCreatedSubsystemObject = true;
423
424            // The subsystem Object has been instantiated, no need to continue looking for an
425            // instantiation mapping
426            break;
427        }
428    }
429
430    return true;
431}
432
433// From IMapper
434// Handle a configurable element mapping
435bool CSubsystem::mapBegin(CInstanceConfigurableElement *pInstanceConfigurableElement,
436                          bool &bKeepDiving, string &strError)
437{
438    // Get current context
439    CMappingContext context = _contextStack.top();
440
441    // Add mapping in context
442    if (!handleMappingContext(pInstanceConfigurableElement, context, strError)) {
443
444        return false;
445    }
446
447    // Push context
448    _contextStack.push(context);
449
450    // Assume diving by default
451    bKeepDiving = true;
452
453    // Deal with ambiguous usage of parameter blocks
454    bool bShouldCreateSubsystemObject = true;
455
456    switch (pInstanceConfigurableElement->getType()) {
457
458    case CInstanceConfigurableElement::EComponent:
459    case CInstanceConfigurableElement::EParameterBlock:
460        // Subsystem object creation is optional in parameter blocks
461        bShouldCreateSubsystemObject = false;
462    // No break
463    case CInstanceConfigurableElement::EBitParameterBlock:
464    case CInstanceConfigurableElement::EParameter:
465    case CInstanceConfigurableElement::EStringParameter:
466
467        bool bHasCreatedSubsystemObject;
468
469        if (!handleSubsystemObjectCreation(pInstanceConfigurableElement, context,
470                                           bHasCreatedSubsystemObject, strError)) {
471
472            return false;
473        }
474        // Check for creation error
475        if (bShouldCreateSubsystemObject && !bHasCreatedSubsystemObject) {
476
477            strError = getMappingError("Not found", "Subsystem object mapping key is missing",
478                                       pInstanceConfigurableElement);
479            return false;
480        }
481        // Not created and no error, keep diving
482        bKeepDiving = !bHasCreatedSubsystemObject;
483
484        return true;
485
486    default:
487        assert(0);
488        return false;
489    }
490}
491
492void CSubsystem::mapEnd()
493{
494    // Unstack context
495    _contextStack.pop();
496}
497