1229 lines
44 KiB
C++
1229 lines
44 KiB
C++
/*
|
|
* Copyright (C) 2015-2017 Intel Corporation
|
|
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#define LOG_TAG "GCSS"
|
|
|
|
#include <assert.h>
|
|
#include "LogHelper.h"
|
|
#include "graph_query_manager.h"
|
|
|
|
using namespace GCSS;
|
|
using namespace GCSS::RelayControl;
|
|
using namespace std;
|
|
using namespace android::camera2;
|
|
|
|
void GraphQueryManager::setGraphSettings(const IGraphConfig *graphSettings)
|
|
{
|
|
// \todo cast while the memberi is not IGraphConfig type
|
|
mGraphSettings = static_cast<const GraphConfigNode*>(graphSettings);
|
|
}
|
|
void GraphQueryManager::setGraphDescriptor(const IGraphConfig *graphDescriptor)
|
|
{
|
|
// \todo cast while the memberi is not IGraphConfig type
|
|
mGraphDescriptor = static_cast<const GraphConfigNode*>(graphDescriptor);
|
|
}
|
|
|
|
css_err_t GraphQueryManager::queryGraphs(const GraphQuery& query,
|
|
GraphQueryResult& ret_vec,
|
|
bool strict)
|
|
{
|
|
css_err_t ret;
|
|
strictQuery = strict;
|
|
|
|
// Get all settings level nodes
|
|
GraphConfigNode::gcss_node_vector settingsVector;
|
|
ret = mGraphSettings->getAllDescendants(settingsVector, GCSS_KEY_SETTINGS);
|
|
if (ret != css_err_none) {
|
|
LOGD("Invalid XML. Settings tag has no children.");
|
|
return ret;
|
|
}
|
|
|
|
ret = goThroughSettings(query, settingsVector, ret_vec);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Searches only from the provided search nodes
|
|
* \param query std::map of items to search for
|
|
* \param baseResults search only from inside these settings
|
|
* \param ret this vector is populated with references to matched settings
|
|
* \return css_err_t
|
|
*/
|
|
css_err_t GraphQueryManager::queryGraphs(const GraphQuery& query,
|
|
const GraphQueryResult& baseResults,
|
|
GraphQueryResult& ret_vec,
|
|
bool strict)
|
|
{
|
|
strictQuery = strict;
|
|
return goThroughSettings(query, baseResults, ret_vec);
|
|
}
|
|
|
|
/**
|
|
* Go through every <settings>, and look for matches in search criteria
|
|
*/
|
|
css_err_t GraphQueryManager::goThroughSettings(const GraphQuery &query,
|
|
const GraphQueryResult& settingsNodes,
|
|
GraphQueryResult& ret_vec)
|
|
{
|
|
css_err_t ret;
|
|
for (uint16_t i = 0; i < settingsNodes.size(); i++) {
|
|
|
|
// Search hit counter
|
|
uint16_t result = 0;
|
|
|
|
// Loop through search items and put hit count to result
|
|
ret = goThroughSearchItems(query, settingsNodes[i], result);
|
|
|
|
if (ret != css_err_none) {
|
|
return ret;
|
|
}
|
|
if (!strictQuery && result > 0) {
|
|
ret_vec.push_back(settingsNodes[i]);
|
|
}
|
|
else if (result == query.size()) {
|
|
ret_vec.push_back(settingsNodes[i]);
|
|
}
|
|
}
|
|
|
|
if (ret_vec.size() == 0) {
|
|
LOGE("No settings matching the query found");
|
|
return css_err_general;
|
|
}
|
|
return css_err_none;
|
|
}
|
|
|
|
/**
|
|
* Checks every search item against settings data. If search is strict, we
|
|
* escape as soon as some condition is not found.
|
|
* \param query map of search items
|
|
* \param settingsNode the settings node to search from
|
|
* \param result count of matches
|
|
* \return css_err_t
|
|
*/
|
|
css_err_t GraphQueryManager::goThroughSearchItems(const GraphQuery &query,
|
|
GraphConfigNode *settingsNode,
|
|
uint16_t &result)
|
|
{
|
|
css_err_t ret;
|
|
GraphQuery::const_iterator ite;
|
|
bool allFound = true;
|
|
std::string str_val;
|
|
int int_val = -1;
|
|
for (ite = query.begin(); ite != query.end(); ++ite) {
|
|
|
|
GraphConfigNode* graphNode = settingsNode;
|
|
/**
|
|
* Loop through item uids in the search query. Traverse the tree
|
|
* node by node. Return back to root node when there's dead end or
|
|
* match is found.
|
|
*/
|
|
for (uint16_t ii = 0; ii < ite->first.size(); ii++) {
|
|
|
|
// If at the last uid in vector, check for attribute
|
|
if (ii == ite->first.size() - 1) {
|
|
|
|
// Try to get int
|
|
ret = graphNode->getValue(ite->first[ii], int_val);
|
|
if (ret == css_err_none) {
|
|
str_val = std::to_string(int_val);
|
|
} else {
|
|
// try to get string
|
|
ret = graphNode->getValue(ite->first[ii], str_val);
|
|
if (ret != css_err_none) {
|
|
if (strictQuery)
|
|
allFound = false;
|
|
}
|
|
}
|
|
|
|
// If searched string matches, increment result counter
|
|
if (str_val == ite->second)
|
|
result++;
|
|
|
|
break;
|
|
} else {
|
|
// Get next level node
|
|
ret = graphNode->getDescendant(ite->first[ii], &graphNode);
|
|
|
|
if (ret != css_err_none) {
|
|
// Dead end
|
|
if (strictQuery)
|
|
allFound = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (allFound != true)
|
|
break;
|
|
}
|
|
return css_err_none;
|
|
}
|
|
|
|
/**
|
|
* Builds a graph which is a combination of data in graph descriptor and
|
|
* graph settings. Resulting graph is based on connections defined in graph
|
|
* descriptor's graph node. This function then applies settings for those
|
|
* connections from the given settingsGraph.
|
|
*
|
|
* \param settingsGraph graph node containing the settings data
|
|
* \param results the result graph to return
|
|
*/
|
|
css_err_t GraphQueryManager::getGraph(GraphConfigNode* settingsGraph,
|
|
GraphConfigNode* results)
|
|
{
|
|
css_err_t ret;
|
|
int settingsKey = -1, graphID = -1;
|
|
|
|
if (settingsGraph == nullptr || mGraphDescriptor == nullptr || results == nullptr)
|
|
return css_err_argument;
|
|
|
|
// Get key from the settings and add that to the result graph
|
|
ret = settingsGraph->getValue(GCSS_KEY_KEY, settingsKey);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
|
|
ret = results->addValue(GCSS_KEY_KEY, settingsKey);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
|
|
/* Get graph id from the settings and find corresponding graph node from the
|
|
* graph descriptor */
|
|
ret = settingsGraph->getValue(GCSS_KEY_ID, graphID);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
|
|
/* Get "graphs" node from the descriptor. Use graph id, and enumerate
|
|
through children to find graph that is associated with selected settings.*/
|
|
const IGraphConfig *gcDescriptor = static_cast<const IGraphConfig*>(mGraphDescriptor);
|
|
|
|
IGraphConfig *graphs = gcDescriptor->getDescendant(GCSS_KEY_GRAPHS);
|
|
if (graphs == nullptr)
|
|
return css_err_data;
|
|
|
|
uint32_t descCount = graphs->getDescendantCount();
|
|
IGraphConfig *graphNode = nullptr;
|
|
for (uint32_t i = 0; i < descCount && graphNode == nullptr; i++) {
|
|
graphNode = graphs->iterateDescendantByIndex(GCSS_KEY_ID, ia_uid(graphID), i);
|
|
}
|
|
if (graphNode == nullptr) {
|
|
LOGE("could not find graph with id %d from graph descriptor",
|
|
graphID);
|
|
return css_err_data;
|
|
}
|
|
// \todo casting needed
|
|
GraphConfigNode *gc_graph_node = static_cast<GraphConfigNode*>(graphNode);
|
|
|
|
// Copy graph config graph node
|
|
GraphConfigNode *gc_graph_node_copy = gc_graph_node->copy();
|
|
|
|
if (gc_graph_node_copy == nullptr) {
|
|
LOGE("Error creating GraphConfigNode: No memory");
|
|
return css_err_nomemory;
|
|
}
|
|
|
|
// Add graph node with connections to return tree
|
|
ret = results->insertDescendant(gc_graph_node_copy, GCSS_KEY_GRAPH);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
|
|
/**
|
|
* Loop through all connections and add associated nodes from descriptor
|
|
*
|
|
* \todo if graph has other children than connections, a type attribute check
|
|
* is needed using getDescendant iterator
|
|
*/
|
|
GraphConfigItem::const_iterator it = gc_graph_node->begin();
|
|
for (;it != gc_graph_node->end(); ++it) {
|
|
if (it->second->type != NODE || it->first != GCSS_KEY_CONNECTION)
|
|
continue;
|
|
GraphConfigNode * connection_node = static_cast<GraphConfigNode*>(it->second);
|
|
|
|
// Insert connected nodes to result graph and add the settings
|
|
std::string source_connection;
|
|
std::string sink_connection;
|
|
ret = connection_node->getValue(GCSS_KEY_SOURCE, source_connection);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
ret = connection_node->getValue(GCSS_KEY_SINK, sink_connection);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
string staticConnection;
|
|
ret = connection_node->getValue(GCSS_KEY_STATIC, staticConnection);
|
|
if (ret != css_err_none || staticConnection == "false") {
|
|
ret = getConnectionData(source_connection,
|
|
sink_connection,
|
|
settingsGraph,
|
|
results);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
} else {
|
|
ret = getStaticConnectionData(source_connection,
|
|
sink_connection,
|
|
results);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
|
|
/* apply everything from settings */
|
|
ret = addDescendantsFromNode(results, settingsGraph,
|
|
RELAY_RULE_ADD_NODES
|
|
| RELAY_RULE_PROPAGATE
|
|
| RELAY_RULE_OVERWRITE);
|
|
if (ret != css_err_none) {
|
|
LOGE("Failed to add settings to the result graph");
|
|
return ret;
|
|
}
|
|
|
|
// Get sensor node from settings and get its id
|
|
/** \todo get rid of SENSOR specification in getGraph(), nesting
|
|
* sensor mode data into sensor node based on mode attribute in settings
|
|
* could re-use the common option-list logic. We just have to solve how
|
|
* to provide option lists also via the settings file as option lists are
|
|
* part of descriptor file atm.
|
|
* Now just considering 'sensor' not mandatory node to allow describing
|
|
* partial graphs or graphs with different types of sources.
|
|
*/
|
|
GraphConfigNode * settingsSensorNode;
|
|
ret = settingsGraph->getDescendant(GCSS_KEY_SENSOR, &settingsSensorNode);
|
|
if (ret != css_err_none) {
|
|
LOGW("getGraph didn't find sensor, ignoring sensor modes.");
|
|
return css_err_none;
|
|
}
|
|
|
|
std::string sensorModeID;
|
|
ret = settingsSensorNode->getValue(GCSS_KEY_MODE_ID, sensorModeID);
|
|
if (ret != css_err_none) {
|
|
LOGW("GetGraph failed to set sensor mode. Sensor lacks mode_id");
|
|
return ret;
|
|
}
|
|
|
|
GraphConfigNode * descSensorNode;
|
|
ret = results->getDescendant(GCSS_KEY_SENSOR, &descSensorNode);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
|
|
/**
|
|
* Find sensor modes from settings xml and apply contents from matching
|
|
* sensor mode to the sensor node.
|
|
*/
|
|
GraphConfigNode *sensorModesNode;
|
|
ret = mGraphSettings->getDescendant(GCSS_KEY_SENSOR_MODES, &sensorModesNode);
|
|
if (ret != css_err_none) {
|
|
LOGW("Settings file is missing sensor_modes");
|
|
return ret;
|
|
}
|
|
|
|
ret = addSensorModeData(descSensorNode, sensorModesNode, sensorModeID);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
|
|
return css_err_none;
|
|
}
|
|
/**
|
|
* Populate sensor node with data from sensor mode settings based on id.
|
|
* \param sensorModesNode [in] Node that contains sensor modes
|
|
* \param sensorModeID [in] id of the sensor mode to get
|
|
* \return css_err_t
|
|
*/
|
|
css_err_t
|
|
GraphQueryManager::addSensorModeData(GraphConfigNode *sensorNode,
|
|
GraphConfigNode *sensorModesNode,
|
|
const std::string &sensorModeID)
|
|
{
|
|
css_err_t ret;
|
|
|
|
// Add globals from sensor modes node
|
|
ret = addDescendantsFromNode(sensorNode,
|
|
sensorModesNode,
|
|
RELAY_RULE_OVERWRITE);
|
|
if (ret != css_err_none) {
|
|
LOGE("couldn't add settings from sensor modes node");
|
|
return ret;
|
|
}
|
|
GraphConfigNode * sensorModeNode;
|
|
GraphConfigItem::const_iterator it = sensorModesNode->begin();
|
|
|
|
ret = sensorModesNode->getDescendant(GCSS_KEY_NAME,
|
|
sensorModeID,
|
|
it,
|
|
&sensorModeNode);
|
|
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
|
|
// Add contents from sensor mode to sensor
|
|
return addDescendantsFromNode(sensorNode, sensorModeNode);
|
|
}
|
|
|
|
GraphConfigNode* GraphQueryManager::getPortPeer(GraphConfigNode *portNode)
|
|
{
|
|
GraphConfigNode *root, *retNode = nullptr;
|
|
css_err_t ret;
|
|
|
|
/** \todo check the type once type is of integer attribute */
|
|
/** \todo peer attribute should be of reference type */
|
|
GraphConfigAttribute * peerAttr = nullptr;
|
|
ret = portNode->getAttribute(GCSS_KEY_PEER, &peerAttr);
|
|
if (ret != css_err_none) {
|
|
/* no peer, nowhere to propagate */
|
|
return nullptr;
|
|
}
|
|
assert(peerAttr);
|
|
|
|
std::string peerPortStr;
|
|
ret = peerAttr->getValue(peerPortStr);
|
|
if (ret != css_err_none)
|
|
return nullptr;
|
|
|
|
root = portNode->getRootNode();
|
|
assert(root);
|
|
size_t del = peerPortStr.find(":");
|
|
string peerNode, peerPort;
|
|
if (del == string::npos) {
|
|
/* no delimiter -> virtual sink */
|
|
peerNode = peerPortStr;
|
|
ret = root->getDescendant(ItemUID::str2key(peerNode), &retNode);
|
|
if (ret != css_err_none)
|
|
return nullptr;
|
|
} else {
|
|
peerNode = peerPortStr.substr(0, del);
|
|
peerPort = peerPortStr.substr(del+1);
|
|
ret = root->getDescendant(ItemUID::str2key(peerNode), &retNode);
|
|
if (ret != css_err_none)
|
|
return nullptr;
|
|
ret = retNode->getDescendant(ItemUID::str2key(peerPort), &retNode);
|
|
if (ret != css_err_none)
|
|
return nullptr;
|
|
}
|
|
return retNode;
|
|
}
|
|
/**
|
|
* copy node from descriptor to result node during getConnectionData
|
|
*
|
|
* One of the operations done several times by get graph is copying nodes from
|
|
* the descriptor tree to the new result node tree that we are building
|
|
*
|
|
* This operation checks that if the node is already present in the result node
|
|
* tree we do not add it more than once.
|
|
*
|
|
* \param[in] descriptorNodes Root node from the graph descriptor where all the
|
|
* nodes in the graph are listed. This is the source.
|
|
* \param[in] nodeId Unique id of the node to to copy
|
|
* \param[out] resultNode Root node of the result settings being constructed by
|
|
* getGraph. This is the destination of the copy
|
|
* \return Pointer to the copied node in the result tree
|
|
*/
|
|
IGraphConfig *GraphQueryManager::copyNodeToResult(GraphConfigNode* descriptorNodes,
|
|
ia_uid nodeId,
|
|
GraphConfigNode* resultNode)
|
|
{
|
|
css_err_t ret = css_err_none;
|
|
IGraphConfig *descNode = descriptorNodes->getDescendant(nodeId);
|
|
if (!descNode) {
|
|
LOGE("Node(%s) not found from descriptor",
|
|
ItemUID::key2str(nodeId));
|
|
return nullptr;
|
|
}
|
|
IGraphConfig *outNode = resultNode->getDescendant(nodeId);
|
|
if (!outNode) {
|
|
ret = resultNode->insertDescendant(
|
|
static_cast<GraphConfigNode*>(descNode)->copy(),
|
|
nodeId);
|
|
if (ret != css_err_none)
|
|
return nullptr;
|
|
outNode = resultNode->getDescendant(nodeId);
|
|
assert(outNode);
|
|
}
|
|
return outNode;
|
|
}
|
|
/**
|
|
* Takes source and destination strings for a connection and looks for the
|
|
* named nodes from the graph descriptor, it then copies the nodes to the
|
|
* result tree and populates the corresponding values from the settings.
|
|
*
|
|
* This method is mainly used during get graph to construct the combined
|
|
* settings + graph information.
|
|
*
|
|
* \param[in] source_connection Contains the connection source string
|
|
* \param[in] sink_connection Contains the connection sink string
|
|
* \param[in] settings It has the container node for settings
|
|
* \param[out] ret_node The root node with the results of get graph that we are
|
|
* building
|
|
* \return css_err_t
|
|
*/
|
|
css_err_t
|
|
GraphQueryManager::getConnectionData(
|
|
const std::string& source_connection,
|
|
const std::string& sink_connection,
|
|
GraphConfigNode *settings,
|
|
GraphConfigNode *ret_node)
|
|
{
|
|
GraphConfigNode *nodes = nullptr;
|
|
css_err_t ret = css_err_general;
|
|
string set_sink_connection = sink_connection;
|
|
|
|
if (ret_node == nullptr)
|
|
return css_err_argument;
|
|
|
|
ret = mGraphDescriptor->getDescendant(GCSS_KEY_NODES, &nodes);
|
|
if (ret != css_err_none) {
|
|
LOGE("Error, graph_descriptor does not have a 'nodes' node");
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Split sink/source values (node:port) to find the node name and
|
|
* the port name.
|
|
* Handle special cases: virtual sinks and sources.
|
|
* Virtual sink is one end point of the graph.
|
|
* Virtual source is a buffer source that inject buffers to the graph.
|
|
* In those cases the port name is left empty and the uid set to
|
|
* GCSS_KEY_NA
|
|
*/
|
|
std::string src_node_name;
|
|
std::string src_port_name;
|
|
std::size_t pos = source_connection.find(":");
|
|
if (pos == string::npos) {
|
|
src_node_name = source_connection;
|
|
} else {
|
|
src_node_name = source_connection.substr(0, pos);
|
|
src_port_name = source_connection.substr(pos+1);
|
|
}
|
|
|
|
pos = sink_connection.find(":");
|
|
std::string dst_node_name;
|
|
std::string dst_port_name;
|
|
if (pos == string::npos) {
|
|
dst_node_name = sink_connection;
|
|
} else {
|
|
dst_node_name = sink_connection.substr(0, pos);
|
|
dst_port_name = sink_connection.substr(pos+1);
|
|
}
|
|
|
|
ia_uid src_node_uid = ItemUID::str2key(src_node_name);
|
|
ia_uid dst_node_uid = ItemUID::str2key(dst_node_name);
|
|
ia_uid src_port_uid = ItemUID::str2key(src_port_name);
|
|
ia_uid dst_port_uid = ItemUID::str2key(dst_port_name);
|
|
|
|
/* Handle source : */
|
|
/* source settings missing implicitly mean that source is not part of
|
|
* the active graph or does not need settings */
|
|
IGraphConfig *setSrcNode = settings->getDescendant(src_node_uid);
|
|
if (!setSrcNode) {
|
|
LOGW("Node(%s) not found from settings, ignoring connection",
|
|
src_node_name.c_str());
|
|
return css_err_none;
|
|
}
|
|
|
|
IGraphConfig *setSrcPort = nullptr;
|
|
if (src_port_uid == GCSS_KEY_NA) {
|
|
// in case of virtual source the node is treated as a port
|
|
setSrcPort = setSrcNode;
|
|
} else {
|
|
setSrcPort = setSrcNode->getDescendant(src_port_uid);
|
|
if (!setSrcPort) {
|
|
LOGW("Node(%s) port %s not found from settings",
|
|
src_node_name.c_str(), src_port_name.c_str());
|
|
return css_err_none;
|
|
}
|
|
}
|
|
|
|
/* Copy src node from descriptor nodes to result */
|
|
IGraphConfig *outSrcNode = copyNodeToResult(nodes, src_node_uid, ret_node);
|
|
if (outSrcNode == nullptr) {
|
|
LOGE("Failed to copy src node(%s) from descriptor to settings",
|
|
src_node_name.c_str());
|
|
return css_err_general;
|
|
}
|
|
/*
|
|
* A src port is disabled only if the attribute enabled is present and set
|
|
* to 0, if the attribute is not present it is assumed that the port is
|
|
* enabled.
|
|
*/
|
|
int32_t is_src_port_enabled;
|
|
ret = setSrcPort->getValue(GCSS_KEY_ENABLED, is_src_port_enabled);
|
|
if (ret == css_err_none && !is_src_port_enabled) {
|
|
// src port disabled skip connection processing
|
|
LOGD("Src port %s disabled, skip dst and peer processing",
|
|
src_node_name.c_str());
|
|
return css_err_none;
|
|
}
|
|
|
|
/* For virtual sinks check if source settings explicitly redefine the
|
|
* connection to sink. If not just take the sink defined by graph.
|
|
* If virtual sink is not in settings then ignore. All active virtual
|
|
* sinks should appear in the settings
|
|
*/
|
|
if (dst_port_uid == GCSS_KEY_NA) {
|
|
|
|
ret = setSrcPort->getValue(GCSS_KEY_PEER, set_sink_connection);
|
|
if (ret != css_err_none) {
|
|
LOGD("Using default connection %s",
|
|
source_connection.c_str());
|
|
} else {
|
|
// override the destination node uid
|
|
dst_node_uid = ItemUID::str2key(set_sink_connection);
|
|
LOGD("Overriding destination node %s with %s",
|
|
dst_node_name.c_str(),set_sink_connection.c_str());
|
|
}
|
|
IGraphConfig *setDstNode = settings->getDescendant(dst_node_uid);
|
|
if (setDstNode == nullptr) {
|
|
LOGD("Ignoring node %s for not being in settings",
|
|
dst_node_name.c_str());
|
|
return css_err_none;
|
|
}
|
|
//add for rockchip, zyc
|
|
int32_t is_dst_port_enabled;
|
|
ret = setDstNode->getValue(GCSS_KEY_ENABLED, is_dst_port_enabled);
|
|
if (ret == css_err_none && !is_dst_port_enabled) {
|
|
// dst port disabled skip connection processing
|
|
LOGD("Dst port %s disabled, skip dst and peer processing",
|
|
dst_node_name.c_str());
|
|
return css_err_none;
|
|
}
|
|
}
|
|
|
|
/* Handle sink : copy sink node to results */
|
|
IGraphConfig *outDstNode = copyNodeToResult(nodes, dst_node_uid, ret_node);
|
|
if (outDstNode == nullptr) {
|
|
LOGE("Failed to copy dst node(%s) from descriptor to settings",
|
|
dst_node_name.c_str());
|
|
return css_err_general;
|
|
}
|
|
|
|
if (dst_port_uid == GCSS_KEY_NA) {
|
|
/* for virtual sinks we add direction input as if they were ports */
|
|
ret = static_cast<GraphConfigNode*>(outDstNode)
|
|
->addValue(GCSS_KEY_DIRECTION, 0);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
}
|
|
|
|
IGraphConfig *outSrcPort;
|
|
if (src_port_uid == GCSS_KEY_NA) {
|
|
// in case of virtual source the node is treated as a port
|
|
outSrcPort = outSrcNode;
|
|
} else {
|
|
outSrcPort = outSrcNode->getDescendant(src_port_uid);
|
|
if (!outSrcPort) {
|
|
LOGE("Node(%s) has no port named '%s'",
|
|
src_node_name.c_str(),
|
|
src_port_name.c_str());
|
|
return css_err_general;
|
|
}
|
|
}
|
|
|
|
IGraphConfig *outDstPort;
|
|
if (dst_port_uid == GCSS_KEY_NA) {
|
|
/* in case of virtual sink, the node is considered as a port */
|
|
outDstPort = outDstNode;
|
|
|
|
string type;
|
|
ret = outDstPort->getValue(GCSS_KEY_TYPE, type);
|
|
if (ret != css_err_none) {
|
|
LOGE("No type for connected peer node");
|
|
return ret;
|
|
}
|
|
|
|
if (type.compare("sink") != 0) {
|
|
LOGE("sink connection attribute without port '%s' not pointing to virtual sink", type.c_str());
|
|
return css_err_general;
|
|
}
|
|
|
|
/** \todo WORKAROUND[START]: add stream_id's to every 'sink' */
|
|
int32_t stream_id;
|
|
/* check if already set */
|
|
ret = outDstPort->getValue(GCSS_KEY_STREAM_ID, stream_id);
|
|
if (ret != css_err_none) {
|
|
/* read stream id from ancestor of the port that we are propagating */
|
|
ret = outSrcNode->getValue(GCSS_KEY_STREAM_ID, stream_id);
|
|
if (ret != css_err_none) {
|
|
LOGE("No stream_id set for connected peer");
|
|
return ret;
|
|
}
|
|
ret = static_cast<GraphConfigNode*>(outDstPort)
|
|
->addValue(GCSS_KEY_STREAM_ID, stream_id);
|
|
if (ret != css_err_none) {
|
|
LOGE("Failed to add stream ID to sink peer");
|
|
return ret;
|
|
}
|
|
}
|
|
/* WORKAROUND[END] */
|
|
} else {
|
|
outDstPort = outDstNode->getDescendant(dst_port_uid);
|
|
if (!outDstPort) {
|
|
LOGE("Node(%s) has no port named '%s'",
|
|
dst_node_name.c_str(),
|
|
dst_port_name.c_str());
|
|
return css_err_general;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Source and destination are valid.
|
|
* Set the peer attribute for each of the ends of the connection.
|
|
*/
|
|
std::string peerStr;
|
|
GraphConfigStrAttribute * peerAttribute;
|
|
ret = outSrcPort->getValue(GCSS_KEY_PEER, peerStr);
|
|
if (ret == css_err_none) {
|
|
LOGD("Node(%s) port '%s' already connected once to '%s'",
|
|
src_node_name.c_str(),
|
|
src_port_name.c_str(),
|
|
peerStr.c_str());
|
|
}
|
|
// even the src port is already in another connection,
|
|
// Allow port to have multiple connections.
|
|
peerAttribute = new GraphConfigStrAttribute;
|
|
if (peerAttribute == nullptr)
|
|
return css_err_nomemory;
|
|
peerAttribute->setValue(set_sink_connection);
|
|
ret = static_cast<GraphConfigNode*>(outSrcPort)
|
|
->insertDescendant(peerAttribute, GCSS_KEY_PEER);
|
|
if (ret != css_err_none) {
|
|
delete peerAttribute;
|
|
return ret;
|
|
}
|
|
|
|
ret = outDstPort->getValue(GCSS_KEY_PEER, peerStr);
|
|
if (ret != css_err_none) {
|
|
ret = static_cast<GraphConfigNode*>(outDstPort)
|
|
->addValue(GCSS_KEY_PEER, source_connection);
|
|
if (ret != css_err_none) {
|
|
return ret;
|
|
}
|
|
} else {
|
|
LOGD("Node(%s) port '%s' already connected once to '%s' adding new peer",
|
|
dst_node_name.c_str(),
|
|
dst_port_name.c_str(),
|
|
peerStr.c_str());
|
|
// even the dst port is already in another connection,
|
|
// Allow port to have multiple connections.
|
|
peerAttribute = new GraphConfigStrAttribute;
|
|
if (peerAttribute == nullptr)
|
|
return css_err_nomemory;
|
|
peerAttribute->setValue(source_connection);
|
|
ret = static_cast<GraphConfigNode*>(outDstPort)
|
|
->insertDescendant(peerAttribute, GCSS_KEY_PEER);
|
|
if (ret != css_err_none) {
|
|
delete peerAttribute;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return css_err_none;
|
|
}
|
|
|
|
/**
|
|
* Handler of static connections
|
|
*
|
|
* Static connections propagate the nodes to the combined settings (output of
|
|
* getGraph, here also referred as the result) no matter what is the content of
|
|
* the settings.
|
|
*
|
|
* For connections with sinks as destination, it propagates the following
|
|
* properties from the source port to the sink:
|
|
* - EXEC_CTX_ID
|
|
* - CONTENT_TYPE
|
|
*
|
|
* Like regular connections it also sets the PEER attribute correctly in each
|
|
* end-node of the connection.
|
|
*/
|
|
css_err_t
|
|
GraphQueryManager::getStaticConnectionData(
|
|
const std::string& source_connection,
|
|
const std::string& sink_connection,
|
|
GraphConfigNode *ret_node)
|
|
{
|
|
GraphConfigNode *nodes = nullptr;
|
|
css_err_t ret = css_err_general;
|
|
string set_sink_connection = sink_connection;
|
|
|
|
if (ret_node == nullptr)
|
|
return css_err_argument;
|
|
|
|
ret = mGraphDescriptor->getDescendant(GCSS_KEY_NODES, &nodes);
|
|
if (ret != css_err_none) {
|
|
LOGE("Error, graph_descriptor does not have a 'nodes' node");
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Split sink/source values (node:port) to find the node name and
|
|
* the port name.
|
|
* Handle special cases: virtual sinks and sources.
|
|
* Virtual sink is one end point of the graph.
|
|
* Virtual source is a buffer source that inject buffers to the graph.
|
|
* In those cases the port name is left empty and the uid set to
|
|
* GCSS_KEY_NA
|
|
*/
|
|
std::string src_node_name;
|
|
std::string src_port_name;
|
|
std::size_t pos = source_connection.find(":");
|
|
if (pos == string::npos) {
|
|
src_node_name = source_connection;
|
|
} else {
|
|
src_node_name = source_connection.substr(0, pos);
|
|
src_port_name = source_connection.substr(pos+1);
|
|
}
|
|
|
|
pos = sink_connection.find(":");
|
|
std::string dst_node_name;
|
|
std::string dst_port_name;
|
|
if (pos == string::npos) {
|
|
dst_node_name = sink_connection;
|
|
} else {
|
|
dst_node_name = sink_connection.substr(0, pos);
|
|
dst_port_name = sink_connection.substr(pos+1);
|
|
}
|
|
|
|
ia_uid src_node_uid = ItemUID::str2key(src_node_name);
|
|
ia_uid dst_node_uid = ItemUID::str2key(dst_node_name);
|
|
ia_uid src_port_uid = ItemUID::str2key(src_port_name);
|
|
ia_uid dst_port_uid = ItemUID::str2key(dst_port_name);
|
|
|
|
/* Handle source : */
|
|
|
|
/* Copy src node from descriptor nodes to result */
|
|
IGraphConfig *outSrcNode = copyNodeToResult(nodes, src_node_uid, ret_node);
|
|
if (outSrcNode == nullptr) {
|
|
LOGE("Failed to copy src node(%s) from descriptor to settings",
|
|
src_node_name.c_str());
|
|
return css_err_general;
|
|
}
|
|
|
|
/* Handle sink : copy sink node to results */
|
|
IGraphConfig *outDstNode = copyNodeToResult(nodes, dst_node_uid, ret_node);
|
|
if (outDstNode == nullptr) {
|
|
LOGE("Failed to copy dst node(%s) from descriptor to settings",
|
|
dst_node_name.c_str());
|
|
return css_err_general;
|
|
}
|
|
|
|
if (dst_port_uid == GCSS_KEY_NA) {
|
|
/* for virtual sinks we add direction input as if they were ports */
|
|
ret = static_cast<GraphConfigNode*>(outDstNode)
|
|
->addValue(GCSS_KEY_DIRECTION, 0);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
}
|
|
|
|
IGraphConfig *outSrcPort;
|
|
if (src_port_uid == GCSS_KEY_NA) {
|
|
// in case of virtual source the node is treated as a port
|
|
outSrcPort = outSrcNode;
|
|
} else {
|
|
outSrcPort = outSrcNode->getDescendant(src_port_uid);
|
|
if (!outSrcPort) {
|
|
LOGE("Node(%s) has no port named '%s'",
|
|
src_node_name.c_str(),
|
|
src_port_name.c_str());
|
|
return css_err_general;
|
|
}
|
|
}
|
|
|
|
IGraphConfig *outDstPort;
|
|
if (dst_port_uid == GCSS_KEY_NA) {
|
|
/* in case of virtual sink, the node is considered as a port */
|
|
outDstPort = outDstNode;
|
|
/* Propagate the execution context of the src node into the sink */
|
|
propagateIntAttribute(outSrcNode, outDstNode, GCSS_KEY_EXEC_CTX_ID);
|
|
|
|
/* Propagate the content_type of the src node into the sink */
|
|
propagateStrAttribute(outSrcNode, outDstNode, GCSS_KEY_CONTENT_TYPE);
|
|
} else {
|
|
outDstPort = outDstNode->getDescendant(dst_port_uid);
|
|
if (!outDstPort) {
|
|
LOGE("Node(%s) has no port named '%s'",
|
|
dst_node_name.c_str(),
|
|
dst_port_name.c_str());
|
|
return css_err_general;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Source and destination are valid.
|
|
* Set the peer attribute for each of the ends of the connection.
|
|
*/
|
|
std::string peerStr;
|
|
GraphConfigStrAttribute * peerAttribute;
|
|
ret = outSrcPort->getValue(GCSS_KEY_PEER, peerStr);
|
|
if (ret == css_err_none) {
|
|
LOGD("Node(%s) port '%s' already connected once to '%s'",
|
|
src_node_name.c_str(),
|
|
src_port_name.c_str(),
|
|
peerStr.c_str());
|
|
}
|
|
// even the src port is already in another connection,
|
|
// Allow port to have multiple connections.
|
|
peerAttribute = new GraphConfigStrAttribute;
|
|
if (peerAttribute == nullptr)
|
|
return css_err_nomemory;
|
|
peerAttribute->setValue(set_sink_connection);
|
|
ret = static_cast<GraphConfigNode*>(outSrcPort)
|
|
->insertDescendant(peerAttribute, GCSS_KEY_PEER);
|
|
if (ret != css_err_none) {
|
|
delete peerAttribute;
|
|
return ret;
|
|
}
|
|
|
|
ret = outDstPort->getValue(GCSS_KEY_PEER, peerStr);
|
|
if (ret != css_err_none) {
|
|
ret = static_cast<GraphConfigNode*>(outDstPort)
|
|
->addValue(GCSS_KEY_PEER, source_connection);
|
|
if (ret != css_err_none) {
|
|
return ret;
|
|
}
|
|
} else {
|
|
LOGD("Node(%s) port '%s' already connected once to '%s' adding new peer",
|
|
dst_node_name.c_str(),
|
|
dst_port_name.c_str(),
|
|
peerStr.c_str());
|
|
// even the dst port is already in another connection,
|
|
// Allow port to have multiple connections.
|
|
peerAttribute = new GraphConfigStrAttribute;
|
|
if (peerAttribute == nullptr)
|
|
return css_err_nomemory;
|
|
peerAttribute->setValue(source_connection);
|
|
ret = static_cast<GraphConfigNode*>(outDstPort)
|
|
->insertDescendant(peerAttribute, GCSS_KEY_PEER);
|
|
if (ret != css_err_none) {
|
|
delete peerAttribute;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return css_err_none;
|
|
}
|
|
css_err_t GraphQueryManager::handleAttributeOptions(GraphConfigNode *node,
|
|
ia_uid attribute_key,
|
|
const string &newValue)
|
|
{
|
|
css_err_t ret;
|
|
|
|
/* find if ite->second has an option list */
|
|
GraphConfigNode *opNode = nullptr;
|
|
GraphConfigNode::const_iterator op_ite;
|
|
for (op_ite = node->begin(); op_ite != node->end(); ++op_ite) {
|
|
/** \todo cannot use generic getDescendant overloads for searh because
|
|
* need to ensure that returned node is one of options */
|
|
if (op_ite->second->type != NODE)
|
|
continue;
|
|
if (op_ite->first != GCSS_KEY_OPTIONS)
|
|
continue;
|
|
css_err_t ret = node->iterateAttributes(GCSS_KEY_ATTRIBUTE,
|
|
(int)attribute_key,
|
|
op_ite);
|
|
if (ret == css_err_none) {
|
|
opNode = static_cast<GraphConfigNode*>(op_ite->second);
|
|
break;
|
|
}
|
|
}
|
|
if (!opNode)
|
|
return css_err_noentry;
|
|
// select option where value matches newValue
|
|
op_ite = opNode->begin();
|
|
ret = opNode->getDescendant(GCSS_KEY_VALUE, newValue, op_ite, &opNode);
|
|
if (ret != css_err_none) {
|
|
LOGE("Failed to find attribute value '%s' from its option list", newValue.c_str());
|
|
return ret;
|
|
}
|
|
ret = opNode->getDescendant(GCSS_KEY_APPLY, &opNode);
|
|
if (ret != css_err_none)
|
|
return css_err_none;
|
|
|
|
ret = addDescendantsFromNode(node->getRootNode(),
|
|
opNode,
|
|
RELAY_RULE_OVERWRITE);
|
|
if (ret != css_err_none) {
|
|
LOGE("Failed to apply option attributes");
|
|
return ret;
|
|
}
|
|
return css_err_none;
|
|
}
|
|
|
|
/**
|
|
* Populate object with children of the param node
|
|
*/
|
|
css_err_t GraphQueryManager::addDescendantsFromNode(
|
|
GraphConfigNode* to,
|
|
GraphConfigNode* from,
|
|
Rule rr)
|
|
{
|
|
GraphConfigItem::gcss_item_map::const_iterator ite;
|
|
css_err_t ret;
|
|
|
|
if (!to || ! from)
|
|
return css_err_argument;
|
|
|
|
for (ite = from->item.begin(); ite != from->item.end(); ++ite) {
|
|
|
|
GraphConfigNode * gc_settings_node = nullptr;
|
|
// traverse to descendant if it already exists
|
|
ret = to->getDescendant(ite->first, &gc_settings_node);
|
|
if (ret == css_err_none) {
|
|
/* never overwrite options */
|
|
if (!(rr & RELAY_RULE_OVERWRITE)
|
|
|| ite->first == GCSS_KEY_OPTIONS)
|
|
continue;
|
|
ret = addDescendantsFromNode(
|
|
gc_settings_node,
|
|
static_cast<GraphConfigNode*>(ite->second),
|
|
rr);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
continue;
|
|
}
|
|
|
|
/* If attribute exists, update descriptor value from the settings,
|
|
* otherwise copy the node from descriptor to the results. */
|
|
std::string newValueStr;
|
|
GraphConfigAttribute * resultsAttr = nullptr;
|
|
if(ite->second->type == INT_ATTRIBUTE) {
|
|
int newValue = 0;
|
|
ret = to->getAttribute(ite->first, &resultsAttr);
|
|
if (ret == css_err_none) {
|
|
// Do not overwrite if id
|
|
if (!(rr & RELAY_RULE_OVERWRITE)
|
|
|| ite->first == GCSS_KEY_ID
|
|
|| ite->first == GCSS_KEY_DIRECTION)
|
|
continue;
|
|
ite->second->getValue(newValue);
|
|
resultsAttr->setValue(newValue);
|
|
if (rr & RELAY_RULE_HANDLE_OPTIONS) {
|
|
newValueStr = to_string(newValue);
|
|
ret = handleAttributeOptions(to, ite->first, newValueStr);
|
|
if (ret != css_err_none && ret != css_err_noentry)
|
|
return ret;
|
|
}
|
|
continue;
|
|
}
|
|
resultsAttr =
|
|
static_cast<GraphConfigIntAttribute*>(ite->second)->copy();
|
|
if (resultsAttr == nullptr) {
|
|
LOGE("Error creating GraphConfigItem: No memory");
|
|
return css_err_nomemory;
|
|
}
|
|
if (rr & RELAY_RULE_HANDLE_OPTIONS) {
|
|
ret = resultsAttr->getValue(newValue);
|
|
if (ret != css_err_none) {
|
|
delete resultsAttr;
|
|
return ret;
|
|
}
|
|
newValueStr = to_string(newValue);
|
|
ret = handleAttributeOptions(to, ite->first, newValueStr);
|
|
if (ret != css_err_none && ret != css_err_noentry) {
|
|
delete resultsAttr;
|
|
return ret;
|
|
}
|
|
}
|
|
to->insertDescendant(resultsAttr, ite->first);
|
|
} else if (ite->second->type == STR_ATTRIBUTE) {
|
|
ret = to->getAttribute(ite->first, &resultsAttr);
|
|
if(ret == css_err_none) {
|
|
// Do not overwrite if name or type
|
|
if (!(rr & RELAY_RULE_OVERWRITE)
|
|
|| ite->first == GCSS_KEY_NAME
|
|
|| ite->first == GCSS_KEY_TYPE
|
|
|| ite->first == GCSS_KEY_PEER)
|
|
continue;
|
|
ite->second->getValue(newValueStr);
|
|
resultsAttr->setValue(newValueStr);
|
|
if (rr & RELAY_RULE_HANDLE_OPTIONS) {
|
|
ret = handleAttributeOptions(to, ite->first, newValueStr);
|
|
if (ret != css_err_none && ret != css_err_noentry)
|
|
return ret;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
resultsAttr =
|
|
static_cast<GraphConfigStrAttribute*>(ite->second)->copy();
|
|
if (resultsAttr == nullptr) {
|
|
LOGE("Error creating GraphConfigItem: No memory");
|
|
return css_err_nomemory;
|
|
}
|
|
if (rr & RELAY_RULE_HANDLE_OPTIONS) {
|
|
ret = resultsAttr->getValue(newValueStr);
|
|
if (ret != css_err_none) {
|
|
delete resultsAttr;
|
|
return ret;
|
|
}
|
|
ret = handleAttributeOptions(to, ite->first, newValueStr);
|
|
if (ret != css_err_none && ret != css_err_noentry) {
|
|
delete resultsAttr;
|
|
return ret;
|
|
}
|
|
}
|
|
to->insertDescendant(resultsAttr, ite->first);
|
|
} else if (rr & RELAY_RULE_ADD_NODES) {
|
|
GraphConfigNode * results_node =
|
|
static_cast<GraphConfigNode*>(ite->second)->copy();
|
|
if (results_node == nullptr) {
|
|
LOGE("Error creating GraphConfigItem: No memory");
|
|
return css_err_nomemory;
|
|
}
|
|
to->insertDescendant(results_node, ite->first);
|
|
}
|
|
}
|
|
|
|
/* propagate between connected ports */
|
|
if (rr & RELAY_RULE_PROPAGATE) {
|
|
/* only propagate to downstream when mandatory direction attribute is
|
|
* set */
|
|
int32_t direction;
|
|
ret = to->getValue(GCSS_KEY_DIRECTION, direction);
|
|
if (ret != css_err_none)
|
|
return css_err_none;
|
|
else if (direction != 1)
|
|
return css_err_none;
|
|
|
|
GraphConfigNode* peerNode = GraphQueryManager::getPortPeer(to);
|
|
if (!peerNode)
|
|
return css_err_none;
|
|
|
|
/** \todo WORKAROUND: don't overwrite when propagating to virtual sinks
|
|
* Currently settings are missing explicit way to tell difference
|
|
* between queried dimension and actual buffer dimenssions.
|
|
*/
|
|
string type;
|
|
Rule rrPropagate = RELAY_RULE_ADD_NODES;
|
|
ret = peerNode->getValue(GCSS_KEY_TYPE, type);
|
|
if (ret != css_err_none || type.compare("sink") != 0)
|
|
rrPropagate = RELAY_RULE_OVERWRITE;
|
|
|
|
/* Note: using 'to' as source instead of 'from' (settings) in order to
|
|
* propagate also the option lists */
|
|
ret = addDescendantsFromNode(peerNode, to, rrPropagate);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
|
|
}
|
|
|
|
return css_err_none;
|
|
}
|
|
|
|
/**
|
|
* Propagate an integer attribute from one node to another
|
|
*
|
|
* The method checks if the attribute is already present in the destination
|
|
* node. If it is it just updates the value with the value from the source node.
|
|
* If it is not present, it inserts a new attribute.
|
|
*
|
|
* \param[in] srcNode Source node to take the the attribute value
|
|
* \param[in] dstNode Destination node that will receive the the attribute value
|
|
* \param[in] attributeId The id for the attribute (like ex: GCSS_KEY_TYPE)
|
|
*
|
|
* \returns css_err_none if everything went fine.
|
|
* \returns css_err_nomemory if the allocation of the new attribute failed.
|
|
* \returns css_err_general if the attribute id is not of the expected type.
|
|
*/
|
|
css_err_t GraphQueryManager::propagateIntAttribute(IGraphConfig *srcNode,
|
|
IGraphConfig *dstNode,
|
|
ia_uid attributeId)
|
|
{
|
|
int32_t value = -1;
|
|
css_err_t ret = css_err_none;
|
|
bool update = false;
|
|
/* check first if the value is already present in the destination */
|
|
ret = dstNode->getValue(attributeId, value);
|
|
if (ret == css_err_none) {
|
|
// we only need to update it
|
|
update = true;
|
|
}
|
|
/* acquire the value from the src */
|
|
ret = srcNode->getValue(attributeId, value);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
|
|
/* update or insert depending */
|
|
if (!update) {
|
|
GraphConfigIntAttribute *attr = new GraphConfigIntAttribute;
|
|
if (attr == nullptr)
|
|
return css_err_nomemory;
|
|
|
|
ret = attr->setValue(value);
|
|
if (ret != css_err_none) {
|
|
delete attr;
|
|
return ret;
|
|
}
|
|
ret = static_cast<GraphConfigNode*>(dstNode)
|
|
->insertDescendant(attr, attributeId);
|
|
if (ret != css_err_none)
|
|
delete attr;
|
|
} else {
|
|
dstNode->setValue(attributeId, value);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Propagate an string attribute from one node to another
|
|
*
|
|
* The method checks if the attribute is already present in the destination
|
|
* node. If it is it just updates the value with the value from the source node.
|
|
* If it is not present, it inserts a new attribute.
|
|
*
|
|
* \param[in] srcNode Source node to take the the attribute value
|
|
* \param[in] dstNode Destination node that will receive the the attribute value
|
|
* \param[in] attributeId The id for the attribute (like ex: GCSS_KEY_TYPE)
|
|
*
|
|
* \returns css_err_none if everything went fine.
|
|
* \returns css_err_nomemory if the allocation of the new attribute failed.
|
|
* \returns css_err_general if the attribute id is not of the expected type.
|
|
*/
|
|
css_err_t GraphQueryManager::propagateStrAttribute(IGraphConfig *srcNode,
|
|
IGraphConfig *dstNode,
|
|
ia_uid attributeId)
|
|
{
|
|
string value;
|
|
css_err_t ret = css_err_none;
|
|
bool update = false;
|
|
/* check first if the value is already present in the destination */
|
|
ret = dstNode->getValue(attributeId, value);
|
|
if (ret == css_err_none) {
|
|
// we only need to update it
|
|
update = true;
|
|
}
|
|
/* acquire the value from the src */
|
|
ret = srcNode->getValue(attributeId, value);
|
|
if (ret != css_err_none)
|
|
return ret;
|
|
|
|
/* update or insert depending */
|
|
if (!update) {
|
|
GraphConfigStrAttribute *attr = new GraphConfigStrAttribute;
|
|
if (attr == nullptr)
|
|
return css_err_nomemory;
|
|
|
|
ret = attr->setValue(value);
|
|
if (ret != css_err_none) {
|
|
delete attr;
|
|
return ret;
|
|
}
|
|
ret = static_cast<GraphConfigNode*>(dstNode)
|
|
->insertDescendant(attr, attributeId);
|
|
if (ret != css_err_none)
|
|
delete attr;
|
|
} else {
|
|
dstNode->setValue(attributeId, value);
|
|
}
|
|
return ret;
|
|
}
|
|
|