[ONOS-6964][ONOS-6966] Add pipeconf codec and pipeconf view

Change-Id: Ie60a5451bcc24a27ede655c8230d82998ea4f3be
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/PipeconfViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/PipeconfViewMessageHandler.java
new file mode 100644
index 0000000..9ecbeca
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/PipeconfViewMessageHandler.java
@@ -0,0 +1,224 @@
+/*
+ * 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.ui.impl;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.pi.model.PiActionModel;
+import org.onosproject.net.pi.model.PiHeaderFieldModel;
+import org.onosproject.net.pi.model.PiHeaderModel;
+import org.onosproject.net.pi.model.PiHeaderTypeModel;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.model.PiPipelineInterpreter;
+import org.onosproject.net.pi.model.PiPipelineModel;
+import org.onosproject.net.pi.model.PiTableMatchFieldModel;
+import org.onosproject.net.pi.model.PiTableModel;
+import org.onosproject.net.pi.runtime.PiPipeconfService;
+import org.onosproject.net.pi.runtime.PiTableId;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiMessageHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+
+public class PipeconfViewMessageHandler extends UiMessageHandler {
+    private static final Logger log =
+            LoggerFactory.getLogger(PipeconfViewMessageHandler.class);
+    private static final String PIPECONF_REQUEST = "pipeconfRequest";
+    private static final String PIPECONF_RESP = "pipeConfResponse";
+    private static final String DEVICE_ID = "devId";
+    private static final String PIPECONF = "pipeconf";
+    private static final String PIPELINE_MODEL = "pipelineModel";
+    private static final String NO_PIPECONF_RESP = "noPipeconfResp";
+
+    @Override
+    protected Collection<RequestHandler> createRequestHandlers() {
+        return ImmutableSet.of(new PipeconfRequestHandler());
+    }
+
+    private class PipeconfRequestHandler extends RequestHandler {
+
+        public PipeconfRequestHandler() {
+            super(PIPECONF_REQUEST);
+        }
+
+        @Override
+        public void process(ObjectNode payload) {
+            PiPipeconfService piPipeconfService = get(PiPipeconfService.class);
+            DeviceService deviceService = get(DeviceService.class);
+            ObjectNode responseData = objectNode();
+            String devId = string(payload, DEVICE_ID);
+            if (devId == null || devId.isEmpty()) {
+                log.warn("{}: Invalid device id", PIPECONF_REQUEST);
+                sendMessage(NO_PIPECONF_RESP, null);
+                return;
+            }
+            DeviceId deviceId = DeviceId.deviceId(devId);
+            Optional<PiPipeconfId> pipeconfId = piPipeconfService.ofDevice(deviceId);
+            if (!pipeconfId.isPresent()) {
+                log.warn("{}: Can't find pipeconf id for device {}", PIPECONF_REQUEST, deviceId);
+                sendMessage(NO_PIPECONF_RESP, null);
+                return;
+            }
+
+            Optional<PiPipeconf> pipeconf = piPipeconfService.getPipeconf(pipeconfId.get());
+            if (!pipeconf.isPresent()) {
+                log.warn("{}: Can't find pipeconf {}", PIPECONF_REQUEST, pipeconfId);
+                sendMessage(NO_PIPECONF_RESP, null);
+                return;
+            }
+            CodecContext codecContext = getJsonCodecContext();
+
+            ObjectNode pipeconfData = codecContext.encode(pipeconf.get(), PiPipeconf.class);
+            responseData.set(PIPECONF, pipeconfData);
+
+            // Filtered out models not exists in interpreter
+            // usually they generated by compiler automatically
+            Device device = deviceService.getDevice(deviceId);
+            if (device == null || !deviceService.isAvailable(deviceId)) {
+                log.warn("{}: Device {} is not available", PIPECONF_REQUEST, deviceId);
+                sendMessage(NO_PIPECONF_RESP, null);
+                return;
+            }
+            PiPipelineInterpreter interpreter = device.as(PiPipelineInterpreter.class);
+            PiPipelineModel pipelineModel =
+                    filteredOutAdditionalData(pipeconf.get().pipelineModel(), interpreter);
+
+            ObjectNode pipelineModelData =
+                    codecContext.encode(pipelineModel, PiPipelineModel.class);
+            responseData.set(PIPELINE_MODEL, pipelineModelData);
+
+            sendMessage(PIPECONF_RESP, responseData);
+        }
+    }
+
+    private PiPipelineModel filteredOutAdditionalData(PiPipelineModel piPipelineModel,
+                                                      PiPipelineInterpreter interpreter) {
+        if (interpreter == null) {
+            // Do nothing if there is no interpreter
+            return piPipelineModel;
+        }
+        // filter out actions, headers and tables if not exists in interpreter
+        Set<PiHeaderTypeModel> newHeaderTypesModels = Sets.newHashSet();
+        Set<PiHeaderModel> newHeaderModels = Sets.newHashSet();
+        Set<PiActionModel> newActionModels = Sets.newHashSet();
+        Set<PiTableModel> newTableModels = Sets.newHashSet();
+
+        piPipelineModel.tables().forEach(table -> {
+            String tableName = table.name();
+            PiTableId tableId = PiTableId.of(tableName);
+
+            if (interpreter.mapPiTableId(tableId).isPresent()) {
+                newTableModels.add(table);
+
+                newActionModels.addAll(table.actions());
+                table.matchFields().stream()
+                        .map(PiTableMatchFieldModel::field)
+                        .map(PiHeaderFieldModel::header)
+                        .forEach(header -> {
+                            newHeaderModels.add(header);
+                            newHeaderTypesModels.add(header.type());
+                        });
+
+            }
+        });
+
+        return new FilteredPipelineModel(newHeaderTypesModels,
+                                         newHeaderModels,
+                                         newActionModels,
+                                         newTableModels);
+    }
+
+    /**
+     * Pipeline model for UI message.
+     * FIXME: Is it necessary to create this class?
+     */
+    private class FilteredPipelineModel implements PiPipelineModel {
+
+        private Set<PiHeaderTypeModel> headerTypesModels;
+        private Set<PiHeaderModel> headerModels;
+        private Set<PiActionModel> actionModels;
+        private Set<PiTableModel> tableModels;
+
+        public FilteredPipelineModel(Set<PiHeaderTypeModel> headerTypesModels,
+                                     Set<PiHeaderModel> headerModels,
+                                     Set<PiActionModel> actionModels,
+                                     Set<PiTableModel> tableModels) {
+            this.headerTypesModels = headerTypesModels;
+            this.headerModels = headerModels;
+            this.actionModels = actionModels;
+            this.tableModels = tableModels;
+        }
+
+        @Override
+        public Optional<PiHeaderTypeModel> headerType(String name) {
+            return headerTypesModels.stream()
+                    .filter(headerType -> headerType.name().equals(name))
+                    .findFirst();
+        }
+
+        @Override
+        public Collection<PiHeaderTypeModel> headerTypes() {
+            return headerTypesModels;
+        }
+
+        @Override
+        public Optional<PiHeaderModel> header(String name) {
+            return headerModels.stream()
+                    .filter(headerModel -> headerModel.name().equals(name))
+                    .findFirst();
+        }
+
+        @Override
+        public Collection<PiHeaderModel> headers() {
+            return headerModels;
+        }
+
+        @Override
+        public Optional<PiActionModel> action(String name) {
+            return actionModels.stream()
+                    .filter(actionModel -> actionModel.name().equals(name))
+                    .findFirst();
+        }
+
+        @Override
+        public Collection<PiActionModel> actions() {
+            return actionModels;
+        }
+
+        @Override
+        public Optional<PiTableModel> table(String name) {
+            return tableModels.stream()
+                    .filter(tableModel -> tableModel.name().equals(name))
+                    .findFirst();
+        }
+
+        @Override
+        public Collection<PiTableModel> tables() {
+            return tableModels;
+        }
+    }
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
index 121df58..f3afa1a 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
@@ -194,6 +194,7 @@
                 new UiViewHidden("port"),
                 new UiViewHidden("group"),
                 new UiViewHidden("meter"),
+                new UiViewHidden("pipeconf"),
 
                 mkView(NETWORK, "link", "nav_links"),
                 mkView(NETWORK, "host", "nav_hosts"),
@@ -221,7 +222,8 @@
                         new ClusterViewMessageHandler(),
                         new ProcessorViewMessageHandler(),
                         new TunnelViewMessageHandler(),
-                        new PartitionViewMessageHandler()
+                        new PartitionViewMessageHandler(),
+                        new PipeconfViewMessageHandler()
                 );
 
         UiTopoOverlayFactory topoOverlayFactory =