[ONOS-6884] Device config related utilities

- misc fixes around dynamic config

Change-Id: I3a7b6130e8c698594fa7deac8a04219c9c8a4af2
diff --git a/apps/config/src/main/java/org/onosproject/d/config/DeviceResourceIds.java b/apps/config/src/main/java/org/onosproject/d/config/DeviceResourceIds.java
new file mode 100644
index 0000000..a49c6fc
--- /dev/null
+++ b/apps/config/src/main/java/org/onosproject/d/config/DeviceResourceIds.java
@@ -0,0 +1,197 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkArgument;
+import java.util.Optional;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.yang.model.KeyLeaf;
+import org.onosproject.yang.model.ListKey;
+import org.onosproject.yang.model.NodeKey;
+import org.onosproject.yang.model.ResourceId;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Utility related to device ResourceId.
+ */
+@Beta
+public abstract class DeviceResourceIds {
+
+    // assuming following device tree structure
+    // - "/"
+    //    +- devices
+    //         +- device (=device root node:ListKey)
+
+
+    // FIXME randomly defined namespace, replace with something appropriate
+    /**
+     * SchemaId namespace for DCS defined nodes.
+     */
+    public static final String DCS_NAMESPACE = "org.onosproject.dcs";
+
+    /**
+     * SchemaId name for root node.
+     */
+    public static final String ROOT_NAME = "/";
+    /**
+     * SchemaId name for devices node.
+     */
+    public static final String DEVICES_NAME = "devices";
+    /**
+     * SchemaId name for device node.
+     */
+    public static final String DEVICE_NAME = "device";
+    /**
+     * KeyLeaf {@code name}, which holds DeviceId information on device node.
+     */
+    public static final String DEVICE_ID_KL_NAME = "device-id";
+
+    /**
+     * ResourceId pointing at root node.
+     */
+    public static final ResourceId ROOT_ID = ResourceId.builder()
+            .addBranchPointSchema(ROOT_NAME, DCS_NAMESPACE)
+            .build();
+
+    static final NodeKey<?> ROOT_NODE =
+            NodeKey.builder().schemaId(ROOT_NAME, DCS_NAMESPACE).build();
+
+    /**
+     * nodeKeys index for root node.
+     */
+    static final int ROOT_INDEX = 0;
+    /**
+     * nodeKeys index for devices node.
+     */
+    static final int DEVICES_INDEX = 1;
+    /**
+     * nodeKeys index for device node.
+     */
+    static final int DEVICE_INDEX = 2;
+
+    /**
+     * Tests if specified path points to root node of a Device.
+     *
+     * @param path to test.
+     * @return true if path points to root node of a Device.
+     */
+    public static boolean isDeviceRootNode(ResourceId path) {
+        return path.nodeKeys().size() == 3 &&
+               isUnderDeviceRootNode(path);
+    }
+
+    /**
+     * Tests if specified path points to root node of a Device.
+     *
+     * @param path to test.
+     * @return true if path points to root node of a Device.
+     */
+    public static boolean isUnderDeviceRootNode(ResourceId path) {
+        return path.nodeKeys().size() >= 3 &&
+                // TODO Would be better to test whole schemeId
+                DEVICE_NAME.equals(path.nodeKeys().get(DEVICE_INDEX).schemaId().name()) &&
+                (path.nodeKeys().get(DEVICE_INDEX) instanceof ListKey) &&
+                // TODO Would be better to test whole schemeId
+                DEVICES_NAME.equals(path.nodeKeys().get(DEVICES_INDEX).schemaId().name()) &&
+                ROOT_NODE.equals(path.nodeKeys().get(ROOT_INDEX));
+    }
+
+    /**
+     * Tests if specified path points to root or devices node.
+     *
+     * @param path to test.
+     * @return true if path points to root node of a Device.
+     */
+    public static boolean isRootOrDevicesNode(ResourceId path) {
+        return isDevicesNode(path) ||
+               isRootNode(path);
+    }
+
+    public static boolean isDevicesNode(ResourceId path) {
+        return path.nodeKeys().size() == 2 &&
+                // TODO Would be better to test whole schemeId
+                DEVICES_NAME.equals(path.nodeKeys().get(DEVICES_INDEX).schemaId().name()) &&
+                ROOT_NODE.equals(path.nodeKeys().get(ROOT_INDEX));
+    }
+
+    public static boolean isRootNode(ResourceId path) {
+        return path.nodeKeys().size() == 1 &&
+                ROOT_NODE.equals(path.nodeKeys().get(ROOT_INDEX));
+    }
+
+    /**
+     * Transforms device resource path to DeviceId.
+     *
+     * @param path pointing to somewhere in the subtree of a device
+     * @return DeviceId
+     * @throws IllegalArgumentException if the path was not part of devices tree
+     */
+    public static DeviceId toDeviceId(ResourceId path) {
+        checkArgument(isUnderDeviceRootNode(path), path);
+        // FIXME if we decide to drop any of intermediate nodes
+        //        "/" - "devices" - "device"
+        return toDeviceId(path.nodeKeys().get(DEVICE_INDEX));
+    }
+
+    /**
+     * Transforms root node of a device to corresponding DeviceId.
+     *
+     * @param deviceRoot NodeKey of a device root node
+     * @return DeviceId
+     * @throws IllegalArgumentException if not a device node
+     */
+    public static DeviceId toDeviceId(NodeKey<?> deviceRoot) {
+        // TODO Would be better to test whole schemeId
+        if (!DEVICE_NAME.equals(deviceRoot.schemaId().name())) {
+            throw new IllegalArgumentException(deviceRoot + " is not a device node");
+        }
+
+        if (deviceRoot instanceof ListKey) {
+            ListKey device = (ListKey) deviceRoot;
+            Optional<DeviceId> did = device.keyLeafs().stream()
+                // TODO If we decide to define ONOS schema for device ID,
+                // use whole schemaId to filter, not only by name
+                .filter(kl -> DEVICE_ID_KL_NAME.equals(kl.leafSchema().name()))
+                .map(KeyLeaf::leafValAsString)
+                .map(DeviceId::deviceId)
+                .findFirst();
+
+            if (did.isPresent()) {
+                return did.get();
+            }
+            throw new IllegalArgumentException("device-id not found in " + deviceRoot);
+        }
+        throw new IllegalArgumentException("Unexpected type " + deviceRoot.getClass());
+    }
+
+    /**
+     * Transforms DeviceId into a ResourceId pointing to device root node.
+     *
+     * @param deviceId to transform
+     * @return ResourceId
+     */
+    public static ResourceId toResourceId(DeviceId deviceId) {
+        return ResourceId.builder()
+                .addBranchPointSchema(ROOT_NAME, DCS_NAMESPACE)
+                .addBranchPointSchema(DEVICES_NAME, DCS_NAMESPACE)
+                .addBranchPointSchema(DEVICE_NAME, DCS_NAMESPACE)
+                .addKeyLeaf(DEVICE_ID_KL_NAME, DCS_NAMESPACE, deviceId.toString())
+                .build();
+    }
+
+}