ONOS-6730: Topo View i18n:
- augmented UiMessageHandler base class to allow injection of
  localization bundles, so that the handler can look up localized
  text when composing data to ship to the client.
- i18n'd the Summary Panel in Topo view.

Change-Id: I15010d1e2fcce72e3133a9ce40e51510c8f5146f
diff --git a/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java b/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java
index c2582c9..53125a7 100644
--- a/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java
+++ b/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java
@@ -22,6 +22,7 @@
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.JsonCodec;
+import org.onosproject.ui.lion.LionBundle;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -64,6 +65,7 @@
 
     private final Logger log = LoggerFactory.getLogger(getClass());
     private final Map<String, RequestHandler> handlerMap = new HashMap<>();
+    private final Map<String, LionBundle> cachedLionBundles = new HashMap<>();
 
     private final ObjectMapper mapper = new ObjectMapper();
 
@@ -181,6 +183,44 @@
     }
 
     /**
+     * Returns the set of identifiers for localization bundles that the
+     * message handler would like injected into itself, so that it can use
+     * those bundles in composing localized data to ship to the client.
+     * <p>
+     * This default implementation returns an empty set.
+     * <p>
+     * Subclasses that wish to have localization bundles injected should
+     * override this method and return the set of bundle identifiers.
+     *
+     * @return the set of identifiers of required localization bundles
+     */
+    public Set<String> requiredLionBundles() {
+        return Collections.emptySet();
+    }
+
+    /**
+     * Invoked during initialization to cache any requested localization
+     * bundles in the handler's context, so that it may subsequently look
+     * up localization strings when composing data for the client.
+     *
+     * @param bundle the bundle to cache
+     */
+    public void cacheLionBundle(LionBundle bundle) {
+        cachedLionBundles.put(bundle.id(), bundle);
+    }
+
+    /**
+     * Returns the localization bundle with the given identifier, if we
+     * requested to have it cached during initialization; null otherwise.
+     *
+     * @param id the lion bundle identifier
+     * @return the associated lion bundle
+     */
+    protected LionBundle getLionBundle(String id) {
+        return cachedLionBundles.get(id);
+    }
+
+    /**
      * Returns a freshly minted object node.
      *
      * @return new object node
diff --git a/core/api/src/main/java/org/onosproject/ui/lion/LionBundle.java b/core/api/src/main/java/org/onosproject/ui/lion/LionBundle.java
index e60ef14..780dea8 100644
--- a/core/api/src/main/java/org/onosproject/ui/lion/LionBundle.java
+++ b/core/api/src/main/java/org/onosproject/ui/lion/LionBundle.java
@@ -85,6 +85,18 @@
     }
 
     /**
+     * Returns the localized value for the given key, or, if no such mapping
+     * exists, returns the key wrapped in '%' characters.
+     *
+     * @param key the key
+     * @return the localized value (or a wrapped key placeholder)
+     */
+    public String getSafe(String key) {
+        String value = mapped.get(key);
+        return value == null ? "%" + key + "%" : value;
+    }
+
+    /**
      * Returns an immutable set of the items in this bundle.
      *
      * @return the items in this bundle
diff --git a/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java b/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java
index 8ce23d3..561fa76 100644
--- a/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java
+++ b/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java
@@ -97,11 +97,12 @@
      * Adds a property to the panel data.
      *
      * @param key   property key
+     * @param label property label (localized)
      * @param value property value
      * @return self, for chaining
      */
-    public PropertyPanel addProp(String key, String value) {
-        properties.add(new Prop(key, value));
+    public PropertyPanel addProp(String key, String label, String value) {
+        properties.add(new Prop(key, label, value));
         return this;
     }
 
@@ -109,11 +110,12 @@
      * Adds a property to the panel data, using a decimal formatter.
      *
      * @param key   property key
+     * @param label property label (localized)
      * @param value property value
      * @return self, for chaining
      */
-    public PropertyPanel addProp(String key, int value) {
-        properties.add(new Prop(key, formatter().format(value)));
+    public PropertyPanel addProp(String key, String label, int value) {
+        properties.add(new Prop(key, label, formatter().format(value)));
         return this;
     }
 
@@ -121,11 +123,12 @@
      * Adds a property to the panel data, using a decimal formatter.
      *
      * @param key   property key
+     * @param label property label (localized)
      * @param value property value
      * @return self, for chaining
      */
-    public PropertyPanel addProp(String key, long value) {
-        properties.add(new Prop(key, formatter().format(value)));
+    public PropertyPanel addProp(String key, String label, long value) {
+        properties.add(new Prop(key, label, formatter().format(value)));
         return this;
     }
 
@@ -135,11 +138,12 @@
      * value to a string.
      *
      * @param key   property key
+     * @param label property label (localized)
      * @param value property value
      * @return self, for chaining
      */
-    public PropertyPanel addProp(String key, Object value) {
-        properties.add(new Prop(key, value.toString()));
+    public PropertyPanel addProp(String key, String label, Object value) {
+        properties.add(new Prop(key, label, value.toString()));
         return this;
     }
 
@@ -150,14 +154,87 @@
      * regular expression string are stripped.
      *
      * @param key     property key
+     * @param label property label (localized)
+     * @param value   property value
+     * @param reStrip regexp characters to strip from value string
+     * @return self, for chaining
+     */
+    public PropertyPanel addProp(String key, String label,
+                                 Object value, String reStrip) {
+        String val = value.toString().replaceAll(reStrip, "");
+        properties.add(new Prop(key, label, val));
+        return this;
+    }
+
+    /*
+     * The following degenerate forms of addProp(...) for backward compatibility.
+     */
+
+    /**
+     * Adds a property to the panel data.
+     * Note that the key is used as the label.
+     *
+     * @param key   property key (also used as display label)
+     * @param value property value
+     * @return self, for chaining
+     * @see #addProp(String, String, String)
+     */
+    public PropertyPanel addProp(String key, String value) {
+        return addProp(key, key, value);
+    }
+
+    /**
+     * Adds a property to the panel data, using a decimal formatter.
+     * Note that the key is used as the label.
+     *
+     * @param key   property key (also used as display label)
+     * @param value property value
+     * @return self, for chaining
+     */
+    public PropertyPanel addProp(String key, int value) {
+        return addProp(key, key, value);
+    }
+
+    /**
+     * Adds a property to the panel data, using a decimal formatter.
+     * Note that the key is used as the label.
+     *
+     * @param key   property key (also used as display label)
+     * @param value property value
+     * @return self, for chaining
+     */
+    public PropertyPanel addProp(String key, long value) {
+        return addProp(key, key, value);
+    }
+
+    /**
+     * Adds a property to the panel data. Note that the value's
+     * {@link Object#toString toString()} method is used to convert the
+     * value to a string.
+     * Note also that the key is used as the label.
+     *
+     * @param key   property key (also used as display label)
+     * @param value property value
+     * @return self, for chaining
+     */
+    public PropertyPanel addProp(String key, Object value) {
+        return addProp(key, key, value);
+    }
+
+    /**
+     * Adds a property to the panel data. Note that the value's
+     * {@link Object#toString toString()} method is used to convert the
+     * value to a string, from which the characters defined in the given
+     * regular expression string are stripped.
+     * Note also that the key is used as the label.
+     *
+     * @param key     property key (also used as display label)
      * @param value   property value
      * @param reStrip regexp characters to strip from value string
      * @return self, for chaining
      */
     public PropertyPanel addProp(String key, Object value, String reStrip) {
-        String val = value.toString().replaceAll(reStrip, "");
-        properties.add(new Prop(key, val));
-        return this;
+        return addProp(key, key, value, reStrip);
     }
 
     /**
@@ -321,20 +398,23 @@
 
 
     /**
-     * Simple data carrier for a property, composed of a key/value pair.
+     * Simple data carrier for a property, composed of a key/label/value trio.
      */
     public static class Prop {
         private final String key;
+        private final String label;
         private final String value;
 
         /**
          * Constructs a property data value.
          *
-         * @param key   property key
+         * @param key   property key (localization key)
+         * @param label property label (localization value)
          * @param value property value
          */
-        public Prop(String key, String value) {
+        public Prop(String key, String label, String value) {
             this.key = key;
+            this.label = label;
             this.value = value;
         }
 
@@ -348,6 +428,15 @@
         }
 
         /**
+         * Returns the property's (localized) label.
+         *
+         * @return the label
+         */
+        public String label() {
+            return label;
+        }
+
+        /**
          * Returns the property's value.
          *
          * @return the value
@@ -356,6 +445,10 @@
             return value;
         }
 
+        /*
+         * NOTE: equals/hashCode are only expressed in terms of key and value.
+         */
+
         @Override
         public boolean equals(Object o) {
             if (this == o) {
@@ -378,7 +471,7 @@
 
         @Override
         public String toString() {
-            return "{" + key + " -> " + value + "}";
+            return "{" + key + "(" + label + ") -> " + value + "}";
         }
     }
 
@@ -387,7 +480,7 @@
      */
     public static class Separator extends Prop {
         public Separator() {
-            super("-", "");
+            super("-", "-", "");
         }
     }
 
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 7d52a5a..c5952a1 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
@@ -22,39 +22,40 @@
 public final class TopoConstants {
 
     /**
-     * Defines constants for property names on the default summary and
-     * details panels.
+     * Defines constants for property keys on the default summary and
+     * details panels. Note that display labels should be looked up using
+     * the "core.view.Topo" localization bundle (LionBundle).
      */
     public static final class Properties {
         public static final String SEPARATOR = "-";
 
         // summary panel
-        public static final String DEVICES = "Devices";
-        public static final String LINKS = "Links";
-        public static final String HOSTS = "Hosts";
-        public static final String TOPOLOGY_SSCS = "Topology SCCs";
-        public static final String INTENTS = "Intents";
-        public static final String TUNNELS = "Tunnels";
-        public static final String FLOWS = "Flows";
-        public static final String VERSION = "Version";
+        public static final String DEVICES = "devices";
+        public static final String LINKS = "links";
+        public static final String HOSTS = "hosts";
+        public static final String TOPOLOGY_SSCS = "topology_sccs";
+        public static final String INTENTS = "intents";
+        public static final String TUNNELS = "tunnels";
+        public static final String FLOWS = "flows";
+        public static final String VERSION = "version";
 
         // device details
-        public static final String URI = "URI";
-        public static final String VENDOR = "Vendor";
-        public static final String HW_VERSION = "H/W Version";
-        public static final String SW_VERSION = "S/W Version";
-        public static final String SERIAL_NUMBER = "Serial #";
-        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";
+        public static final String URI = "uri";
+        public static final String VENDOR = "vendor";
+        public static final String HW_VERSION = "hw_version";
+        public static final String SW_VERSION = "sw_version";
+        public static final String SERIAL_NUMBER = "serial_number";
+        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
-        public static final String MAC = "MAC";
-        public static final String IP = "IP";
-        public static final String VLAN = "VLAN";
+        public static final String MAC = "mac";
+        public static final String IP = "ip";
+        public static final String VLAN = "vlan";
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java b/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java
index c6e8079..a0646c7 100644
--- a/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java
+++ b/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java
@@ -49,7 +49,8 @@
     static final String TYPE = "type";
     static final String NAV_PATH = "navPath";
     static final String PROP_ORDER = "propOrder";
-    static final String PROPS = "props";
+    static final String PROP_LABELS = "propLabels";
+    static final String PROP_VALUES = "propValues";
     static final String BUTTONS = "buttons";
 
 
@@ -185,14 +186,17 @@
             result.put(NAV_PATH, pp.navPath());
         }
 
-        ObjectNode pnode = objectNode();
+        ObjectNode plabels = objectNode();
+        ObjectNode pvalues = objectNode();
         ArrayNode porder = arrayNode();
         for (PropertyPanel.Prop p : pp.properties()) {
             porder.add(p.key());
-            pnode.put(p.key(), p.value());
+            plabels.put(p.key(), p.label());
+            pvalues.put(p.key(), p.value());
         }
         result.set(PROP_ORDER, porder);
-        result.set(PROPS, pnode);
+        result.set(PROP_LABELS, plabels);
+        result.set(PROP_VALUES, pvalues);
 
         ArrayNode buttons = arrayNode();
         for (ButtonId b : pp.buttons()) {