changeset 128:9d043bbbdbcd

Move view related code from MainWindow controller to view class.
author Roman Kennke <rkennke@redhat.com>
date Wed, 21 Mar 2012 20:52:33 +0100
parents 9ae036f42ea8
children 17916c4207d3
files client/src/main/java/com/redhat/thermostat/client/HostsVMsLoader.java client/src/main/java/com/redhat/thermostat/client/MainWindowFacade.java client/src/main/java/com/redhat/thermostat/client/MainWindowFacadeImpl.java client/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java client/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java
diffstat 5 files changed, 211 insertions(+), 177 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/main/java/com/redhat/thermostat/client/HostsVMsLoader.java	Wed Mar 21 20:52:33 2012 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client;
+
+import java.util.Collection;
+
+import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.VmRef;
+
+/**
+ * Provides a way to load the current hosts and VMs.
+ */
+public interface HostsVMsLoader {
+
+    Collection<HostRef> getHosts();
+    Collection<VmRef> getVMs(HostRef host);
+}
--- a/client/src/main/java/com/redhat/thermostat/client/MainWindowFacade.java	Wed Mar 21 17:14:53 2012 +0100
+++ b/client/src/main/java/com/redhat/thermostat/client/MainWindowFacade.java	Wed Mar 21 20:52:33 2012 +0100
@@ -36,26 +36,16 @@
 
 package com.redhat.thermostat.client;
 
-import javax.swing.tree.TreeModel;
-
 import com.redhat.thermostat.client.ui.MainWindow;
-import com.redhat.thermostat.common.dao.HostRef;
-import com.redhat.thermostat.common.dao.VmRef;
 
 public interface MainWindowFacade extends AsyncUiFacade {
 
-    public HostRef[] getHosts();
-
-    public VmRef[] getVms(HostRef ref);
-
-    public TreeModel getHostVmTree();
-
     public void setHostVmTreeFilter(String filter);
 
     /**
      * TODO: This method is only here temporarily until we inversed the dependency between the MainWindow
      * and MainWindowFacadeImpl classes.
      */
-    public void initListeners(MainWindow mainWindow);
+    public void initView(MainWindow mainWindow);
 
 }
--- a/client/src/main/java/com/redhat/thermostat/client/MainWindowFacadeImpl.java	Wed Mar 21 17:14:53 2012 +0100
+++ b/client/src/main/java/com/redhat/thermostat/client/MainWindowFacadeImpl.java	Wed Mar 21 20:52:33 2012 +0100
@@ -36,42 +36,28 @@
 
 package com.redhat.thermostat.client;
 
-import static com.redhat.thermostat.client.locale.Translate.localize;
-
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
-import java.io.PrintStream;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
+import java.util.Collection;
 import java.util.List;
 import java.util.Timer;
 import java.util.TimerTask;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import javax.swing.SwingWorker;
-import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.DefaultTreeModel;
-import javax.swing.tree.TreeModel;
-import javax.swing.tree.TreeNode;
-
 import com.mongodb.BasicDBObject;
 import com.mongodb.DB;
 import com.mongodb.DBCollection;
 import com.mongodb.DBCursor;
 import com.mongodb.DBObject;
-import com.redhat.thermostat.client.locale.LocaleResources;
 import com.redhat.thermostat.client.ui.MainWindow;
 import com.redhat.thermostat.common.dao.HostRef;
-import com.redhat.thermostat.common.dao.Ref;
 import com.redhat.thermostat.common.dao.VmRef;
 import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.common.utils.StringUtils;
 
-public class MainWindowFacadeImpl implements MainWindowFacade {
+public class MainWindowFacadeImpl implements MainWindowFacade, HostsVMsLoader {
 
     private static final Logger logger = LoggingUtils.getLogger(MainWindowFacadeImpl.class);
 
@@ -79,13 +65,11 @@
     private final DBCollection hostInfoCollection;
     private final DBCollection vmInfoCollection;
 
-    private final DefaultMutableTreeNode publishedRoot = 
-            new DefaultMutableTreeNode(localize(LocaleResources.MAIN_WINDOW_TREE_ROOT_NAME));
-    private final DefaultTreeModel publishedTreeModel = new DefaultTreeModel(publishedRoot);
+    private final Timer backgroundUpdater = new Timer();
 
-    private String filterText;
+    private MainWindow view;
 
-    private final Timer backgroundUpdater = new Timer();
+    private String filter;
 
     public MainWindowFacadeImpl(DB db) {
         this.agentConfigCollection = db.getCollection("agent-config");
@@ -109,7 +93,7 @@
     }
 
     @Override
-    public HostRef[] getHosts() {
+    public Collection<HostRef> getHosts() {
         List<HostRef> hostRefs = new ArrayList<HostRef>();
 
         DBCursor cursor = agentConfigCollection.find();
@@ -124,11 +108,11 @@
             }
         }
         logger.log(Level.FINER, "found " + hostRefs.size() + " connected agents");
-        return hostRefs.toArray(new HostRef[0]);
+        return hostRefs;
     }
 
     @Override
-    public VmRef[] getVms(HostRef hostRef) {
+    public Collection<VmRef> getVMs(HostRef hostRef) {
         List<VmRef> vmRefs = new ArrayList<VmRef>();
         DBCursor cursor = vmInfoCollection.find(new BasicDBObject("agent-id", hostRef.getAgentId()));
         while (cursor.hasNext()) {
@@ -140,149 +124,22 @@
             vmRefs.add(ref);
         }
 
-        return vmRefs.toArray(new VmRef[0]);
-    }
-
-    @Override
-    public TreeModel getHostVmTree() {
-        return publishedTreeModel;
-    }
-
-    private Ref[] getChildren(Ref parent) {
-        if (parent == null) {
-            return getHosts();
-        } else if (parent instanceof HostRef) {
-            HostRef host = (HostRef) parent;
-            return getVms(host);
-        }
-        return new Ref[0];
+        return vmRefs;
     }
 
     @Override
     public void setHostVmTreeFilter(String filter) {
-        this.filterText = filter;
+        this.filter = filter;
         doUpdateTreeAsync();
     }
 
     public void doUpdateTreeAsync() {
-        BackgroundTreeModelWorker worker = new BackgroundTreeModelWorker(this, publishedTreeModel, publishedRoot);
-        worker.execute();
-    }
-
-    /**
-     * Updates a TreeModel in the background in an Swing EDT-safe manner.
-     */
-    private static class BackgroundTreeModelWorker extends SwingWorker<DefaultMutableTreeNode, Void> {
-
-        private final DefaultTreeModel treeModel;
-        private MainWindowFacadeImpl facade;
-        private DefaultMutableTreeNode treeRoot;
-
-        public BackgroundTreeModelWorker(MainWindowFacadeImpl facade, DefaultTreeModel model, DefaultMutableTreeNode root) {
-            this.facade = facade;
-            this.treeModel = model;
-            this.treeRoot = root;
-        }
-
-        @Override
-        protected DefaultMutableTreeNode doInBackground() throws Exception {
-            DefaultMutableTreeNode root = new DefaultMutableTreeNode();
-            List<HostRef> hostsInRemoteModel = Arrays.asList(facade.getHosts());
-            buildSubTree(root, hostsInRemoteModel, facade.filterText);
-            return root;
-        }
-
-        private boolean buildSubTree(DefaultMutableTreeNode parent, List<? extends Ref> objectsInRemoteModel, String filter) {
-            boolean subTreeMatches = false;
-            for (Ref inRemoteModel : objectsInRemoteModel) {
-                DefaultMutableTreeNode inTreeNode = new DefaultMutableTreeNode(inRemoteModel);
-
-                boolean shouldInsert = false;
-                if (filter == null || inRemoteModel.matches(filter)) {
-                    shouldInsert = true;
-                }
-
-                List<Ref> children = Arrays.asList(facade.getChildren(inRemoteModel));
-                boolean subtreeResult = buildSubTree(inTreeNode, children, filter);
-                if (subtreeResult) {
-                    shouldInsert = true;
-                }
-
-                if (shouldInsert) {
-                    parent.add(inTreeNode);
-                    subTreeMatches = true;
-                }
-            }
-            return subTreeMatches;
-        }
-
-        @Override
-        protected void done() {
-            DefaultMutableTreeNode sourceRoot;
-            try {
-                sourceRoot = get();
-                syncTree(sourceRoot, treeModel, treeRoot);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            } catch (ExecutionException e) {
-                e.printStackTrace();
-            }
-        }
-
-        private void syncTree(DefaultMutableTreeNode sourceRoot, DefaultTreeModel targetModel, DefaultMutableTreeNode targetNode) {
-            @SuppressWarnings("unchecked") // We know what we put into these trees.
-            List<DefaultMutableTreeNode> sourceChildren = Collections.list(sourceRoot.children());
-            @SuppressWarnings("unchecked")
-            List<DefaultMutableTreeNode> targetChildren = Collections.list(targetNode.children());
-            for (DefaultMutableTreeNode sourceChild : sourceChildren) {
-                Ref sourceRef = (Ref) sourceChild.getUserObject();
-                DefaultMutableTreeNode targetChild = null;
-                for (DefaultMutableTreeNode aChild : targetChildren) {
-                    Ref targetRef = (Ref) aChild.getUserObject();
-                    if (targetRef.equals(sourceRef)) {
-                        targetChild = aChild;
-                        break;
-                    }
-                }
-
-                if (targetChild == null) {
-                    targetChild = new DefaultMutableTreeNode(sourceRef);
-                    targetModel.insertNodeInto(targetChild, targetNode, targetNode.getChildCount());
-                }
-
-                syncTree(sourceChild, targetModel, targetChild);
-            }
-
-            for (DefaultMutableTreeNode targetChild : targetChildren) {
-                Ref targetRef = (Ref) targetChild.getUserObject();
-                boolean matchFound = false;
-                for (DefaultMutableTreeNode sourceChild : sourceChildren) {
-                    Ref sourceRef = (Ref) sourceChild.getUserObject();
-                    if (targetRef.equals(sourceRef)) {
-                        matchFound = true;
-                        break;
-                    }
-                }
-
-                if (!matchFound) {
-                    targetModel.removeNodeFromParent(targetChild);
-                }
-            }
-        }
-    }
-
-    @SuppressWarnings("unused") // Used for debugging but not in production code.
-    private static void printTree(PrintStream out, TreeNode node, int depth) {
-        out.println(StringUtils.repeat("  ", depth) + node.toString());
-        @SuppressWarnings("unchecked")
-        List<TreeNode> children = Collections.list(node.children());
-        for (TreeNode child : children) {
-            printTree(out, child, depth + 1);
-        }
+        view.updateTree(filter, this);
     }
 
     @Override
-    public void initListeners(MainWindow mainWindow) {
+    public void initView(MainWindow mainWindow) {
+        this.view = mainWindow;
         mainWindow.addViewPropertyListener(new PropertyChangeListener() {
 
             @Override
--- a/client/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java	Wed Mar 21 17:14:53 2012 +0100
+++ b/client/src/main/java/com/redhat/thermostat/client/ui/MainWindow.java	Wed Mar 21 20:52:33 2012 +0100
@@ -51,6 +51,11 @@
 import java.awt.event.WindowEvent;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
+import java.io.PrintStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
 
 import javax.swing.BorderFactory;
 import javax.swing.JFrame;
@@ -65,6 +70,7 @@
 import javax.swing.JTextField;
 import javax.swing.JTree;
 import javax.swing.KeyStroke;
+import javax.swing.SwingWorker;
 import javax.swing.ToolTipManager;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
@@ -78,18 +84,136 @@
 import javax.swing.tree.DefaultTreeCellRenderer;
 import javax.swing.tree.DefaultTreeModel;
 import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
 import javax.swing.tree.TreePath;
 import javax.swing.tree.TreeSelectionModel;
 
 import com.redhat.thermostat.client.ApplicationInfo;
+import com.redhat.thermostat.client.HostsVMsLoader;
 import com.redhat.thermostat.client.MainWindowFacade;
 import com.redhat.thermostat.client.UiFacadeFactory;
 import com.redhat.thermostat.client.locale.LocaleResources;
 import com.redhat.thermostat.common.dao.HostRef;
+import com.redhat.thermostat.common.dao.Ref;
 import com.redhat.thermostat.common.dao.VmRef;
+import com.redhat.thermostat.common.utils.StringUtils;
 
 public class MainWindow extends JFrame {
 
+    /**
+     * Updates a TreeModel in the background in an Swing EDT-safe manner.
+     */
+    private static class BackgroundTreeModelWorker extends SwingWorker<DefaultMutableTreeNode, Void> {
+
+        private final DefaultTreeModel treeModel;
+        private DefaultMutableTreeNode treeRoot;
+        private String filterText;
+        private HostsVMsLoader hostsVMsLoader;
+
+        public BackgroundTreeModelWorker(DefaultTreeModel model, DefaultMutableTreeNode root, String filterText, HostsVMsLoader hostsVMsLoader) {
+            this.filterText = filterText;
+            this.treeModel = model;
+            this.treeRoot = root;
+            this.hostsVMsLoader = hostsVMsLoader;
+        }
+
+        @Override
+        protected DefaultMutableTreeNode doInBackground() throws Exception {
+            DefaultMutableTreeNode root = new DefaultMutableTreeNode();
+            Collection<HostRef> hostsInRemoteModel = hostsVMsLoader.getHosts();
+            buildSubTree(root, hostsInRemoteModel, filterText);
+            return root;
+        }
+
+        private boolean buildSubTree(DefaultMutableTreeNode parent, Collection<? extends Ref> objectsInRemoteModel, String filter) {
+            boolean subTreeMatches = false;
+            for (Ref inRemoteModel : objectsInRemoteModel) {
+                DefaultMutableTreeNode inTreeNode = new DefaultMutableTreeNode(inRemoteModel);
+
+                boolean shouldInsert = false;
+                if (filter == null || inRemoteModel.matches(filter)) {
+                    shouldInsert = true;
+                }
+
+                Collection<? extends Ref> children = getChildren(inRemoteModel);
+                boolean subtreeResult = buildSubTree(inTreeNode, children, filter);
+                if (subtreeResult) {
+                    shouldInsert = true;
+                }
+
+                if (shouldInsert) {
+                    parent.add(inTreeNode);
+                    subTreeMatches = true;
+                }
+            }
+            return subTreeMatches;
+        }
+
+        @Override
+        protected void done() {
+            DefaultMutableTreeNode sourceRoot;
+            try {
+                sourceRoot = get();
+                syncTree(sourceRoot, treeModel, treeRoot);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            } catch (ExecutionException e) {
+                e.printStackTrace();
+            }
+        }
+
+        private Collection<? extends Ref> getChildren(Ref parent) {
+            if (parent == null) {
+                return hostsVMsLoader.getHosts();
+            } else if (parent instanceof HostRef) {
+                HostRef host = (HostRef) parent;
+                return hostsVMsLoader.getVMs(host);
+            }
+            return Collections.emptyList();
+        }
+
+        private void syncTree(DefaultMutableTreeNode sourceRoot, DefaultTreeModel targetModel, DefaultMutableTreeNode targetNode) {
+            @SuppressWarnings("unchecked") // We know what we put into these trees.
+            List<DefaultMutableTreeNode> sourceChildren = Collections.list(sourceRoot.children());
+            @SuppressWarnings("unchecked")
+            List<DefaultMutableTreeNode> targetChildren = Collections.list(targetNode.children());
+            for (DefaultMutableTreeNode sourceChild : sourceChildren) {
+                Ref sourceRef = (Ref) sourceChild.getUserObject();
+                DefaultMutableTreeNode targetChild = null;
+                for (DefaultMutableTreeNode aChild : targetChildren) {
+                    Ref targetRef = (Ref) aChild.getUserObject();
+                    if (targetRef.equals(sourceRef)) {
+                        targetChild = aChild;
+                        break;
+                    }
+                }
+
+                if (targetChild == null) {
+                    targetChild = new DefaultMutableTreeNode(sourceRef);
+                    targetModel.insertNodeInto(targetChild, targetNode, targetNode.getChildCount());
+                }
+
+                syncTree(sourceChild, targetModel, targetChild);
+            }
+
+            for (DefaultMutableTreeNode targetChild : targetChildren) {
+                Ref targetRef = (Ref) targetChild.getUserObject();
+                boolean matchFound = false;
+                for (DefaultMutableTreeNode sourceChild : sourceChildren) {
+                    Ref sourceRef = (Ref) sourceChild.getUserObject();
+                    if (targetRef.equals(sourceRef)) {
+                        matchFound = true;
+                        break;
+                    }
+                }
+
+                if (!matchFound) {
+                    targetModel.removeNodeFromParent(targetChild);
+                }
+            }
+        }
+    }
+
     private static final long serialVersionUID = 5608972421496808177L;
 
     // TODO: When we break out a view interface, this constant needs to go there.
@@ -109,6 +233,10 @@
 
     private PropertyChangeSupport viewPropertySupport = new PropertyChangeSupport(this);
 
+    private final DefaultMutableTreeNode publishedRoot = 
+            new DefaultMutableTreeNode(localize(LocaleResources.MAIN_WINDOW_TREE_ROOT_NAME));
+    private final DefaultTreeModel publishedTreeModel = new DefaultTreeModel(publishedRoot);
+
     public MainWindow(UiFacadeFactory facadeFactory) {
         super();
         
@@ -117,12 +245,12 @@
 
         this.facadeFactory = facadeFactory;
         this.facade = facadeFactory.getMainWindow();
-        facade.initListeners(this);
+        facade.initView(this);
         shutdownAction = new ShutdownClient(facade, this);
 
         searchField = new JTextField();
         searchField.setName("hostVMTreeFilter");
-        TreeModel model = facade.getHostVmTree();
+        TreeModel model = publishedTreeModel;
         agentVmTree = new JTree(model);
         model.addTreeModelListener(new KeepRootExpandedListener(agentVmTree));
         agentVmTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
@@ -401,4 +529,20 @@
     private void fireViewPropertyChange(String propertyName, Object oldValue, Object newValue) {
         viewPropertySupport.firePropertyChange(propertyName, oldValue, newValue);
     }
+
+    public void updateTree(String filter, HostsVMsLoader hostsVMsLoader) {
+        BackgroundTreeModelWorker worker = new BackgroundTreeModelWorker(publishedTreeModel, publishedRoot, filter, hostsVMsLoader);
+        worker.execute();
+    }
+
+    @SuppressWarnings("unused") // Used for debugging but not in production code.
+    private static void printTree(PrintStream out, TreeNode node, int depth) {
+        out.println(StringUtils.repeat("  ", depth) + node.toString());
+        @SuppressWarnings("unchecked")
+        List<TreeNode> children = Collections.list(node.children());
+        for (TreeNode child : children) {
+            printTree(out, child, depth + 1);
+        }
+    }
+
 }
--- a/client/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java	Wed Mar 21 17:14:53 2012 +0100
+++ b/client/src/test/java/com/redhat/thermostat/client/ui/MainWindowTest.java	Wed Mar 21 20:52:33 2012 +0100
@@ -44,11 +44,6 @@
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 
-import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.DefaultTreeModel;
-import javax.swing.tree.TreeModel;
-import javax.swing.tree.TreeNode;
-
 import org.apache.commons.lang3.ObjectUtils;
 import org.fest.swing.fixture.FrameFixture;
 import org.fest.swing.fixture.JTextComponentFixture;
@@ -94,9 +89,6 @@
     @Test
     public void testHostVMTreeFilterPropertySupport() {
         MainWindowFacade mainWindowFacade = mock(MainWindowFacade.class);
-        TreeNode root = new DefaultMutableTreeNode();
-        TreeModel treeModel = new DefaultTreeModel(root);
-        when(mainWindowFacade.getHostVmTree()).thenReturn(treeModel);
 
         SummaryPanelFacade summaryPanelFacade = mock(SummaryPanelFacade.class);
         when(summaryPanelFacade.getTotalConnectedAgents()).thenReturn(new ChangeableText("totalConnectedAgents"));