Behaviour for Transceiver

- basically ONOS-7445, ONOS-7446 returning XML as intermediate representation.

Change-Id: I3380e089cc2d8a408c280df18f5fa7c75feb02f9
diff --git a/apps/odtn/src/main/java/org/onosproject/odtn/behaviour/ConfigurableTransceiver.java b/apps/odtn/src/main/java/org/onosproject/odtn/behaviour/ConfigurableTransceiver.java
new file mode 100644
index 0000000..a94f597
--- /dev/null
+++ b/apps/odtn/src/main/java/org/onosproject/odtn/behaviour/ConfigurableTransceiver.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018-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.odtn.behaviour;
+
+import java.util.List;
+
+import org.onosproject.net.driver.HandlerBehaviour;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Behaviour for Transceiver.
+ */
+@Beta
+public interface ConfigurableTransceiver extends HandlerBehaviour {
+
+    /**
+     * Generate configuration to enable/disable transceiver.
+     *
+     * @param componentName to enable/disable
+     * @param enable or disable
+     * @return XML documents (List to handle configuration with multiple-roots)
+     */
+    // return type and how component get specified are likely to change in future
+    @Beta
+    List<CharSequence> enable(String componentName, boolean enable);
+}
diff --git a/apps/odtn/src/main/java/org/onosproject/odtn/behaviour/PlainTransceiver.java b/apps/odtn/src/main/java/org/onosproject/odtn/behaviour/PlainTransceiver.java
new file mode 100644
index 0000000..ae14e07
--- /dev/null
+++ b/apps/odtn/src/main/java/org/onosproject/odtn/behaviour/PlainTransceiver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018-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.odtn.behaviour;
+
+import static org.onosproject.odtn.utils.YangToolUtil.toCharSequence;
+import static org.onosproject.odtn.utils.YangToolUtil.toCompositeData;
+import static org.onosproject.odtn.utils.YangToolUtil.toResourceData;
+import static org.onosproject.odtn.utils.YangToolUtil.toXmlCompositeStream;
+
+import java.util.List;
+
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.odtn.utils.openconfig.Transceiver;
+import org.onosproject.yang.model.DataNode;
+import org.onosproject.yang.model.ResourceId;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Plain OpenConfig based implementation.
+ */
+public class PlainTransceiver extends AbstractHandlerBehaviour
+        implements ConfigurableTransceiver {
+
+    @Override
+    public List<CharSequence> enable(String componentName, boolean enable) {
+        List<DataNode> nodes = Transceiver.enable(componentName, enable);
+
+        ResourceId empty = ResourceId.builder().build();
+        return Lists.transform(nodes,
+                   node -> toCharSequence(toXmlCompositeStream(toCompositeData(toResourceData(empty, node)))));
+    }
+
+}
diff --git a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/package-info.java b/apps/odtn/src/main/java/org/onosproject/odtn/behaviour/package-info.java
similarity index 88%
rename from apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/package-info.java
rename to apps/odtn/src/main/java/org/onosproject/odtn/behaviour/package-info.java
index ca8433c..85c8df6 100644
--- a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/package-info.java
+++ b/apps/odtn/src/main/java/org/onosproject/odtn/behaviour/package-info.java
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 /**
- * sub-package for ODTN related CLI.
+ * ODTN Behaviours.
  */
-package org.onosproject.odtn.cli.impl.sub;
+package org.onosproject.odtn.behaviour;
diff --git a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/OdtnManualTestCommand.java b/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/OdtnManualTestCommand.java
index 4f1ed4a..604e9d0 100644
--- a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/OdtnManualTestCommand.java
+++ b/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/OdtnManualTestCommand.java
@@ -18,8 +18,6 @@
 import static org.onosproject.odtn.utils.YangToolUtil.toCharSequence;
 import static org.onosproject.odtn.utils.YangToolUtil.toCompositeData;
 import static org.onosproject.odtn.utils.YangToolUtil.toDocument;
-import static org.onosproject.odtn.utils.YangToolUtil.toJsonCompositeStream;
-import static org.onosproject.odtn.utils.YangToolUtil.toJsonNode;
 import static org.onosproject.odtn.utils.YangToolUtil.toResourceData;
 import static org.onosproject.odtn.utils.YangToolUtil.toXmlCompositeStream;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -41,18 +39,22 @@
 import org.onosproject.cli.net.DeviceIdCompleter;
 import org.onosproject.config.DynamicConfigService;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.netconf.NetconfController;
 import org.onosproject.netconf.NetconfDevice;
 import org.onosproject.netconf.NetconfException;
-import org.onosproject.odtn.cli.impl.sub.OpticalChannel;
-import org.onosproject.odtn.cli.impl.sub.Transceiver;
+import org.onosproject.odtn.behaviour.ConfigurableTransceiver;
+import org.onosproject.odtn.behaviour.PlainTransceiver;
+import org.onosproject.odtn.utils.openconfig.OpticalChannel;
+import org.onosproject.odtn.utils.openconfig.Transceiver;
 import org.onosproject.yang.model.DataNode;
 import org.onosproject.yang.model.ResourceId;
 import org.slf4j.Logger;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.Lists;
+import com.google.common.io.CharSource;
 
 
 
@@ -90,6 +92,7 @@
 
     // OSGi Services to be filled in at the beginning.
     private DynamicConfigService dcs;
+    private DeviceService deviceService;
 
 
     void printlog(String format, Object... objs) {
@@ -97,9 +100,17 @@
         log.info(format, objs);
     }
 
+    List<CharSequence> transform(List<DataNode> input) {
+        ResourceId empty = ResourceId.builder().build();
+        return Lists.transform(input,
+                   node -> toCharSequence(toXmlCompositeStream(toCompositeData(toResourceData(empty, node)))));
+    }
+
+
     @Override
     protected void execute() {
         dcs = get(DynamicConfigService.class);
+        deviceService = get(DeviceService.class);
 
         try {
             mode = Mode.valueOf(modeStr);
@@ -112,23 +123,36 @@
         }
 
         // effectively configuration context
-        List<DataNode> nodes = new ArrayList<>();
+        List<CharSequence> nodes = new ArrayList<>();
+
+        // driver selection with fallback to plain OpenConfig
+        DeviceId did = Optional.ofNullable(uri)
+                        .map(DeviceId::deviceId)
+                        .orElse(null);
+        ConfigurableTransceiver transceiver =
+            Optional.ofNullable(did)
+            .map(deviceService::getDevice)
+            .filter(device -> device.is(ConfigurableTransceiver.class))
+            .map(device -> device.as(ConfigurableTransceiver.class))
+            .orElseGet(() -> new PlainTransceiver());
 
         switch (mode) {
         case PRECONF_TRANSCEIVER:
-            nodes.addAll(Transceiver.preconf(componentName));
+            // note: these doesn't support driver
+            nodes.addAll(transform(Transceiver.preconf(componentName)));
             break;
 
         case ENABLE_TRANSCEIVER:
-            nodes.addAll(Transceiver.enable(componentName, true));
+            nodes.addAll(transceiver.enable(componentName, true));
             break;
 
         case DISABLE_TRANSCEIVER:
-            nodes.addAll(Transceiver.enable(componentName, false));
+            nodes.addAll(transceiver.enable(componentName, false));
             break;
 
         case PRECONF_OPTICAL_CHANNEL:
-            nodes.addAll(OpticalChannel.preconf(componentName));
+            // note: these doesn't support driver
+            nodes.addAll(transform(OpticalChannel.preconf(componentName)));
             break;
 
         default:
@@ -140,7 +164,7 @@
         doTheMagic(nodes);
     }
 
-    void doTheMagic(List<DataNode> nodes) {
+    void doTheMagic(List<CharSequence> nodes) {
 
         Document doc;
         try {
@@ -167,10 +191,8 @@
         editConfig.appendChild(config);
 
 
-        for (DataNode node : nodes) {
-            // empty resourceId assuming node is root of cfg tree
-            ResourceId resourceId = ResourceId.builder().build();
-            Document ldoc = toDocument(toXmlCompositeStream(toCompositeData(toResourceData(resourceId, node))));
+        for (CharSequence node : nodes) {
+            Document ldoc = toDocument(CharSource.wrap(node));
             Element cfgRoot = ldoc.getDocumentElement();
 
             // is everything as merge, ok?
@@ -178,11 +200,11 @@
 
             // move (or copy) node to another Document
             config.appendChild(Optional.ofNullable(doc.adoptNode(cfgRoot))
-                             .orElseGet(() -> doc.importNode(cfgRoot, true)));
+                                  .orElseGet(() -> doc.importNode(cfgRoot, true)));
 
             // don't have good use for JSON for now
-            JsonNode json = toJsonNode(toJsonCompositeStream(toCompositeData(toResourceData(resourceId, node))));
-            printlog("JSON:\n{}", toCharSequence(json));
+            //JsonNode json = toJsonNode(toJsonCompositeStream(toCompositeData(toResourceData(resourceId, node))));
+            //printlog("JSON:\n{}", toCharSequence(json));
         }
 
         printlog("XML:\n{}", XmlString.prettifyXml(toCharSequence(doc)));
diff --git a/apps/odtn/src/main/java/org/onosproject/odtn/utils/YangToolUtil.java b/apps/odtn/src/main/java/org/onosproject/odtn/utils/YangToolUtil.java
index 4df3943..3f097fc 100644
--- a/apps/odtn/src/main/java/org/onosproject/odtn/utils/YangToolUtil.java
+++ b/apps/odtn/src/main/java/org/onosproject/odtn/utils/YangToolUtil.java
@@ -63,6 +63,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.google.common.annotations.Beta;
+import com.google.common.io.CharSource;
 import com.google.common.io.CharStreams;
 
 @Beta
@@ -146,7 +147,7 @@
 
         try {
             return mapper.writerWithDefaultPrettyPrinter()
-                        .writeValueAsString(jsonInput);
+                         .writeValueAsString(jsonInput);
         } catch (JsonProcessingException e) {
             log.error("Exception thrown", e);
             return null;
@@ -204,6 +205,23 @@
     }
 
     /**
+     * Converts XML source into XML Document.
+     *
+     * @param xmlInput to convert
+     * @return Document
+     */
+    public static Document toDocument(CharSource xmlInput) {
+        try {
+            return DocumentBuilderFactory.newInstance()
+                    .newDocumentBuilder()
+                .parse(new InputSource(xmlInput.openStream()));
+        } catch (ParserConfigurationException | SAXException | IOException e) {
+            log.error("Exception thrown", e);
+            return null;
+        }
+    }
+
+    /**
      * Converts CompositeData into XML CompositeStream.
      *
      * @param input CompositeData to convert
@@ -269,7 +287,6 @@
         return builder.build();
     }
 
-
     /**
      * Converts ModelObject into a DataNode.
      *
diff --git a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/OpticalChannel.java b/apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/OpticalChannel.java
similarity index 98%
rename from apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/OpticalChannel.java
rename to apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/OpticalChannel.java
index 9d813e5..2784706 100644
--- a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/OpticalChannel.java
+++ b/apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/OpticalChannel.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.odtn.cli.impl.sub;
+package org.onosproject.odtn.utils.openconfig;
 
 import static org.onosproject.odtn.utils.YangToolUtil.toDataNode;
 
diff --git a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/PlainPlatform.java b/apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/PlainPlatform.java
similarity index 97%
rename from apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/PlainPlatform.java
rename to apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/PlainPlatform.java
index 74beaa6..0997632 100644
--- a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/PlainPlatform.java
+++ b/apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/PlainPlatform.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.odtn.cli.impl.sub;
+package org.onosproject.odtn.utils.openconfig;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
diff --git a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/Transceiver.java b/apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/Transceiver.java
similarity index 98%
rename from apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/Transceiver.java
rename to apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/Transceiver.java
index 9d55408..66ca6cd 100644
--- a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/Transceiver.java
+++ b/apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/Transceiver.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.odtn.cli.impl.sub;
+package org.onosproject.odtn.utils.openconfig;
 
 import static org.onosproject.odtn.utils.YangToolUtil.toDataNode;
 
diff --git a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/package-info.java b/apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/package-info.java
similarity index 84%
copy from apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/package-info.java
copy to apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/package-info.java
index ca8433c..3b83083 100644
--- a/apps/odtn/src/main/java/org/onosproject/odtn/cli/impl/sub/package-info.java
+++ b/apps/odtn/src/main/java/org/onosproject/odtn/utils/openconfig/package-info.java
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 /**
- * sub-package for ODTN related CLI.
+ * Utilities to deal with onos-yang-tools OpenConfig objects..
  */
-package org.onosproject.odtn.cli.impl.sub;
+package org.onosproject.odtn.utils.openconfig;