org.onosproject.d.config.ResourceIds.java Source code

Java tutorial

Introduction

Here is the source code for org.onosproject.d.config.ResourceIds.java

Source

/*
 * Copyright 2017-present Open Networking Foundation
 *
 * 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.
 */
package org.onosproject.d.config;

import com.google.common.annotations.Beta;
import org.apache.commons.text.StringEscapeUtils;
import org.onosproject.yang.model.DataNode;
import org.onosproject.yang.model.KeyLeaf;
import org.onosproject.yang.model.LeafListKey;
import org.onosproject.yang.model.LeafListKey.LeafListKeyBuilder;
import org.onosproject.yang.model.ListKey.ListKeyBuilder;
import org.onosproject.yang.model.ListKey;
import org.onosproject.yang.model.NodeKey;
import org.onosproject.yang.model.ResourceId;
import org.onosproject.yang.model.ResourceId.Builder;
import org.onosproject.yang.model.SchemaId;
import org.slf4j.Logger;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * Utility related to ResourceId.
 */
@Beta
public abstract class ResourceIds {

    private static final Logger log = getLogger(ResourceIds.class);

    /**
     * Root resource Id used by Yang Runtime.
     * (name: {@code "/"}, nameSpace: {@code null})
     */
    public static final ResourceId YRS_ROOT = ResourceId.builder().addBranchPointSchema("/", null).build();
    /**
     * Absolute ResourceId pointing at root node.
     * (name: {@link DeviceResourceIds#ROOT_NAME},
     *  nameSpace: {@link DeviceResourceIds#DCS_NAMESPACE})
     */
    public static final ResourceId ROOT_ID = ResourceId.builder()
            .addBranchPointSchema(DeviceResourceIds.ROOT_NAME, DeviceResourceIds.DCS_NAMESPACE).build();

    /**
     * Builds the ResourceId of specified {@code node}.
     *
     * @param parent ResourceId of {@code node} parent
     * @param node   to create ResourceId.
     * @return ResourceId of {@code node}
     */
    public static ResourceId resourceId(ResourceId parent, DataNode node) {
        final ResourceId.Builder builder;
        if (parent == null) {
            builder = ResourceId.builder();
        } else {
            try {
                builder = parent.copyBuilder();
            } catch (CloneNotSupportedException e) {
                throw new IllegalArgumentException(e);
            }
        }

        SchemaId sid = node.key().schemaId();
        switch (node.type()) {
        case MULTI_INSTANCE_LEAF_VALUE_NODE:
            builder.addLeafListBranchPoint(sid.name(), sid.namespace(), ((LeafListKey) node.key()).asString());
            break;

        case MULTI_INSTANCE_NODE:
            builder.addBranchPointSchema(sid.name(), sid.namespace());
            for (KeyLeaf keyLeaf : ((ListKey) node.key()).keyLeafs()) {
                builder.addKeyLeaf(keyLeaf.leafSchema().name(), keyLeaf.leafSchema().namespace(),
                        keyLeaf.leafValAsString());
            }
            break;

        case SINGLE_INSTANCE_LEAF_VALUE_NODE:
        case SINGLE_INSTANCE_NODE:
            builder.addBranchPointSchema(sid.name(), sid.namespace());
            break;

        default:
            throw new IllegalArgumentException("Unknown type " + node);

        }

        return builder.build();
    }

    /**
     * Concats {@code path} after {@code prefix}.
     *
     * @param prefix path
     * @param path   to append after {@code path}
     * @return concatenated ResouceId
     */
    public static ResourceId concat(ResourceId prefix, ResourceId path) {
        checkArgument(!path.nodeKeys().contains(DeviceResourceIds.ROOT_NODE), "%s was already absolute path", path);
        try {
            return prefix.copyBuilder().append(path).build();
        } catch (CloneNotSupportedException e) {
            log.error("Could not copy {}", path, e);
            throw new IllegalArgumentException("Could not copy " + path, e);
        }
    }

    /**
     * Returns {@code child} as relative ResourceId against {@code base}.
     *
     * @param base  ResourceId
     * @param child ResourceId to relativize
     * @return relative ResourceId
     */
    public static ResourceId relativize(ResourceId base, ResourceId child) {
        checkArgument(child.nodeKeys().size() >= base.nodeKeys().size(),
                "%s path must be deeper than base prefix %s", child, base);
        @SuppressWarnings("rawtypes")
        Iterator<NodeKey> bIt = base.nodeKeys().iterator();
        @SuppressWarnings("rawtypes")
        Iterator<NodeKey> cIt = child.nodeKeys().iterator();
        while (bIt.hasNext()) {
            NodeKey<?> b = bIt.next();
            NodeKey<?> c = cIt.next();

            checkArgument(Objects.equals(b, c), "%s is not a prefix of %s.\n" + "b:%s != c:%s", base, child, b, c);
        }

        return ResourceId.builder()
                .append(child.nodeKeys().subList(base.nodeKeys().size(), child.nodeKeys().size())).build();
    }

    /**
     * Removes the root node from {@code path}.
     *
     * @param path given resource ID
     * @return resource ID without root node
     */
    public static ResourceId removeRootNode(ResourceId path) {
        if (!startsWithRootNode(path)) {
            return path;
        }

        return ResourceId.builder().append(path.nodeKeys().subList(1, path.nodeKeys().size())).build();
    }

    /**
     * Returns the resource ID of the parent data node pointed by {@code path}.
     *
     * @param path resource ID of the given data node
     * @return resource ID of the parent data node
     */
    public static ResourceId parentOf(ResourceId path) {
        try {
            return path.copyBuilder().removeLastKey().build();
        } catch (CloneNotSupportedException e) {
            log.error("Could not copy {}", path, e);
            throw new IllegalArgumentException("Could not copy " + path, e);
        }
    }

    /**
     * Tests if {@code child} starts with {@code prefix}.
     *
     * @param prefix expected
     * @param child  to test
     * @return true if {@code child} starts with {@code prefix}
     */
    public static boolean isPrefix(ResourceId prefix, ResourceId child) {

        return child.nodeKeys().size() >= prefix.nodeKeys().size()
                && prefix.nodeKeys().equals(child.nodeKeys().subList(0, prefix.nodeKeys().size()));
    }

    /**
     * Tests if {@code path} starts with {@link DeviceResourceIds#ROOT_NAME}.
     *
     * @param path to test
     * @return true if {@code path} starts with {@link DeviceResourceIds#ROOT_NAME}
     */
    public static boolean startsWithRootNode(ResourceId path) {
        return !path.nodeKeys().isEmpty()
                && DeviceResourceIds.ROOT_NAME.equals(path.nodeKeys().get(0).schemaId().name());
    }

    /**
     * Converts node-identifier element to a NodeKey.
     *
     * @param id to parse (node-identifier fragment between '/')
     * @return NodeKey (warning: returned namespace can be null, which should be interpreted as
     *         same as parent)
     */
    private static NodeKey toNodeKey(String id) {
        Pattern nodeId = Pattern.compile(
                "^((?<prefix>[a-zA-Z_](?:[a-zA-Z0-9_.\\-]*)):)?" + "(?<identifier>[a-zA-Z_](?:[a-zA-Z0-9_.-]*))");

        Matcher nidMatcher = nodeId.matcher(id);
        if (!nidMatcher.find()) {
            throw new IllegalArgumentException("node identifier not found in " + id);
        }

        String prefix = nidMatcher.group("prefix");
        String identifier = nidMatcher.group("identifier");

        // key and val pattern is a bit loosened from RFC for simplicity
        Pattern preds = Pattern.compile("\\[\\s*(?<key>[^=\\s]+)\\s*=\\s*\\\"(?<val>[^\\]]+)\\\"\\s*\\]");
        Matcher predMatcher = preds.matcher(id);
        predMatcher.region(nidMatcher.end(), id.length());
        LeafListKeyBuilder llkb = null;
        ListKeyBuilder llb = null;
        while (predMatcher.find()) {
            String key = predMatcher.group("key");
            String val = predMatcher.group("val");
            if (key.equals(".")) {
                // LeafList
                if (llkb == null) {
                    llkb = new LeafListKeyBuilder();
                }
                llkb.schemaId(identifier, prefix).value(val);
            } else {
                // ListKey
                if (llb == null) {
                    llb = new ListKeyBuilder();
                }
                llb.schemaId(identifier, prefix);
                Matcher m = nodeId.matcher(key);
                m.matches();
                llb.addKeyLeaf(m.group("identifier"), m.group("prefix"), val);
            }
        }
        if (llkb != null) {
            return llkb.build();
        } else if (llb != null) {
            return llb.build();
        } else {
            return NodeKey.builder().schemaId(identifier, prefix).build();
        }
    }

    /**
     * Add {@link #YRS_ROOT} prefix if not already.
     *
     * @param rid resource id
     * @return ResourceId starting from {@link #YRS_ROOT}
     */
    public static ResourceId prefixYrsRoot(ResourceId rid) {
        if (rid == null) {
            return YRS_ROOT;
        }

        if (isPrefix(YRS_ROOT, rid)) {
            return rid;
        }

        if (isPrefix(ROOT_ID, rid)) {
            return concat(YRS_ROOT, relativize(ROOT_ID, rid));
        }

        return concat(YRS_ROOT, rid);
    }

    /**
     * Add {@link #ROOT_ID} prefix if not already.
     *
     * @param rid resource id
     * @return ResourceId starting from {@link #ROOT_ID}
     */
    public static ResourceId prefixDcsRoot(ResourceId rid) {
        if (rid == null) {
            return ROOT_ID;
        }

        if (isPrefix(ROOT_ID, rid)) {
            return rid;
        }

        // test and replace YangRuntime root?
        if (isPrefix(YRS_ROOT, rid)) {
            return concat(ROOT_ID, relativize(YRS_ROOT, rid));
        }

        return concat(ROOT_ID, rid);
    }

    /**
     * Converts instance-identifier String into a ResourceId.
     *
     * @param input instance-identifier
     * @return Corresponding ResourceId relative to root or null if {@code iid} is '/'
     * Returned ResourceId will not have root NodeKey in it's path.
     * consider using {@link #prefixDcsRoot(ResourceId)},
     * {@link #prefixYrsRoot(ResourceId)} to add them.
     */
    public static ResourceId fromInstanceIdentifier(String input) {

        String[] nodes = input.split("/");
        List<NodeKey> nodeKeys = Arrays.stream(nodes).filter(s -> !s.isEmpty()).map(ResourceIds::toNodeKey)
                .collect(Collectors.toList());

        if (nodeKeys.isEmpty()) {
            return null;
        }

        Builder builder = ResourceId.builder();

        // fill-in null (=inherit from parent) nameSpace
        String lastNamespace = null;
        for (NodeKey nodeKey : nodeKeys) {
            if (nodeKey.schemaId().namespace() != null) {
                lastNamespace = nodeKey.schemaId().namespace();
            }
            if (nodeKey instanceof LeafListKey) {
                builder.addLeafListBranchPoint(nodeKey.schemaId().name(),
                        firstNonNull(nodeKey.schemaId().namespace(), lastNamespace),
                        ((LeafListKey) nodeKey).value());

            } else if (nodeKey instanceof ListKey) {
                builder.addBranchPointSchema(nodeKey.schemaId().name(), lastNamespace);
                for (KeyLeaf kl : ((ListKey) nodeKey).keyLeafs()) {
                    builder.addKeyLeaf(kl.leafSchema().name(),
                            firstNonNull(kl.leafSchema().namespace(), lastNamespace), kl.leafValue());
                }
            } else {
                builder.addBranchPointSchema(nodeKey.schemaId().name(), lastNamespace);
            }
        }
        return builder.build();
    }

    /**
     * Converts ResourceId to instance-identifier.
     *
     * @param rid to convert
     * @return instance-identifier
     *
     * @see <a href="https://tools.ietf.org/html/rfc7951#section-6.11">RFC 7951</a>
     * @see <a href="https://tools.ietf.org/html/rfc7950#section-14">RFC 7950 for ABNF</a>
     */
    public static String toInstanceIdentifier(ResourceId rid) {
        StringBuilder s = new StringBuilder();

        String lastNamespace = null;
        for (NodeKey nk : rid.nodeKeys()) {
            if (nk.schemaId().name().equals("/")) {
                // special handling for root nodeKey: skip it
                // YANG runtime root: null:/
                // DCS root: org.onosproject.dcs:/
                continue;
            }

            s.append('/');

            if (!Objects.equals(lastNamespace, nk.schemaId().namespace())) {
                s.append(nk.schemaId().namespace());
                s.append(':');
                lastNamespace = nk.schemaId().namespace();
            }
            s.append(nk.schemaId().name());

            if (nk instanceof LeafListKey) {
                LeafListKey llk = (LeafListKey) nk;
                s.append('[');
                s.append('.');

                s.append('=');

                s.append('"').append(StringEscapeUtils.escapeJson(llk.asString())).append('"');
                s.append(']');

            } else if (nk instanceof ListKey) {
                ListKey lk = (ListKey) nk;

                for (KeyLeaf kl : lk.keyLeafs()) {
                    s.append('[');

                    if (!Objects.equals(kl.leafSchema().namespace(), lastNamespace)) {
                        s.append(kl.leafSchema().namespace());
                        s.append(':');
                    }
                    s.append(kl.leafSchema().name());

                    s.append('=');

                    s.append('"').append(StringEscapeUtils.escapeJson(kl.leafValAsString())).append('"');
                    s.append(']');
                }
            } else {
                // normal NodeKey
                // nothing to do
            }
        }
        if (s.length() == 0) {
            return "/";
        }
        return s.toString();
    }

}