/*
* Copyright (c) 2015, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ParameterFramework.h"
#include <ParameterMgrPlatformConnector.h>
#include <NonCopyable.hpp>
#include <iostream>
#include <limits>
#include <string>
#include <map>
#include <cassert>
#include <cstring>
#include <cstdlib>
using std::string;
/** Rename long pfw types to short ones in pfw namespace. */
namespace pfw
{
typedef ISelectionCriterionInterface Criterion;
typedef std::map<string, Criterion *> Criteria;
typedef CParameterMgrPlatformConnector Pfw;
} // namespace pfw
/** Class to abstract the boolean+string status api. */
class Status
{
public:
/** Fail without an instance of status. */
static bool failure() { return false; }
/** Fail with the given error msg. */
bool failure(const string &msg)
{
mMsg = msg;
return false;
}
/** Success (no error message). */
bool success()
{
mMsg.clear();
return true;
}
/** Forward a status operation.
* @param[in] success the operaton status to forward
* or forward a previous failure if omitted
*/
bool forward(bool success = false)
{
if (success) {
mMsg.clear();
}
return success;
}
/** Error message accessors.
*
* Pfw api requires to provide a reference to a string in order
* for it to log. This function provide a reference to a string that
* will be added to the error message on failure.
*/
string &msg() { return mMsg; }
private:
string mMsg;
};
///////////////////////////////
///////////// Log /////////////
///////////////////////////////
/** Default log callback. Log to cout or cerr depending on level. */
static void defaultLogCb(void *, PfwLogLevel level, const char *logLine)
{
switch (level) {
case pfwLogInfo:
std::cout << logLine << std::endl;
break;
case pfwLogWarning:
std::cerr << logLine << std::endl;
break;
};
}
static PfwLogger defaultLogger = {nullptr, &defaultLogCb};
class LogWrapper : public CParameterMgrPlatformConnector::ILogger
{
public:
LogWrapper(const PfwLogger &logger) : mLogger(logger) {}
LogWrapper() : mLogger() {}
virtual ~LogWrapper() {}
private:
void info(const string &msg) override { log(pfwLogInfo, msg); }
void warning(const string &msg) override { log(pfwLogWarning, msg); }
void log(PfwLogLevel level, const string &strLog)
{
// A LogWrapper should NOT be register to the pfw (thus log called)
// if logCb is NULL.
assert(mLogger.logCb != nullptr);
mLogger.logCb(mLogger.userCtx, level, strLog.c_str());
}
PfwLogger mLogger;
};
///////////////////////////////
///////////// Core ////////////
///////////////////////////////
struct PfwHandler_ : private utility::NonCopyable
{
void setLogger(const PfwLogger *logger);
bool createCriteria(const PfwCriterion criteria[], size_t criterionNb);
pfw::Criteria criteria;
pfw::Pfw *pfw = nullptr;
/** Status of the last called function.
* Is mutable because even a const function can fail.
*/
mutable Status lastStatus;
private:
LogWrapper mLogger;
};
PfwHandler *pfwCreate()
{
return new PfwHandler();
}
void pfwDestroy(PfwHandler *handle)
{
delete handle->pfw;
delete handle;
}
void PfwHandler::setLogger(const PfwLogger *logger)
{
if (logger != nullptr and logger->logCb == nullptr) {
return; // There is no callback, do not log => do not add a logger
}
mLogger = logger != nullptr ? *logger : defaultLogger;
pfw->setLogger(&mLogger);
}
bool PfwHandler::createCriteria(const PfwCriterion criteriaArray[], size_t criterionNb)
{
Status &status = lastStatus;
// Add criteria
for (size_t criterionIndex = 0; criterionIndex < criterionNb; ++criterionIndex) {
const PfwCriterion &criterion = criteriaArray[criterionIndex];
if (criterion.name == nullptr) {
return status.failure("Criterion name is NULL");
}
if (criterion.values == nullptr) {
return status.failure("Criterion values is NULL");
}
// Check that the criterion does not exist
if (criteria.find(criterion.name) != criteria.end()) {
return status.failure("Criterion \"" + string(criterion.name) + "\" already exist");
}
// Create criterion type
ISelectionCriterionTypeInterface *type =
pfw->createSelectionCriterionType(criterion.inclusive);
assert(type != nullptr);
// Add criterion values
for (size_t valueIndex = 0; criterion.values[valueIndex] != nullptr; ++valueIndex) {
int value;
if (criterion.inclusive) {
// Check that (int)1 << valueIndex would not overflow (UB)
if (std::numeric_limits<int>::max() >> valueIndex == 0) {
return status.failure("Too many values for criterion " +
string(criterion.name));
}
value = 1 << valueIndex;
} else {
value = static_cast<int>(valueIndex);
}
const char *valueName = criterion.values[valueIndex];
string error;
if (not type->addValuePair(value, valueName, error)) {
return status.failure("Could not add value " + string(valueName) +
" to criterion " + criterion.name + ": " + error);
}
}
// Create criterion and add it to the pfw
criteria[criterion.name] = pfw->createSelectionCriterion(criterion.name, type);
}
return status.success();
}
bool pfwStart(PfwHandler *handle, const char *configPath, const PfwCriterion criteria[],
size_t criterionNb, const PfwLogger *logger)
{
// Check that the api is correctly used
Status &status = handle->lastStatus;
if (handle->pfw != nullptr) {
return status.failure("Can not start an already started parameter framework");
}
// Create a pfw
handle->pfw = new CParameterMgrPlatformConnector(configPath);
handle->setLogger(logger);
if (not handle->createCriteria(criteria, criterionNb)) {
return status.failure();
}
return status.forward(handle->pfw->start(status.msg()));
}
const char *pfwGetLastError(const PfwHandler *handle)
{
return handle->lastStatus.msg().c_str();
}
static pfw::Criterion *getCriterion(const pfw::Criteria &criteria, const string &name)
{
auto it = criteria.find(name);
return it == criteria.end() ? nullptr : it->second;
}
bool pfwSetCriterion(PfwHandler *handle, const char name[], int value)
{
Status &status = handle->lastStatus;
if (handle->pfw == nullptr) {
return status.failure("Can not set criterion \"" + string(name) +
"\" as the parameter framework is not started.");
}
pfw::Criterion *criterion = getCriterion(handle->criteria, name);
if (criterion == nullptr) {
return status.failure("Can not set criterion " + string(name) + " as does not exist");
}
criterion->setCriterionState(value);
return status.success();
}
bool pfwGetCriterion(const PfwHandler *handle, const char name[], int *value)
{
Status &status = handle->lastStatus;
if (handle->pfw == nullptr) {
return status.failure("Can not get criterion \"" + string(name) +
"\" as the parameter framework is not started.");
}
pfw::Criterion *criterion = getCriterion(handle->criteria, name);
if (criterion == nullptr) {
return status.failure("Can not get criterion " + string(name) + " as it does not exist");
}
*value = criterion->getCriterionState();
return status.success();
}
bool pfwApplyConfigurations(const PfwHandler *handle)
{
Status &status = handle->lastStatus;
if (handle->pfw == nullptr) {
return status.failure("Can not commit criteria "
"as the parameter framework is not started.");
}
handle->pfw->applyConfigurations();
return status.success();
}
///////////////////////////////
/////// Parameter access //////
///////////////////////////////
struct PfwParameterHandler_
{
PfwHandler &pfw;
CParameterHandle ¶meter;
};
PfwParameterHandler *pfwBindParameter(PfwHandler *handle, const char path[])
{
Status &status = handle->lastStatus;
if (handle->pfw == nullptr) {
status.failure("The parameter framework is not started, "
"while trying to bind parameter \"" +
string(path) + "\")");
return nullptr;
}
CParameterHandle *paramHandle;
paramHandle = handle->pfw->createParameterHandle(path, status.msg());
if (paramHandle == nullptr) {
return nullptr;
}
status.success();
PfwParameterHandler publicHandle = {*handle, *paramHandle};
return new PfwParameterHandler(publicHandle);
}
void pfwUnbindParameter(PfwParameterHandler *handle)
{
delete &handle->parameter;
delete handle;
}
bool pfwGetIntParameter(const PfwParameterHandler *handle, int32_t *value)
{
Status &status = handle->pfw.lastStatus;
return status.forward(handle->parameter.getAsSignedInteger(*value, status.msg()));
}
bool pfwSetIntParameter(PfwParameterHandler *handle, int32_t value)
{
Status &status = handle->pfw.lastStatus;
return status.forward(handle->parameter.setAsSignedInteger(value, status.msg()));
}
bool pfwGetStringParameter(const PfwParameterHandler *handle, char *value[])
{
Status &status = handle->pfw.lastStatus;
*value = nullptr;
string retValue;
bool success = handle->parameter.getAsString(retValue, status.msg());
if (not success) {
return status.forward();
}
*value = strdup(retValue.c_str());
return status.success();
}
bool pfwSetStringParameter(PfwParameterHandler *handle, const char value[])
{
Status &status = handle->pfw.lastStatus;
return status.forward(handle->parameter.setAsString(value, status.msg()));
}
void pfwFree(void *ptr)
{
std::free(ptr);
}