More plumbing of grid coordinates vs. geo coordinates.
- Added background reference parameter to layout command
- send correct location data to client for devices, hosts

Change-Id: Ic00bda76f4e4bc8d3e23e07a08f3bc5367ec85a9
diff --git a/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java b/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
index b950b9f..8d2a73a 100644
--- a/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
+++ b/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
@@ -24,6 +24,8 @@
  */
 public final class AnnotationKeys {
 
+    private static final double DEFAULT_VALUE = 1.0;
+
     // Prohibit instantiation
     private AnnotationKeys() {
     }
@@ -47,16 +49,36 @@
     public static final String UI_TYPE = "uiType";
 
     /**
-     * Annotation key for latitude (e.g. latitude of device).
+     * Annotation key for UI location type of device/host
+     * (either 'geo' or 'grid').
+     */
+    public static final String LOC_TYPE = "locType";
+
+    /**
+     * Annotation key for latitude (e.g. latitude of device/host
+     * in a geo-layout).
      */
     public static final String LATITUDE = "latitude";
 
     /**
-     * Annotation key for longitude (e.g. longitude of device).
+     * Annotation key for longitude (e.g. longitude of device/host
+     * in a geo-layout).
      */
     public static final String LONGITUDE = "longitude";
 
     /**
+     * Annotation key for grid-Y (e.g. y-coordinate of device/host
+     * in a grid-layout).
+     */
+    public static final String GRID_Y = "gridY";
+
+    /**
+     * Annotation key for grid-X (e.g. x-coordinate of device/host
+     * in a grid-layout).
+     */
+    public static final String GRID_X = "gridX";
+
+    /**
      * Annotation key for southbound protocol.
      */
     public static final String PROTOCOL = "protocol";
@@ -168,7 +190,7 @@
     /**
      * Returns the value annotated object for the specified annotation key.
      * The annotated value is expected to be String that can be parsed as double.
-     * If parsing fails, the returned value will be 1.0.
+     * If parsing fails, the returned value will be {@value DEFAULT_VALUE}.
      *
      * @param annotated annotated object whose annotated value is obtained
      * @param key       key of annotation
@@ -179,7 +201,7 @@
         try {
             value = Double.parseDouble(annotated.annotations().value(key));
         } catch (NumberFormatException e) {
-            value = 1.0;
+            value = DEFAULT_VALUE;
         }
         return value;
     }
diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java b/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java
index ebcdd82..6fa169e 100644
--- a/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java
@@ -35,9 +35,10 @@
 
     @Override
     public boolean isValid() {
-        return hasOnlyFields(ALLOWED, NAME, LATITUDE, LONGITUDE, UI_TYPE,
-                RACK_ADDRESS, OWNER, TYPE, DRIVER, MANUFACTURER, HW_VERSION,
-                SW_VERSION, SERIAL, MANAGEMENT_ADDRESS, DEVICE_KEY_ID);
+        return hasOnlyFields(ALLOWED, NAME, LOC_TYPE, LATITUDE, LONGITUDE,
+                GRID_Y, GRID_X, UI_TYPE, RACK_ADDRESS, OWNER, TYPE, DRIVER,
+                MANUFACTURER, HW_VERSION, SW_VERSION, SERIAL,
+                MANAGEMENT_ADDRESS, DEVICE_KEY_ID);
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/BasicElementConfig.java b/core/api/src/main/java/org/onosproject/net/config/basics/BasicElementConfig.java
index 65a2783..1d18e80 100644
--- a/core/api/src/main/java/org/onosproject/net/config/basics/BasicElementConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/config/basics/BasicElementConfig.java
@@ -48,15 +48,14 @@
     public static final String LONGITUDE = "longitude";
 
     /**
-     * Key for grid X coordinate.
-     */
-    public static final String GRID_X = "gridx";
-
-    /**
      * Key for grid Y coordinate.
      */
-    public static final String GRID_Y = "gridy";
+    public static final String GRID_Y = "gridY";
 
+    /**
+     * Key for grid X coordinate.
+     */
+    public static final String GRID_X = "gridX";
 
     /**
      * Key for rack address.
@@ -196,22 +195,17 @@
     }
 
     /**
-     * Returns element grid x-coordinate.
+     * Returns true if the grid coordinates (gridY and gridX) are set on
+     * this element; false otherwise.
+     * <p>
+     * It is assumed that elements will not be placed at {@code (0,0)}.
+     * If you really need to position the element there, consider setting the
+     * coordinates to something like {@code (0.000001, 0.000001)} instead.
      *
-     * @return element x-coordinate
+     * @return true if grid coordinates are set; false otherwise.
      */
-    public double gridX() {
-        return get(GRID_X, DEFAULT_COORD);
-    }
-
-    /**
-     * Sets the element grid x-coordinate.
-     *
-     * @param x new x-coordinate; null to clear
-     * @return self
-     */
-    public BasicElementConfig gridX(Double x) {
-        return (BasicElementConfig) setOrClear(GRID_X, x);
+    public boolean gridCoordsSet() {
+        return !doubleIsZero(gridY()) || !doubleIsZero(gridX());
     }
 
     /**
@@ -234,6 +228,25 @@
     }
 
     /**
+     * Returns element grid x-coordinate.
+     *
+     * @return element x-coordinate
+     */
+    public double gridX() {
+        return get(GRID_X, DEFAULT_COORD);
+    }
+
+    /**
+     * Sets the element grid x-coordinate.
+     *
+     * @param x new x-coordinate; null to clear
+     * @return self
+     */
+    public BasicElementConfig gridX(Double x) {
+        return (BasicElementConfig) setOrClear(GRID_X, x);
+    }
+
+    /**
      * Returns the element rack address.
      *
      * @return rack address; null if not set
diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java b/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
index 3758c4e..cb99aaf 100644
--- a/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
@@ -36,8 +36,8 @@
         // Location and IP addresses can be absent, but if present must be valid.
         this.location();
         this.ipAddresses();
-        return hasOnlyFields(ALLOWED, NAME, LATITUDE, LONGITUDE, UI_TYPE,
-                RACK_ADDRESS, OWNER, IPS, LOCATION);
+        return hasOnlyFields(ALLOWED, NAME, LOC_TYPE, LATITUDE, LONGITUDE,
+                GRID_Y, GRID_Y, UI_TYPE, RACK_ADDRESS, OWNER, IPS, LOCATION);
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopoLayout.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopoLayout.java
index b37f1df..45fdb80 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopoLayout.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopoLayout.java
@@ -202,7 +202,7 @@
 
     /**
      * Sets the name of the sprites definition for this layout. This is the
-     * symbolic name for a "json" file containing a definition of sprites,
+     * symbolic name for a definition of sprites,
      * which render as a symbolic background (e.g. a campus, or floor plan),
      * to be displayed in the topology view, for this layout.
      * <p>
diff --git a/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java b/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java
index bf1e829..dc5297a 100644
--- a/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java
+++ b/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java
@@ -47,6 +47,8 @@
         public static final String PROTOCOL = "Protocol";
         public static final String LATITUDE = "Latitude";
         public static final String LONGITUDE = "Longitude";
+        public static final String GRID_Y = "Grid Y";
+        public static final String GRID_X = "Grid X";
         public static final String PORTS = "Ports";
 
         // host details
diff --git a/core/api/src/test/java/org/onosproject/net/config/basics/BasicElementConfigTest.java b/core/api/src/test/java/org/onosproject/net/config/basics/BasicElementConfigTest.java
index f81f462..1c41843 100644
--- a/core/api/src/test/java/org/onosproject/net/config/basics/BasicElementConfigTest.java
+++ b/core/api/src/test/java/org/onosproject/net/config/basics/BasicElementConfigTest.java
@@ -110,6 +110,7 @@
     @Test
     public void defaultGridCoords() {
         print(cfg);
+        assertFalse("grid not origin?", cfg.gridCoordsSet());
         assertEquals("gridx", 0.0, cfg.gridX(), ZERO_THRESHOLD);
         assertEquals("gridy", 0.0, cfg.gridY(), ZERO_THRESHOLD);
     }
@@ -118,6 +119,7 @@
     public void someGridCoords() {
         cfg.gridX(35.0).gridY(49.7);
         print(cfg);
+        assertTrue("grid at origin?", cfg.gridCoordsSet());
         assertEquals("gridx", 35.0, cfg.gridX(), ZERO_THRESHOLD);
         assertEquals("gridy", 49.7, cfg.gridY(), ZERO_THRESHOLD);
     }
diff --git a/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java b/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java
index a9da3cc..ce3ee1a 100644
--- a/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java
+++ b/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java
@@ -19,25 +19,19 @@
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.Device;
 import org.onosproject.net.SparseAnnotations;
-import org.onosproject.net.config.ConfigOperator;
 import org.onosproject.net.config.basics.BasicDeviceConfig;
 import org.onosproject.net.device.DefaultDeviceDescription;
 import org.onosproject.net.device.DeviceDescription;
-import org.slf4j.Logger;
 
 import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Implementations of merge policies for various sources of device configuration
  * information. This includes applications, providers, and network configurations.
  */
-public final class BasicDeviceOperator implements ConfigOperator {
-
-    protected static final double DEFAULT_COORD = -1.0;
-    private static final Logger log = getLogger(BasicDeviceOperator.class);
+public final class BasicDeviceOperator extends BasicElementOperator {
 
     private BasicDeviceOperator() {
     }
@@ -46,83 +40,76 @@
      * Generates a DeviceDescription containing fields from a DeviceDescription and
      * a DeviceConfig.
      *
-     * @param bdc   the device config entity from network config
+     * @param cfg   the device config entity from network config
      * @param descr a DeviceDescription
      * @return DeviceDescription based on both sources
      */
-    public static DeviceDescription combine(BasicDeviceConfig bdc, DeviceDescription descr) {
-        if (bdc == null || descr == null) {
+    public static DeviceDescription combine(BasicDeviceConfig cfg, DeviceDescription descr) {
+        if (cfg == null || descr == null) {
             return descr;
         }
 
         Device.Type type = descr.type();
-        if (bdc.type() != null && bdc.type() != type) {
-            type = bdc.type();
+        if (cfg.type() != null && cfg.type() != type) {
+            type = cfg.type();
         }
         String manufacturer = descr.manufacturer();
-        if (bdc.manufacturer() != null && !bdc.manufacturer().equals(manufacturer)) {
-            manufacturer = bdc.manufacturer();
+        if (cfg.manufacturer() != null && !cfg.manufacturer().equals(manufacturer)) {
+            manufacturer = cfg.manufacturer();
         }
         String hwVersion = descr.hwVersion();
-        if (bdc.hwVersion() != null && !bdc.hwVersion().equals(hwVersion)) {
-            hwVersion = bdc.hwVersion();
+        if (cfg.hwVersion() != null && !cfg.hwVersion().equals(hwVersion)) {
+            hwVersion = cfg.hwVersion();
         }
         String swVersion = descr.swVersion();
-        if (bdc.swVersion() != null && !bdc.swVersion().equals(swVersion)) {
-            swVersion = bdc.swVersion();
+        if (cfg.swVersion() != null && !cfg.swVersion().equals(swVersion)) {
+            swVersion = cfg.swVersion();
         }
         String serial = descr.serialNumber();
-        if (bdc.serial() != null && !bdc.serial().equals(serial)) {
-            serial = bdc.serial();
+        if (cfg.serial() != null && !cfg.serial().equals(serial)) {
+            serial = cfg.serial();
         }
 
-        SparseAnnotations sa = combine(bdc, descr.annotations());
+        SparseAnnotations sa = combine(cfg, descr.annotations());
         return new DefaultDeviceDescription(descr.deviceUri(), type, manufacturer,
-                                            hwVersion, swVersion,
-                                            serial, descr.chassisId(),
-                                            descr.isDefaultAvailable(), sa);
+                hwVersion, swVersion,
+                serial, descr.chassisId(),
+                descr.isDefaultAvailable(), sa);
     }
 
     /**
      * Generates an annotation from an existing annotation and DeviceConfig.
      *
-     * @param bdc the device config entity from network config
+     * @param cfg the device config entity from network config
      * @param an  the annotation
      * @return annotation combining both sources
      */
-    public static SparseAnnotations combine(BasicDeviceConfig bdc, SparseAnnotations an) {
-        DefaultAnnotations.Builder newBuilder = DefaultAnnotations.builder();
-        if (!Objects.equals(bdc.driver(), an.value(AnnotationKeys.DRIVER))) {
-            newBuilder.set(AnnotationKeys.DRIVER, bdc.driver());
+    public static SparseAnnotations combine(BasicDeviceConfig cfg, SparseAnnotations an) {
+        DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
+        if (!Objects.equals(cfg.driver(), an.value(AnnotationKeys.DRIVER))) {
+            builder.set(AnnotationKeys.DRIVER, cfg.driver());
         }
-        if (bdc.name() != null) {
-            newBuilder.set(AnnotationKeys.NAME, bdc.name());
+
+        combineElementAnnotations(cfg, builder);
+
+        if (cfg.managementAddress() != null) {
+            builder.set(AnnotationKeys.MANAGEMENT_ADDRESS, cfg.managementAddress());
         }
-        if (bdc.uiType() != null) {
-            newBuilder.set(AnnotationKeys.UI_TYPE, bdc.uiType());
-        }
-        if (bdc.geoCoordsSet()) {
-            newBuilder.set(AnnotationKeys.LATITUDE, Double.toString(bdc.latitude()));
-            newBuilder.set(AnnotationKeys.LONGITUDE, Double.toString(bdc.longitude()));
-        }
-        if (bdc.rackAddress() != null) {
-            newBuilder.set(AnnotationKeys.RACK_ADDRESS, bdc.rackAddress());
-        }
-        if (bdc.owner() != null) {
-            newBuilder.set(AnnotationKeys.OWNER, bdc.owner());
-        }
-        if (bdc.managementAddress() != null) {
-            newBuilder.set(AnnotationKeys.MANAGEMENT_ADDRESS, bdc.managementAddress());
-        }
-        DefaultAnnotations newAnnotations = newBuilder.build();
-        return DefaultAnnotations.union(an, newAnnotations);
+
+        return DefaultAnnotations.union(an, builder.build());
     }
 
+    /**
+     * Returns a description of the given device.
+     *
+     * @param device the device
+     * @return a description of the device
+     */
     public static DeviceDescription descriptionOf(Device device) {
         checkNotNull(device, "Must supply non-null Device");
         return new DefaultDeviceDescription(device.id().uri(), device.type(),
-                                            device.manufacturer(), device.hwVersion(),
-                                            device.swVersion(), device.serialNumber(),
-                                            device.chassisId(), (SparseAnnotations) device.annotations());
+                device.manufacturer(), device.hwVersion(),
+                device.swVersion(), device.serialNumber(),
+                device.chassisId(), (SparseAnnotations) device.annotations());
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/device/impl/BasicElementOperator.java b/core/net/src/main/java/org/onosproject/net/device/impl/BasicElementOperator.java
new file mode 100644
index 0000000..9177f51
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/device/impl/BasicElementOperator.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.net.device.impl;
+
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.config.ConfigOperator;
+import org.onosproject.net.config.basics.BasicElementConfig;
+
+/**
+ * Abstract base implementation for element operators.
+ */
+public abstract class BasicElementOperator implements ConfigOperator {
+
+    /**
+     * Sets all defined values from the element config on the supplied
+     * annotations builder.
+     *
+     * @param cfg     element configuration
+     * @param builder annotations builder
+     */
+    protected static void combineElementAnnotations(BasicElementConfig cfg,
+                                                    DefaultAnnotations.Builder builder) {
+
+        if (cfg.name() != null) {
+            builder.set(AnnotationKeys.NAME, cfg.name());
+        }
+        if (cfg.uiType() != null) {
+            builder.set(AnnotationKeys.UI_TYPE, cfg.uiType());
+        }
+
+        if (cfg.locType() != null) {
+            builder.set(AnnotationKeys.LOC_TYPE, cfg.locType());
+        }
+        if (cfg.geoCoordsSet()) {
+            builder.set(AnnotationKeys.LATITUDE, Double.toString(cfg.latitude()));
+            builder.set(AnnotationKeys.LONGITUDE, Double.toString(cfg.longitude()));
+        } else if (cfg.gridCoordsSet()) {
+            builder.set(AnnotationKeys.GRID_Y, Double.toString(cfg.gridY()));
+            builder.set(AnnotationKeys.GRID_X, Double.toString(cfg.gridX()));
+        }
+
+        if (cfg.rackAddress() != null) {
+            builder.set(AnnotationKeys.RACK_ADDRESS, cfg.rackAddress());
+        }
+        if (cfg.owner() != null) {
+            builder.set(AnnotationKeys.OWNER, cfg.owner());
+        }
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java b/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
index a35eecf..97b87f0 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
@@ -16,13 +16,12 @@
 package org.onosproject.net.host.impl;
 
 import org.onlab.packet.IpAddress;
-import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.HostLocation;
 import org.onosproject.net.SparseAnnotations;
-import org.onosproject.net.config.ConfigOperator;
 import org.onosproject.net.config.basics.BasicHostConfig;
+import org.onosproject.net.device.impl.BasicElementOperator;
 import org.onosproject.net.host.DefaultHostDescription;
 import org.onosproject.net.host.HostDescription;
 
@@ -32,7 +31,7 @@
  * Implementations of merge policies for various sources of host configuration
  * information. This includes applications, providers, and network configurations.
  */
-public final class BasicHostOperator implements ConfigOperator {
+public final class BasicHostOperator extends BasicElementOperator {
 
     private BasicHostOperator() {
     }
@@ -65,36 +64,22 @@
 
         SparseAnnotations sa = combine(cfg, descr.annotations());
         return new DefaultHostDescription(descr.hwAddress(), descr.vlan(),
-                                          location, ipAddresses,
-                                          descr.configured(), sa);
+                location, ipAddresses,
+                descr.configured(), sa);
     }
 
     /**
      * Generates an annotation from an existing annotation and HostConfig.
      *
-     * @param cfg the device config entity from network config
+     * @param cfg the host config entity from network config
      * @param an  the annotation
      * @return annotation combining both sources
      */
-    public static SparseAnnotations combine(BasicHostConfig cfg,
-                                            SparseAnnotations an) {
-        DefaultAnnotations.Builder newBuilder = DefaultAnnotations.builder();
-        if (cfg.name() != null) {
-            newBuilder.set(AnnotationKeys.NAME, cfg.name());
-        }
-        if (cfg.uiType() != null) {
-            newBuilder.set(AnnotationKeys.UI_TYPE, cfg.uiType());
-        }
-        if (cfg.geoCoordsSet()) {
-            newBuilder.set(AnnotationKeys.LATITUDE, Double.toString(cfg.latitude()));
-            newBuilder.set(AnnotationKeys.LONGITUDE, Double.toString(cfg.longitude()));
-        }
-        if (cfg.rackAddress() != null) {
-            newBuilder.set(AnnotationKeys.RACK_ADDRESS, cfg.rackAddress());
-        }
-        if (cfg.owner() != null) {
-            newBuilder.set(AnnotationKeys.OWNER, cfg.owner());
-        }
-        return DefaultAnnotations.union(an, newBuilder.build());
+    public static SparseAnnotations combine(BasicHostConfig cfg, SparseAnnotations an) {
+        DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
+
+        combineElementAnnotations(cfg, builder);
+
+        return DefaultAnnotations.union(an, builder.build());
     }
 }