// ATTN: This function seriously needs re-organization, will be done // shortly. rw_yang_netconf_op_status_t XMLDocMerger::do_edit(XMLNode* new_node, XMLNode* delta_node, XMLNode* update_node, bool* update_required) { rw_yang_netconf_op_status_t status = RW_YANG_NETCONF_OP_STATUS_OK; YangNode* ynode = new_node->get_descend_yang_node(); XMLEditDefaultOperation parent_op = current_operation_; for(XMLNodeIter it = delta_node->child_begin(); it != delta_node->child_end(); ++it) { XMLNode* delta_child = &(*it); std::string child_name = delta_child->get_local_name(); std::string child_ns = delta_child->get_name_space(); YangNode* child_ynode = ynode->search_child(child_name.c_str(), child_ns.c_str()); if (child_ynode == nullptr) { // Incoming node is not in our model and thus is an error std::string const err_msg = "Cannot find child ("+child_name+") of node ("+delta_node->get_local_name()+")"; report_error(delta_node, RW_YANG_NETCONF_OP_STATUS_INVALID_VALUE, err_msg.c_str()); return RW_YANG_NETCONF_OP_STATUS_INVALID_VALUE; } // Set the current node operation (default=merge) status = set_current_operation(delta_child); if (status != RW_YANG_NETCONF_OP_STATUS_OK) { return status; } bool subtree_update = false; XMLNode* new_child = new_node->find(child_name.c_str(), child_ns.c_str()); if (new_child == nullptr) { // Node not found in existing config, edit-config on new node status = do_edit_new(child_ynode, new_node, delta_child, update_node, &subtree_update); } else { status = do_edit_existing(child_ynode, new_node, new_child, delta_child, update_node, &subtree_update); } *update_required = (*update_required || subtree_update); if (status != RW_YANG_NETCONF_OP_STATUS_OK) { return status; } current_operation_ = parent_op; } if (current_operation_ == XML_EDIT_OP_REPLACE) { // Iterate thru the config dom node and find the elements not in delta // Those are marked for deletion. do_delete_missing(new_node, delta_node); } // Add defaults fill_defaults(ynode, new_node, update_node, update_required); if (!(*update_required)) { // No updates on this subtree. Either it is delete/remove operation or // the config is not changed. So remove the update subtree. XMLNode* parent = update_node->get_parent(); if (parent) { parent->remove_child(update_node); } } if (new_node->get_first_child() == nullptr && ynode->get_stmt_type() == RW_YANG_STMT_TYPE_CONTAINER && !ynode->is_presence()) { // No children for a non-presence container, they are only present to // maintain hierarchy. Remove it XMLNode* parent = new_node->get_parent(); if (parent) { parent->remove_child(new_node); } } return status; }
void XMLDocMerger::fill_defaults( YangNode* ynode, XMLNode* new_node, XMLNode* update_node, bool* update_required ) { YangNode* yn = nullptr; XMLNode* xchild = nullptr; for (YangNodeIter it = ynode->child_begin(); it != ynode->child_end(); ++it) { yn = &(*it); const char* default_val = yn->get_default_value(); if (!default_val && !yn->has_default()) { // Only default values or containers with default leaf descendents continue; } // Default values may be within a choice/case. Check if the choice has // another case. If the same case is present YangNode* ychoice = yn->get_choice(); YangNode* ycase = yn->get_case(); YangNode* other_choice = nullptr; YangNode* other_case = nullptr; bool add_default = true; bool subtree_update = false; if (ychoice && ycase && (ychoice->get_default_case() != ycase)) { add_default = false; } for (XMLNodeIter xit = new_node->child_begin(); xit != new_node->child_end(); ++xit) { xchild = &(*xit); if (xchild->get_local_name() == yn->get_name() && xchild->get_name_space() == yn->get_ns()) { if (yn->get_stmt_type() == RW_YANG_STMT_TYPE_CONTAINER) { // The container has a default descendant node XMLNode* update_child = nullptr; bool created = false; if ((update_child = update_node->find( yn->get_name(), yn->get_ns())) == nullptr) { update_child = update_node->add_child(yn); created = true; } fill_defaults(yn, xchild, update_child, &subtree_update); if (!subtree_update && created) { update_node->remove_child(update_child); } *update_required = (*update_required || subtree_update); } // Default node already present in the new-dom add_default = false; break; } if (!ychoice) { // Not part of a choice continue; } other_choice = xchild->get_yang_node()->get_choice(); if (!other_choice || (other_choice != ychoice)) { // Other node is not a choice, or not the same choice, not conflicting continue; } other_case = xchild->get_yang_node()->get_case(); if (other_case && (ycase != other_case)) { // There is a node with conflicting case. Hence no default add_default = false; break; } // Same case, in-case the case is not default, some-other node in the same // case is set. Then add this default, unless the same node is found in // the new-dom. add_default = true; } if (add_default) { XMLNode* xn = new_node->add_child(yn, default_val); RW_ASSERT(xn); XMLNode* un = update_node->add_child(yn, default_val); if (yn->get_stmt_type() == RW_YANG_STMT_TYPE_CONTAINER) { // The container has a default descendant node fill_defaults(yn, xn, un, &subtree_update); if (!subtree_update) { new_node->remove_child(xn); update_node->remove_child(un); } *update_required = (*update_required || subtree_update); } else { *update_required = true; } } } }
virtual rw_xml_next_action_t visit(XMLNode* node, XMLNode** path, int32_t level) { if (node == nullptr) { return RW_XML_ACTION_TERMINATE; } YangNode *yn = node->get_yang_node(); if (nullptr == yn) { return RW_XML_ACTION_NEXT; } if (yn->get_stmt_type() == RW_YANG_STMT_TYPE_LIST) { return RW_XML_ACTION_NEXT_SIBLING; } RW_ASSERT(to_node_); // This is the root node then this is the first time call if (level == 0) { RW_ASSERT(level_ == -1); std::string text = node->get_text_value(); if (node->get_yang_node()->is_leafy() && node->get_yang_node()->get_type()->get_leaf_type() != RW_YANG_LEAF_TYPE_EMPTY && text.find_first_not_of("\t\n\v\f\r ") != std::string::npos) { if (text.find_first_of ("http")) { RW_CRASH(); } curr_node_ = to_node_->add_child(node->get_yang_node(), node->get_text_value().c_str()); } else { curr_node_ = to_node_->add_child(node->get_yang_node()); } path_[++level_].first = node; path_[level_].second = curr_node_; return RW_XML_ACTION_NEXT; } XMLNode *from_parent = path[level-1]; RW_ASSERT(from_parent); // Walk up our path and find the corresponding parent in teh vistor's tree while(level_ >= 0) { if (path_[level_].first == from_parent) { // Found a common parent - break and add the new node XMLNode *to_parent = static_cast<XMLNode*>(path_[level_].second); std::string text = node->get_text_value(); if (text.find_first_not_of("\t\n\v\f\r ") != std::string::npos) { // A bit of a hack to ensure that white spaces are not inserted to the // XML. This is a problem for UT that read from files curr_node_ = to_parent->add_child(node->get_yang_node(), node->get_text_value().c_str()); } else { curr_node_ = to_parent->add_child(node->get_yang_node()); } // Replace the node in the current path with the new node. path_[++level_].first = node; path_[level_].second = curr_node_; break; } level_--; } RW_ASSERT(level_ > 0); return RW_XML_ACTION_NEXT; }