UI-Lion: start integration of LionBundles into UiExtension.

Change-Id: I5c693f7f218fcd4fecbc34e2a18ef3ee29ed1e45
diff --git a/core/api/src/main/java/org/onosproject/ui/UiExtension.java b/core/api/src/main/java/org/onosproject/ui/UiExtension.java
index 9edcba9a..5255fa0 100644
--- a/core/api/src/main/java/org/onosproject/ui/UiExtension.java
+++ b/core/api/src/main/java/org/onosproject/ui/UiExtension.java
@@ -16,6 +16,7 @@
 package org.onosproject.ui;
 
 import com.google.common.collect.ImmutableList;
+import org.onosproject.ui.lion.LionBundle;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,6 +46,7 @@
     private final ClassLoader classLoader;
     private final String resourcePath;
     private final List<UiView> viewList;
+    private final List<LionBundle> lionBundles;
     private final UiMessageHandlerFactory messageHandlerFactory;
     private final UiTopoOverlayFactory topoOverlayFactory;
     private final UiTopo2OverlayFactory topo2OverlayFactory;
@@ -54,6 +56,7 @@
 
     // private constructor - only the builder calls this
     private UiExtension(ClassLoader cl, String path, List<UiView> views,
+                        List<LionBundle> bundles,
                         UiMessageHandlerFactory mhFactory,
                         UiTopoOverlayFactory toFactory,
                         UiTopo2OverlayFactory to2Factory,
@@ -61,6 +64,7 @@
         classLoader = cl;
         resourcePath = path;
         viewList = views;
+        lionBundles = bundles;
         messageHandlerFactory = mhFactory;
         topoOverlayFactory = toFactory;
         topo2OverlayFactory = to2Factory;
@@ -96,6 +100,16 @@
     }
 
     /**
+     * Returns the list of localization bundles that this extension is
+     * contributing.
+     *
+     * @return contributed localization bundles
+     */
+    public List<LionBundle> lionBundles() {
+        return ImmutableList.copyOf(lionBundles);
+    }
+
+    /**
      * Returns input stream containing specified view-specific resource.
      *
      * @param viewId view identifier
@@ -162,6 +176,7 @@
 
         private String resourcePath = EMPTY;
         private List<UiView> viewList = new ArrayList<>();
+        private List<LionBundle> lionBundles = new ArrayList<>();
         private UiMessageHandlerFactory messageHandlerFactory = null;
         private UiTopoOverlayFactory topoOverlayFactory = null;
         private UiTopo2OverlayFactory topo2OverlayFactory = null;
@@ -184,6 +199,19 @@
         }
 
         /**
+         * Sets the localization bundles (aka {@code LionBundle}s) that this
+         * UI extension is contributing.
+         *
+         * @param bundles the bundles to register
+         * @return self, for chaining
+         */
+        public Builder lionBundles(List<LionBundle> bundles) {
+            checkNotNull(bundles, "Must provide a list");
+            lionBundles = bundles;
+            return this;
+        }
+
+        /**
          * Set the resource path. That is, the path to where the CSS and JS
          * files are located.
          *
@@ -246,6 +274,7 @@
          */
         public UiExtension build() {
             return new UiExtension(classLoader, resourcePath, viewList,
+                                   lionBundles,
                                    messageHandlerFactory, topoOverlayFactory,
                                    topo2OverlayFactory, topoMapFactory);
         }
diff --git a/core/api/src/main/java/org/onosproject/ui/lion/LionUtils.java b/core/api/src/main/java/org/onosproject/ui/lion/LionUtils.java
index c14409a..c7aecca 100644
--- a/core/api/src/main/java/org/onosproject/ui/lion/LionUtils.java
+++ b/core/api/src/main/java/org/onosproject/ui/lion/LionUtils.java
@@ -17,9 +17,13 @@
 
 package org.onosproject.ui.lion;
 
+import com.google.common.collect.ImmutableList;
+import org.onosproject.ui.lion.stitch.BundleStitcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 import java.util.ResourceBundle;
 import java.util.Set;
@@ -211,4 +215,58 @@
         sb.append(DOT).append(baseName);
         return ResourceBundle.getBundle(sb.toString());
     }
+
+    /**
+     * Generates an immutable list of localization bundles, using the specified
+     * resource tree (base) and localization configuration file names (tags).
+     * <p>
+     * As an example, you might invoke:
+     * <pre>
+     * private static final String LION_BASE = "/org/onosproject/ui/lion";
+     *
+     * private static final String[] LION_TAGS = {
+     *     "core.view.App",
+     *     "core.view.Settings",
+     *     "core.view.Cluster",
+     *     "core.view.Processor",
+     *     "core.view.Partition",
+     * };
+     *
+     * List&lt;LionBundle&gt; bundles =
+     *      LionUtils.generateLionBundles(LION_BASE, LION_TAGS);
+     * </pre>
+     * It is expected that in the "LION_BASE" directory there is a subdirectory
+     * named "_config" which contains the configuration files listed in the
+     * "LION_TAGS" array, each with a ".lioncfg" suffix...
+     * <pre>
+     * /org/onosproject/ui/lion/
+     *   |
+     *   +-- _config
+     *         |
+     *         +-- core.view.App.lioncfg
+     *         +-- core.view.Settings.lioncfg
+     *         :
+     * </pre>
+     * These files collate a localization bundle for their particular view
+     * by referencing resource bundles and their keys.
+     *
+     * @param base the base resource directory path
+     * @param tags the list of bundles to generate
+     * @return a list of generated localization bundles
+     */
+    public static List<LionBundle> generateLionBundles(String base,
+                                                       String... tags) {
+        List<LionBundle> result = new ArrayList<>(tags.length);
+        BundleStitcher stitcher = new BundleStitcher(base);
+        for (String tag : tags) {
+            try {
+                LionBundle b = stitcher.stitch(tag);
+                result.add(b);
+
+            } catch (IllegalArgumentException e) {
+                log.warn("Unable to generate bundle: {} / {}", base, tag);
+            }
+        }
+        return ImmutableList.copyOf(result);
+    }
 }
diff --git a/core/api/src/main/java/org/onosproject/ui/lion/stitch/BundleStitcher.java b/core/api/src/main/java/org/onosproject/ui/lion/stitch/BundleStitcher.java
index 612edd4..dc6e05f 100644
--- a/core/api/src/main/java/org/onosproject/ui/lion/stitch/BundleStitcher.java
+++ b/core/api/src/main/java/org/onosproject/ui/lion/stitch/BundleStitcher.java
@@ -25,7 +25,7 @@
 
 /**
  * Gathers and stitches together a localization bundle according to a
- *  "lion" configuration file.
+ * "lion" configuration file.
  */
 public class BundleStitcher {
 
@@ -58,6 +58,7 @@
      *
      * @param id the bundle ID
      * @return a corresponding lion bundle
+     * @throws IllegalArgumentException if the bundle config cannot be loaded
      */
     public LionBundle stitch(String id) {
         String source = base + SLASH + CONFIG_DIR + SLASH + id + SUFFIX;
diff --git a/core/api/src/main/java/org/onosproject/ui/lion/stitch/LionConfig.java b/core/api/src/main/java/org/onosproject/ui/lion/stitch/LionConfig.java
index 68e5f9d..71a423a 100644
--- a/core/api/src/main/java/org/onosproject/ui/lion/stitch/LionConfig.java
+++ b/core/api/src/main/java/org/onosproject/ui/lion/stitch/LionConfig.java
@@ -80,7 +80,7 @@
         InputStream is = getClass().getResourceAsStream(source);
         try {
             lines = IOUtils.readLines(is, UTF_8);
-        } catch (IOException e) {
+        } catch (NullPointerException | IOException e) {
             throw new IllegalArgumentException("Failed to read: " + source, e);
         }
 
@@ -173,7 +173,7 @@
     private boolean singleStarCheck(CmdFrom from) {
         from.starred = false;
         Set<String> keys = from.keys();
-        for (String k: keys) {
+        for (String k : keys) {
             if (STAR.equals(k)) {
                 from.starred = true;
             }
diff --git a/core/api/src/test/java/org/onosproject/ui/lion/LionUtilsTest.java b/core/api/src/test/java/org/onosproject/ui/lion/LionUtilsTest.java
index 2471277..957fe73 100644
--- a/core/api/src/test/java/org/onosproject/ui/lion/LionUtilsTest.java
+++ b/core/api/src/test/java/org/onosproject/ui/lion/LionUtilsTest.java
@@ -20,10 +20,11 @@
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
-import org.junit.Test;
 import org.junit.Ignore;
+import org.junit.Test;
 import org.onosproject.ui.AbstractUiTest;
 
+import java.util.List;
 import java.util.Locale;
 import java.util.ResourceBundle;
 
@@ -174,8 +175,30 @@
         checkLanguageCountry(locale, "ko", "KR");
     }
 
+    // -- Testing generateLionBundles(...)
+
+    private static final String LION_BASE = "/org/onosproject/ui/lion/stitchtests";
+
+    private static final String[] LION_TAGS = {"CardGame1"};
+
+    @Test
+    public void generateLionBundles() {
+        title("generateLionBundles");
+        List<LionBundle> bundles =
+                LionUtils.generateLionBundles(LION_BASE, LION_TAGS);
+        print(bundles);
+        assertEquals("missing the bundle", 1, bundles.size());
+
+        LionBundle b = bundles.get(0);
+        assertEquals("wrong id", "CardGame1", b.id());
+        assertEquals("unexpected item count", 12, b.size());
+        assertEquals("missing 3oak", "Three of a Kind", b.getValue("three_oak"));
+        assertEquals("missing queen", "Queen", b.getValue("queen"));
+        assertEquals("missing clubs", "Clubs", b.getValue("clubs"));
+    }
 
     // -- Testing loading of correct bundle, based on locale
+
     private void checkLookups(String computer, String disk, String monitor,
                               String keyboard) {
         res = LionUtils.getBundledResource(LionUtils.class, "MyBundle");
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 32d1427..85935de 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
@@ -104,6 +104,15 @@
     private static final int IDX_USER = 0;
     private static final int IDX_KEY = 1;
 
+    private static final String LION_BASE = "/org/onosproject/ui/lion";
+
+    private static final String[] LION_TAGS = {
+            "core.view.Cluster",
+
+            // TODO: fill this out, once we have written the other config files
+    };
+
+
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     // List of all extensions
@@ -214,6 +223,9 @@
                 );
 
         return new UiExtension.Builder(CL, coreViews)
+                // TODO: currently broken, until BundleStitcher & LionConfig
+                //        have been moved to web.gui module...
+//                .lionBundles(generateLionBundles(LION_BASE, LION_TAGS))
                 .messageHandlerFactory(messageHandlerFactory)
                 .topoOverlayFactory(topoOverlayFactory)
                 .topo2OverlayFactory(topo2OverlayFactory)
@@ -222,14 +234,15 @@
                 .build();
     }
 
+
     @Activate
     public void activate() {
         Serializer serializer = Serializer.using(KryoNamespaces.API,
-                     ObjectNode.class, ArrayNode.class,
-                     JsonNodeFactory.class, LinkedHashMap.class,
-                     TextNode.class, BooleanNode.class,
-                     LongNode.class, DoubleNode.class, ShortNode.class,
-                     IntNode.class, NullNode.class, UiSessionToken.class);
+                                                 ObjectNode.class, ArrayNode.class,
+                                                 JsonNodeFactory.class, LinkedHashMap.class,
+                                                 TextNode.class, BooleanNode.class,
+                                                 LongNode.class, DoubleNode.class, ShortNode.class,
+                                                 IntNode.class, NullNode.class, UiSessionToken.class);
 
         prefsConsistentMap = storageService.<String, ObjectNode>consistentMapBuilder()
                 .withName(ONOS_USER_PREFERENCES)
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/_config/core.view.cluster.lioncfg b/web/gui/src/main/resources/org/onosproject/ui/lion/_config/core.view.Cluster.lioncfg
similarity index 71%
rename from web/gui/src/main/resources/org/onosproject/ui/lion/_config/core.view.cluster.lioncfg
rename to web/gui/src/main/resources/org/onosproject/ui/lion/_config/core.view.Cluster.lioncfg
index 9d0e1ef..1cf7e03 100644
--- a/web/gui/src/main/resources/org/onosproject/ui/lion/_config/core.view.cluster.lioncfg
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/_config/core.view.Cluster.lioncfg
@@ -15,13 +15,13 @@
 #
 #
 
-bundle core.view.cluster
+bundle core.view.Cluster
 
 alias cv core.view
 alias cc core.common
 
-from cv.cluster import *
-from cc.network import devices, node_id, ip_address, tcp_port, uri, protocol
-from cc.props import type, chassis_id, vendor, hw_version, sw_version, serial_number
-from cc.state import total, active, started, last_updated
-from cc.ui import click, scroll_down
+from cv.Cluster import *
+from cc.Network import devices, node_id, ip_address, tcp_port, uri, protocol
+from cc.Props import type, chassis_id, vendor, hw_version, sw_version, serial_number
+from cc.State import total, active, started, last_updated
+from cc.Ui import click, scroll_down
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/action.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Action.properties
similarity index 100%
rename from web/gui/src/main/resources/org/onosproject/ui/lion/core/common/action.properties
rename to web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Action.properties
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/network.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Network.properties
similarity index 100%
rename from web/gui/src/main/resources/org/onosproject/ui/lion/core/common/network.properties
rename to web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Network.properties
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/props.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Props.properties
similarity index 100%
rename from web/gui/src/main/resources/org/onosproject/ui/lion/core/common/props.properties
rename to web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Props.properties
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/state.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/State.properties
similarity index 100%
rename from web/gui/src/main/resources/org/onosproject/ui/lion/core/common/state.properties
rename to web/gui/src/main/resources/org/onosproject/ui/lion/core/common/State.properties
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/ui.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Ui.properties
similarity index 100%
rename from web/gui/src/main/resources/org/onosproject/ui/lion/core/common/ui.properties
rename to web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Ui.properties
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/cluster.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/Cluster.properties
similarity index 100%
rename from web/gui/src/main/resources/org/onosproject/ui/lion/core/view/cluster.properties
rename to web/gui/src/main/resources/org/onosproject/ui/lion/core/view/Cluster.properties