ONOS-3505: Basic behaviour of drivers table implemented.
- WIP -- still need to fix scrolling, styling, etc.

Change-Id: I376155ab1375ea4b4b136969b89f74c3733178b8
diff --git a/apps/drivermatrix/src/main/java/org/onosproject/drivermatrix/DriverViewMessageHandler.java b/apps/drivermatrix/src/main/java/org/onosproject/drivermatrix/DriverViewMessageHandler.java
index 8e9cdb5..148cfbe 100644
--- a/apps/drivermatrix/src/main/java/org/onosproject/drivermatrix/DriverViewMessageHandler.java
+++ b/apps/drivermatrix/src/main/java/org/onosproject/drivermatrix/DriverViewMessageHandler.java
@@ -30,24 +30,24 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 /**
- * Message handler for device view related messages.
+ * Message handler for driver matrix view related messages.
  */
 public class DriverViewMessageHandler extends UiMessageHandler {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
+    private static final int ONE = 1;
     private static final String DRIVER_DATA_REQUEST = "driverDataRequest";
     private static final String DRIVER_DATA_RESPONSE = "driverDataResponse";
 
     private static final String DRIVERS = "drivers";
     private static final String BEHAVIOURS = "behaviours";
+    private static final String MATRIX = "matrix";
 
     private static final Comparator<? super Class<? extends Behaviour>> BEHAVIOUR_BY_NAME =
             (o1, o2) -> o1.getSimpleName().compareTo(o2.getSimpleName());
@@ -59,11 +59,12 @@
     protected Collection<RequestHandler> createRequestHandlers() {
         return ImmutableSet.of(
                 new DataRequestHandler()
+                // TODO: for row selection, produce data for detail panel
 //                new DetailRequestHandler()
         );
     }
 
-    // handler for device table requests
+    // handler for driver matrix requests
     private final class DataRequestHandler extends RequestHandler {
 
         private DataRequestHandler() {
@@ -72,54 +73,46 @@
 
         @Override
         public void process(long sid, ObjectNode payload) {
-            // Search for drivers producing two artifacts:
-            // 1) list of abstract behaviours as column listing
-            // 2) sparse matrix of drivers-to-concrete behaviours
-
             DriverService driverService = get(DriverService.class);
 
-            // Collect all behaviours for all drivers
-            Map<Driver, Set<Class<? extends Behaviour>>> driverBehaviours = new HashMap<>();
-            driverService.getDrivers().forEach(d -> driverBehaviours.put(d, d.behaviours()));
-
-            // Order all drivers
-            List<Driver> drivers = orderDrivers(driverBehaviours.keySet());
+            List<Driver> drivers = new ArrayList<>(driverService.getDrivers());
+            drivers = orderDrivers(drivers);
 
             // Produce a union of all behaviours (and order them)
-            List<Class<? extends Behaviour>> behaviours = orderBehaviours(driverBehaviours.values());
+            List<Class<? extends Behaviour>> behaviours = orderBehaviours(drivers);
 
             // Produce a JSON structure and send it
-            sendMessage(DRIVER_DATA_RESPONSE, 0, driversJson(driverBehaviours, drivers, behaviours));
+            sendMessage(DRIVER_DATA_RESPONSE, 0, driversJson(drivers, behaviours));
         }
 
-        private List<Driver> orderDrivers(Set<Driver> drivers) {
+        private List<Driver> orderDrivers(List<Driver> drivers) {
             // For now order by alphanumeric name of the driver
-            List<Driver> ordered = new ArrayList<>(drivers);
-            ordered.sort(DRIVER_BY_NAME);
-            return ordered;
+            drivers.sort(DRIVER_BY_NAME);
+            return drivers;
         }
 
-        private List<Class<? extends Behaviour>>
-        orderBehaviours(Collection<Set<Class<? extends Behaviour>>> behaviours) {
-            // For now order by alphanumeric name of the abstract behaviour simple name
+        private List<Class<? extends Behaviour>> orderBehaviours(List<Driver> drivers) {
+            // first, produce a set-union of all behaviours from all drivers...
             Set<Class<? extends Behaviour>> allBehaviours = new HashSet<>();
-            behaviours.forEach(allBehaviours::addAll);
+            drivers.forEach(d -> allBehaviours.addAll(d.behaviours()));
+
+            // for now, order by alphanumeric name of the abstract behaviour simple name
             List<Class<? extends Behaviour>> ordered = new ArrayList<>(allBehaviours);
             ordered.sort(BEHAVIOUR_BY_NAME);
             return ordered;
         }
 
-        private ObjectNode driversJson(Map<Driver, Set<Class<? extends Behaviour>>> driverBehaviours,
-                                       List<Driver> drivers,
+        private ObjectNode driversJson(List<Driver> drivers,
                                        List<Class<? extends Behaviour>> behaviours) {
             ObjectNode root = objectNode();
             addBehaviours(root, behaviours);
             addDrivers(root, drivers);
-            addRelationships(root, drivers, behaviours, driverBehaviours);
+            addMatrixCells(root, drivers);
             return root;
         }
 
-        private void addBehaviours(ObjectNode root, List<Class<? extends Behaviour>> behaviours) {
+        private void addBehaviours(ObjectNode root,
+                                   List<Class<? extends Behaviour>> behaviours) {
             ArrayNode array = arrayNode();
             root.set(BEHAVIOURS, array);
             behaviours.forEach(b -> array.add(b.getSimpleName()));
@@ -131,9 +124,19 @@
             drivers.forEach(d -> array.add(d.name()));
         }
 
-        private void addRelationships(ObjectNode root,
-                                      List<Driver> drivers, List<Class<? extends Behaviour>> behaviours,
-                                      Map<Driver, Set<Class<? extends Behaviour>>> driverBehaviours) {
+        private void addMatrixCells(ObjectNode root, List<Driver> drivers) {
+            ObjectNode matrix = objectNode();
+            root.set(MATRIX, matrix);
+
+            drivers.forEach(d -> {
+                ObjectNode dnode = objectNode();
+                matrix.set(d.name(), dnode);
+
+                d.behaviours().forEach(b -> {
+                    // TODO: can put a payload here, rather than a '1' marker
+                    dnode.put(b.getSimpleName(), ONE);
+                });
+            });
         }
     }
 
diff --git a/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.css b/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.css
index 82ac4bb..441f129 100644
--- a/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.css
+++ b/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.css
@@ -1,9 +1,47 @@
-/* css for sample table view */
+/* css for driver matrix view */
 
 #ov-driver-matrix h2 {
     display: inline-block;
 }
 
+#ov-driver-matrix table {
+    margin-left: 20px;
+}
+
+#ov-driver-matrix .table-header-rotated {
+    border-collapse: collapse;
+}
+#ov-driver-matrix .table-header-rotated td {
+    width: 30px;
+}
+#ov-driver-matrix .table-header-rotated td.xmark {
+    background-color: yellow;
+}
+#ov-driver-matrix .table-header-rotated td {
+    text-align: center;
+    padding: 2px 5px;
+    border: 1px solid #ccc;
+}
+#ov-driver-matrix .table-header-rotated th.rotate {
+    height: 140px;
+    white-space: nowrap;
+}
+#ov-driver-matrix .table-header-rotated th.rotate > div {
+    -webkit-transform: translate(25px, 51px) rotate(-45deg);
+    transform: translate(25px, 51px) rotate(-45deg);
+    width: 30px;
+}
+#ov-driver-matrix .table-header-rotated th.rotate > div > span {
+    border-bottom: 1px solid #ccc;
+    padding: 5px 10px;
+}
+#ov-driver-matrix .table-header-rotated th.row-header {
+    padding: 0 10px;
+    border-bottom: 1px solid #ccc;
+    text-align: right;
+}
+
+
 /* Panel Styling */
 #ov-driver-matrix-item-details-panel.floatpanel {
     position: absolute;
@@ -33,3 +71,4 @@
     font-style: italic;
     opacity: 0.8;
 }
+
diff --git a/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.html b/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.html
index 940a887..5df2a46 100644
--- a/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.html
+++ b/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.html
@@ -1,8 +1,9 @@
 <!-- partial HTML -->
 <div id="ov-driver-matrix">
     <div class="tabular-header">
-        <h2>Items ({{tableData.length}} total)</h2>
+        <h2>Driver Matrix</h2>
         <div class="ctrl-btns">
+            <!-- TODO: fix (or remove) refresh button -->
             <div class="refresh" ng-class="{active: autoRefresh}"
                  icon icon-id="refresh" icon-size="36"
                  tooltip tt-msg="autoRefreshTip"
@@ -10,36 +11,35 @@
         </div>
     </div>
 
-    <div class="summary-list" onos-table-resize>
-
-        <div class="table-header" onos-sortable-header>
-            <table>
+    <!-- TODO: handle resizing / scrolling -->
+    <div class="driver-matrix">
+        <table class="table-header-rotated">
+            <thead>
                 <tr>
-                    <td colId="id" sortable>Item ID </td>
-                    <td colId="label" sortable>Label </td>
-                    <td colId="code" sortable>Code </td>
-                </tr>
-            </table>
-        </div>
-
-        <div class="table-body">
-            <table>
-                <tr ng-if="!tableData.length" class="no-data">
-                    <td colspan="3">
-                        No Items found
-                    </td>
+                    <!-- first column header is not rotated -->
+                    <th></th>
+                    <!-- following headers are rotated -->
+                    <th class="rotate" ng-repeat="beh in behaviours track by $index">
+                        <div><span>{{beh}}</span></div>
+                    </th>
                 </tr>
 
-                <tr ng-repeat="item in tableData track by $index"
-                    ng-click="selectCallback($event, item)"
-                    ng-class="{selected: item.id === selId}">
-                    <td>{{item.id}}</td>
-                    <td>{{item.label}}</td>
-                    <td>{{item.code}}</td>
-                </tr>
-            </table>
-        </div>
+            </thead>
 
+            <tbody>
+            <tr ng-repeat="drv in drivers track by $index">
+                <!--ng-click="selectCallback($event, item)"-->
+                <!--ng-class="{selected: item.id === selId}">-->
+                <th class="row-header">
+                    {{drv}}
+                </th>
+                <td ng-repeat="beh in behaviours track by $index"
+                    ng-class="{'xmark':cellMarked(drv, beh)}">
+                    {{cellValue(drv, beh)}}
+                </td>
+            </tr>
+            </tbody>
+        </table>
     </div>
 
     <ov-driver-matrix-item-details-panel></ov-driver-matrix-item-details-panel>
diff --git a/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.js b/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.js
index 3c77c87..80d46a3 100644
--- a/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.js
+++ b/apps/drivermatrix/src/main/resources/app/view/driverMatrix/driverMatrix.js
@@ -1,4 +1,4 @@
-// js for sample app table view
+// js for driver view
 (function () {
     'use strict';
 
@@ -8,8 +8,8 @@
     // constants
     var detailsReq = 'driverDataRequest',
         detailsResp = 'driverDataResponse',
+        // TODO: deal with details panel
         pName = 'ov-driver-matrix-item-details-panel',
-
         propOrder = ['id', 'label', 'code'],
         friendlyProps = ['Item ID', 'Item Label', 'Special Code'];
 
@@ -40,9 +40,11 @@
     }
 
     function respDetailsCb(data) {
-        $log.debug(data);
-        //$scope.panelDetails = data.details;
-        //$scope.$apply();
+        //$log.debug('Matrix Data', data);
+        $scope.behaviours = data.behaviours;
+        $scope.drivers = data.drivers;
+        $scope.matrix = data.matrix;
+        $scope.$apply();
     }
 
     angular.module('ovDriverMatrix', [])
@@ -57,7 +59,9 @@
                 wss = _wss_;
 
                 var handlers = {};
-                $scope.panelDetails = {};
+                $scope.behaviours = [];
+                $scope.drivers = [];
+                $scope.matrix = {};
 
                 // details response handler
                 handlers[detailsResp] = respDetailsCb;
@@ -75,12 +79,17 @@
                 //    $log.debug('Got a click on:', row);
                 //}
 
-                //// TableBuilderService creating a table for us
-                //tbs.buildTable({
-                //    scope: $scope,
-                //    tag: 'driverMatrix',
-                //    selCb: selCb
-                //});
+                function cellHit(d, b) {
+                    var drec = $scope.matrix[d],
+                        brec = drec && drec[b];
+                    return !!brec;
+                }
+
+                $scope.cellMarked = cellHit;
+
+                $scope.cellValue = function(d, b) {
+                    return cellHit(d, b) ? 'x' : '';
+                };
 
                 // cleanup
                 $scope.$on('$destroy', function () {
@@ -91,6 +100,7 @@
                 $log.log('OvDriverMatrixCtrl has been created');
             }])
 
+        // TODO: implement row selection to show details panel
         .directive('ovDriverMatrixItemDetailsPanel', ['PanelService', 'KeyService',
             function (ps, ks) {
             return {