1/*
2 * Copyright (c) 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
31#include "ParameterFramework.h"
32#include <ParameterMgrPlatformConnector.h>
33
34#include <NonCopyable.hpp>
35
36#include <iostream>
37#include <limits>
38#include <string>
39#include <map>
40
41#include <cassert>
42#include <cstring>
43#include <cstdlib>
44
45using std::string;
46
47/** Rename long pfw types to short ones in pfw namespace. */
48namespace pfw
49{
50typedef ISelectionCriterionInterface Criterion;
51typedef std::map<string, Criterion *> Criteria;
52typedef CParameterMgrPlatformConnector Pfw;
53}
54
55/** Class to abstract the boolean+string status api. */
56class Status
57{
58public:
59    /** Fail without an instance of status. */
60    static bool failure() { return false; }
61    /** Fail with the given error msg. */
62    bool failure(const string &msg)
63    {
64        mMsg = msg;
65        return false;
66    }
67    /** Success (no error message). */
68    bool success()
69    {
70        mMsg.clear();
71        return true;
72    }
73
74    /** Forward a status operation.
75      * @param[in] success the operaton status to forward
76      *                    or forward a previous failure if omitted
77      */
78    bool forward(bool success = false)
79    {
80        if (success) {
81            mMsg.clear();
82        }
83        return success;
84    }
85    /** Error message accessors.
86      *
87      * Pfw api requires to provide a reference to a string in order
88      * for it to log. This function provide a reference to a string that
89      * will be added to the error message on failure.
90      */
91    string &msg() { return mMsg; }
92private:
93    string mMsg;
94};
95
96///////////////////////////////
97///////////// Log /////////////
98///////////////////////////////
99
100/** Default log callback. Log to cout or cerr depending on level. */
101static void defaultLogCb(void *, PfwLogLevel level, const char *logLine)
102{
103    switch (level) {
104    case pfwLogInfo:
105        std::cout << logLine << std::endl;
106        break;
107    case pfwLogWarning:
108        std::cerr << logLine << std::endl;
109        break;
110    };
111}
112
113static PfwLogger defaultLogger = {NULL, &defaultLogCb};
114
115class LogWrapper : public CParameterMgrPlatformConnector::ILogger
116{
117public:
118    LogWrapper(const PfwLogger &logger) : mLogger(logger) {}
119    LogWrapper() : mLogger() {}
120    virtual ~LogWrapper() {}
121private:
122    void info(const string &msg) override { log(pfwLogInfo, msg); }
123
124    void warning(const string &msg) override { log(pfwLogWarning, msg); }
125
126    void log(PfwLogLevel level, const string &strLog)
127    {
128        // A LogWrapper should NOT be register to the pfw (thus log called)
129        // if logCb is NULL.
130        assert(mLogger.logCb != NULL);
131        mLogger.logCb(mLogger.userCtx, level, strLog.c_str());
132    }
133
134    PfwLogger mLogger;
135};
136
137///////////////////////////////
138///////////// Core ////////////
139///////////////////////////////
140
141struct PfwHandler_ : private utility::NonCopyable
142{
143    void setLogger(const PfwLogger *logger);
144    bool createCriteria(const PfwCriterion criteria[], size_t criterionNb);
145
146    pfw::Criteria criteria;
147    pfw::Pfw *pfw = nullptr;
148    /** Status of the last called function.
149      * Is mutable because even a const function can fail.
150      */
151    mutable Status lastStatus;
152
153private:
154    LogWrapper mLogger;
155};
156
157PfwHandler *pfwCreate()
158{
159    return new PfwHandler();
160}
161
162void pfwDestroy(PfwHandler *handle)
163{
164    delete handle->pfw;
165    delete handle;
166}
167
168void PfwHandler::setLogger(const PfwLogger *logger)
169{
170    if (logger != NULL and logger->logCb == NULL) {
171        return; // There is no callback, do not log => do not add a logger
172    }
173    mLogger = logger != NULL ? *logger : defaultLogger;
174    pfw->setLogger(&mLogger);
175}
176
177bool PfwHandler::createCriteria(const PfwCriterion criteriaArray[], size_t criterionNb)
178{
179    Status &status = lastStatus;
180    // Add criteria
181    for (size_t criterionIndex = 0; criterionIndex < criterionNb; ++criterionIndex) {
182        const PfwCriterion &criterion = criteriaArray[criterionIndex];
183        if (criterion.name == NULL) {
184            return status.failure("Criterion name is NULL");
185        }
186        if (criterion.values == NULL) {
187            return status.failure("Criterion values is NULL");
188        }
189        // Check that the criterion does not exist
190        if (criteria.find(criterion.name) != criteria.end()) {
191            return status.failure("Criterion \"" + string(criterion.name) + "\" already exist");
192        }
193
194        // Create criterion type
195        ISelectionCriterionTypeInterface *type =
196            pfw->createSelectionCriterionType(criterion.inclusive);
197        assert(type != NULL);
198        // Add criterion values
199        for (size_t valueIndex = 0; criterion.values[valueIndex] != NULL; ++valueIndex) {
200            int value;
201            if (criterion.inclusive) {
202                // Check that (int)1 << valueIndex would not overflow (UB)
203                if (std::numeric_limits<int>::max() >> valueIndex == 0) {
204                    return status.failure("Too many values for criterion " +
205                                          string(criterion.name));
206                }
207                value = 1 << valueIndex;
208            } else {
209                value = static_cast<int>(valueIndex);
210            }
211            const char *valueName = criterion.values[valueIndex];
212            string error;
213            if (not type->addValuePair(value, valueName, error)) {
214                return status.failure("Could not add value " + string(valueName) +
215                                      " to criterion " + criterion.name + ": " + error);
216            }
217        }
218        // Create criterion and add it to the pfw
219        criteria[criterion.name] = pfw->createSelectionCriterion(criterion.name, type);
220    }
221    return status.success();
222}
223
224bool pfwStart(PfwHandler *handle, const char *configPath, const PfwCriterion criteria[],
225              size_t criterionNb, const PfwLogger *logger)
226{
227    // Check that the api is correctly used
228    Status &status = handle->lastStatus;
229
230    if (handle->pfw != NULL) {
231        return status.failure("Can not start an already started parameter framework");
232    }
233    // Create a pfw
234    handle->pfw = new CParameterMgrPlatformConnector(configPath);
235
236    handle->setLogger(logger);
237
238    if (not handle->createCriteria(criteria, criterionNb)) {
239        return status.failure();
240    }
241
242    return status.forward(handle->pfw->start(status.msg()));
243}
244
245const char *pfwGetLastError(const PfwHandler *handle)
246{
247    return handle->lastStatus.msg().c_str();
248}
249
250static pfw::Criterion *getCriterion(const pfw::Criteria &criteria, const string &name)
251{
252    pfw::Criteria::const_iterator it = criteria.find(name);
253    return it == criteria.end() ? NULL : it->second;
254}
255
256bool pfwSetCriterion(PfwHandler *handle, const char name[], int value)
257{
258    Status &status = handle->lastStatus;
259    if (handle->pfw == NULL) {
260        return status.failure("Can not set criterion \"" + string(name) +
261                              "\" as the parameter framework is not started.");
262    }
263    pfw::Criterion *criterion = getCriterion(handle->criteria, name);
264    if (criterion == NULL) {
265        return status.failure("Can not set criterion " + string(name) + " as does not exist");
266    }
267    criterion->setCriterionState(value);
268    return status.success();
269}
270bool pfwGetCriterion(const PfwHandler *handle, const char name[], int *value)
271{
272    Status &status = handle->lastStatus;
273    if (handle->pfw == NULL) {
274        return status.failure("Can not get criterion \"" + string(name) +
275                              "\" as the parameter framework is not started.");
276    }
277    pfw::Criterion *criterion = getCriterion(handle->criteria, name);
278    if (criterion == NULL) {
279        return status.failure("Can not get criterion " + string(name) + " as it does not exist");
280    }
281    *value = criterion->getCriterionState();
282    return status.success();
283}
284
285bool pfwApplyConfigurations(const PfwHandler *handle)
286{
287    Status &status = handle->lastStatus;
288    if (handle->pfw == NULL) {
289        return status.failure("Can not commit criteria "
290                              "as the parameter framework is not started.");
291    }
292    handle->pfw->applyConfigurations();
293    return status.success();
294}
295
296///////////////////////////////
297/////// Parameter access //////
298///////////////////////////////
299
300struct PfwParameterHandler_
301{
302    PfwHandler &pfw;
303    CParameterHandle &parameter;
304};
305
306PfwParameterHandler *pfwBindParameter(PfwHandler *handle, const char path[])
307{
308    Status &status = handle->lastStatus;
309    if (handle->pfw == NULL) {
310        status.failure("The parameter framework is not started, "
311                       "while trying to bind parameter \"" +
312                       string(path) + "\")");
313        return NULL;
314    }
315
316    CParameterHandle *paramHandle;
317    paramHandle = handle->pfw->createParameterHandle(path, status.msg());
318    if (paramHandle == NULL) {
319        return NULL;
320    }
321
322    status.success();
323    PfwParameterHandler publicHandle = {*handle, *paramHandle};
324    return new PfwParameterHandler(publicHandle);
325}
326
327void pfwUnbindParameter(PfwParameterHandler *handle)
328{
329    delete &handle->parameter;
330    delete handle;
331}
332
333bool pfwGetIntParameter(const PfwParameterHandler *handle, int32_t *value)
334{
335    Status &status = handle->pfw.lastStatus;
336    return status.forward(handle->parameter.getAsSignedInteger(*value, status.msg()));
337}
338bool pfwSetIntParameter(PfwParameterHandler *handle, int32_t value)
339{
340    Status &status = handle->pfw.lastStatus;
341    return status.forward(handle->parameter.setAsSignedInteger(value, status.msg()));
342}
343
344bool pfwGetStringParameter(const PfwParameterHandler *handle, char *value[])
345{
346    Status &status = handle->pfw.lastStatus;
347    *value = NULL;
348    string retValue;
349    bool success = handle->parameter.getAsString(retValue, status.msg());
350    if (not success) {
351        return status.forward();
352    }
353
354    *value = strdup(retValue.c_str());
355    return status.success();
356}
357
358bool pfwSetStringParameter(PfwParameterHandler *handle, const char value[])
359{
360    Status &status = handle->pfw.lastStatus;
361    return status.forward(handle->parameter.setAsString(value, status.msg()));
362}
363
364void pfwFree(void *ptr)
365{
366    std::free(ptr);
367}
368