Added scale and offset parameters to LayoutAddCommand.
- Also, allow dot (.) to be used as a token for null.

Change-Id: Ic04e9624c03f7f45b14b0ec8a209b6804f6333c4
diff --git a/cli/src/main/java/org/onosproject/cli/net/LayoutAddCommand.java b/cli/src/main/java/org/onosproject/cli/net/LayoutAddCommand.java
index dd47e01..5b1604e 100644
--- a/cli/src/main/java/org/onosproject/cli/net/LayoutAddCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/LayoutAddCommand.java
@@ -29,6 +29,17 @@
 
 /**
  * Add a new UI layout.
+ * <p>
+ * <pre>
+ * layout-add {layout-id} {bg-ref} \
+ *   [ {region-id} {parent-layout-id} {scale} {offset-x} {offset-y} ]
+ * </pre>
+ * Note that if you want to skip a parameter, but set later parameters,
+ * use dot (".") as a placeholder for null. For example, no associated region
+ * or parent layout, but setting the scale and offset for the root layout...
+ * <pre>
+ * layout-add root @bayareaGEO . . 1.2 0.0 -4.0
+ * </pre>
  */
 @Command(scope = "onos", name = "layout-add",
         description = "Adds a new UI layout.")
@@ -36,8 +47,14 @@
 
     private static final char CODE_GEO = '@';
     private static final char CODE_GRID = '+';
+
+    private static final String NULL_TOKEN = ".";
     private static final String ROOT = "root";
 
+    private static final double DEFAULT_SCALE = 1.0;
+    private static final double DEFAULT_OFFSET = 0.0;
+
+
     @Argument(index = 0, name = "id", description = "Layout ID",
             required = true, multiValued = false)
     String id = null;
@@ -54,6 +71,18 @@
             required = false, multiValued = false)
     String parentId = null;
 
+    @Argument(index = 4, name = "scale", description = "Zoom scale (optional; default 1.0)",
+            required = false, multiValued = false)
+    String zoomScale = null;
+
+    @Argument(index = 5, name = "offx", description = "Zoom offset-X (optional; default 0.0)",
+            required = false, multiValued = false)
+    String zoomOffsetX = null;
+
+    @Argument(index = 6, name = "offy", description = "Zoom offset-Y (optional; default 0.0)",
+            required = false, multiValued = false)
+    String zoomOffsetY = null;
+
     private RegionService regionService;
 
     @Override
@@ -61,38 +90,73 @@
         UiTopoLayoutService service = get(UiTopoLayoutService.class);
         RegionService regionService = get(RegionService.class);
 
+        UiTopoLayout layout;
+
         if (ROOT.equals(id)) {
-            // set the background for the root layout
-            setAppropriateBackground(service.getRootLayout(), backgroundRef);
+            layout = service.getRootLayout();
+            setAppropriateBackground(layout);
+            setZoomParameters(layout);
             return;
         }
 
-        Region region = regionId == null ? null : regionService.getRegion(regionId(regionId));
-        UiTopoLayoutId pid = parentId == null ? UiTopoLayoutId.DEFAULT_ID : layoutId(parentId);
+        // Otherwise, it is a user-defined layout...
 
-        UiTopoLayout layout = new UiTopoLayout(layoutId(id)).region(region).parent(pid);
-        setAppropriateBackground(layout, backgroundRef);
+        Region region = nullToken(regionId) ? null : regionService.getRegion(regionId(regionId));
+        UiTopoLayoutId pid = nullToken(parentId) ? UiTopoLayoutId.DEFAULT_ID : layoutId(parentId);
+
+        layout = new UiTopoLayout(layoutId(id)).region(region).parent(pid);
+
+        setAppropriateBackground(layout);
+        setZoomParameters(layout);
         service.addLayout(layout);
     }
 
-    private void setAppropriateBackground(UiTopoLayout layout, String bgRef) {
+    private boolean nullToken(String token) {
+        return token == null || token.equals(NULL_TOKEN);
+    }
+
+    private void setAppropriateBackground(UiTopoLayout layout) {
         /*
          * A note about the format of bgref.. it should be one of:
          *    "."               - signifies no background
          *    "@{map-id}"       - signifies geo background (map)
          *    "+{sprite-id}"    - signifies grid background (sprite)
          *
-         *    For example, "!", "@bayareaGEO", "+segmentRouting"
+         *    For example, ".", "@bayareaGEO", "+segmentRouting"
          */
-        char type = bgRef.charAt(0);
+        char type = backgroundRef.charAt(0);
 
         if (type == CODE_GEO) {
             // GEO (map) reference
-            layout.geomap(bgRef.substring(1));
+            layout.geomap(backgroundRef.substring(1));
 
         } else if (type == CODE_GRID) {
             // Grid (sprite) reference
-            layout.sprites(bgRef.substring(1));
+            layout.sprites(backgroundRef.substring(1));
         }
+        // simply ignore null token (".")
+    }
+
+    private double parseDouble(String s, double def) {
+        if (nullToken(s)) {
+            return def;
+        }
+
+        double result;
+        try {
+            result = Double.parseDouble(s);
+        } catch (NumberFormatException e) {
+            result = def;
+        }
+
+        return result;
+    }
+
+    private void setZoomParameters(UiTopoLayout layout) {
+        double scale = parseDouble(zoomScale, DEFAULT_SCALE);
+        double offsetX = parseDouble(zoomOffsetX, DEFAULT_OFFSET);
+        double offsetY = parseDouble(zoomOffsetY, DEFAULT_OFFSET);
+
+        layout.scale(scale).offsetX(offsetX).offsetY(offsetY);
     }
 }
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 45fdb80..9c55b7d 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
@@ -27,7 +27,7 @@
  * Represents a specific "subset" of the UI model of the network topology
  * that a user might wish to view. Backed by a {@link Region}.
  * <p>
- * These instances include information about which geo-map (or sprite definition)
+ * These instances include information about which geo-map or grid-layout
  * should be displayed, along with zoom and offset parameters.
  */
 public class UiTopoLayout {
diff --git a/tools/test/topos/regions-bayarea-grid.sh b/tools/test/topos/regions-bayarea-grid.sh
index a7a3e4a..80716cf 100755
--- a/tools/test/topos/regions-bayarea-grid.sh
+++ b/tools/test/topos/regions-bayarea-grid.sh
@@ -262,13 +262,15 @@
 
 ### Add layouts, associating backing regions, and optional parent.
 #
-# layout-add <layout-id> <bg-ref> <region-id(opt)> <parent-layout-id(opt)>
+# layout-add <layout-id> <bg-ref> \
+#   [ <region-id> <parent-layout-id> <scale> <offset-x> <offset-y> ]
+#
 
 onos ${host} <<-EOF
 
-layout-add root @bayareaGEO
+layout-add root @bayareaGEO . . 0.4
 
-layout-add lC01 +segmentRouting c01
+layout-add lC01 +segmentRouting c01 . 0.9 5.2 -4.0
 layout-add lC02 +segmentRouting c02
 layout-add lC03 +segmentRouting c03
 layout-add lC04 . c04         # testing no-background