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