Fix benchmark viewer.

Patch by: jat
Review by: tobyr, rdayal


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7289 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/RunWebApp.java b/dev/core/src/com/google/gwt/dev/RunWebApp.java
new file mode 100644
index 0000000..d4987b2
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/RunWebApp.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2009 Google Inc.
+ * 
+ * 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 com.google.gwt.dev;
+
+import com.google.gwt.core.ext.ServletContainer;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.DevModeBase.OptionPort;
+import com.google.gwt.dev.DevModeBase.OptionStartupURLs;
+import com.google.gwt.dev.shell.jetty.JettyLauncher;
+import com.google.gwt.dev.util.BrowserLauncher;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+import com.google.gwt.util.tools.ArgHandlerExtra;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An utility class for running web apps with Jetty and launching the default
+ * browser.
+ */
+public class RunWebApp {
+
+  interface RunWebAppOptions extends OptionStartupURLs, OptionPort {
+  }
+
+  static class RunWebAppOptionsImpl implements RunWebAppOptions {
+    private int port;
+    private final List<String> startupURLs = new ArrayList<String>();
+
+    public void addStartupURL(String url) {
+      startupURLs.add(url);
+    }
+
+    public int getPort() {
+      return port;
+    }
+
+    public List<String> getStartupURLs() {
+      return Collections.unmodifiableList(startupURLs);
+    }
+
+    public void setPort(int port) {
+      this.port = port;
+    }
+  }
+
+  private class ArgHandlerWar extends ArgHandlerExtra {
+    @Override
+    public boolean addExtraArg(String arg) {
+      warFile = new File(arg);
+      if (!warFile.exists()) {
+        System.err.println("Could not open war file '"
+            + warFile.getAbsolutePath() + "'");
+        return false;
+      }
+      return true;
+    }
+
+    @Override
+    public String getPurpose() {
+      return "Specifies the location of the target .war file or war directory";
+    }
+
+    @Override
+    public String[] getTagArgs() {
+      return new String[] {"war"};
+    }
+
+    @Override
+    public boolean isRequired() {
+      return true;
+    }
+  }
+
+  private class ArgProcessor extends ArgProcessorBase {
+    public ArgProcessor(RunWebAppOptions options) {
+      registerHandler(new DevMode.ArgHandlerStartupURLs(options));
+      registerHandler(new DevModeBase.ArgHandlerPort(options));
+      registerHandler(new ArgHandlerWar());
+    }
+
+    @Override
+    protected String getName() {
+      return RunWebApp.class.getName();
+    }
+  }
+
+  public static void main(String[] args) {
+    try {
+      RunWebAppOptionsImpl options = new RunWebAppOptionsImpl();
+      RunWebApp runWebApp = new RunWebApp(options);
+      ArgProcessor argProcessor = runWebApp.new ArgProcessor(options);
+      if (argProcessor.processArgs(args)) {
+        runWebApp.run();
+      }
+    } catch (Exception e) {
+      System.err.println("Unable to start Jetty server");
+      e.printStackTrace();
+    }
+  }
+
+  protected File warFile;
+
+  private final RunWebAppOptions options;
+
+  public RunWebApp(RunWebAppOptions options) {
+    this.options = options;
+  }
+
+  protected void run() {
+    PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
+    logger.setMaxDetail(TreeLogger.WARN);
+    int port = options.getPort();
+    try {
+      ServletContainer scl = new JettyLauncher().start(logger, port, warFile);
+      port = scl.getPort();
+    } catch (Exception e) {
+      System.err.println("Unable to start Jetty server");
+      e.printStackTrace();
+      return;
+    }
+    if (options.getStartupURLs().isEmpty()) {
+      options.addStartupURL("/");
+    }
+    for (String startupUrl : options.getStartupURLs()) {
+      startupUrl = DevModeBase.normalizeURL(startupUrl, port, "localhost");
+      try {
+        BrowserLauncher.browse(startupUrl);
+      } catch (IOException e) {
+        System.err.println("Unable to start " + startupUrl);
+      } catch (URISyntaxException e) {
+        System.err.println(startupUrl + " is not a valid URL");
+      }
+    }
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
index 2168df7..a4c60c0 100644
--- a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
+++ b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
@@ -476,7 +476,9 @@
   }
 
   private final Object privateInstanceLock = new Object();
-  private TreeLogger.Type baseLogLevel;
+  
+  // default value used if setBaseLogLevel isn't called
+  private TreeLogger.Type baseLogLevel = TreeLogger.INFO;
 
   @Override
   public String getIconPath() {
diff --git a/dev/core/src/com/google/gwt/dev/util/BrowserLauncher.java b/dev/core/src/com/google/gwt/dev/util/BrowserLauncher.java
index 55bb6e2..c567698 100644
--- a/dev/core/src/com/google/gwt/dev/util/BrowserLauncher.java
+++ b/dev/core/src/com/google/gwt/dev/util/BrowserLauncher.java
@@ -178,7 +178,7 @@
       throw new UnsupportedOperationException("no suitable browser found");
     }
 
-    public void browse(String url) throws IOException, URISyntaxException {
+    public void browse(String url) throws IOException {
       Runtime.getRuntime().exec(new String[] { browserExecutable, url });
       // TODO(jat): do we need to wait for it to exit and check exit status?
       // That would be best for Firefox, but bad for some of the other browsers.
@@ -190,7 +190,7 @@
    */
   private static class WindowsLauncher implements Launcher {
 
-    public void browse(String url) throws IOException, URISyntaxException {
+    public void browse(String url) throws IOException {
       Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
       // TODO(jat): do we need to wait for it to exit and check exit status?
     }
diff --git a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/server/ReportXml.java b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/server/ReportXml.java
index bcccbd8..b908fdd 100644
--- a/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/server/ReportXml.java
+++ b/tools/benchmark-viewer/src/com/google/gwt/benchmarks/viewer/server/ReportXml.java
@@ -26,7 +26,9 @@
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Hydrates a Report from its XML representation.
@@ -56,10 +58,18 @@
     report.setGwtVersion(element.getAttribute("gwt_version"));
 
     List<Element> children = getElementChildren(element, "category");
-    report.setCategories(new ArrayList<Category>(children.size()));
-    for (int i = 0; i < children.size(); ++i) {
-      report.getCategories().add(CategoryXml.fromXml(children.get(i)));
+    Map<String, Category> categories = new HashMap<String, Category>();
+    for (Element child : children) {
+      Category newCategory = CategoryXml.fromXml(child);
+      Category oldCategory = categories.get(newCategory.getName());
+      if (oldCategory != null) {
+        // if a category with the same name exists, combine the benchmarks
+        oldCategory.getBenchmarks().addAll(newCategory.getBenchmarks());
+      } else {
+        categories.put(newCategory.getName(), newCategory);
+      }
     }
+    report.setCategories(new ArrayList<Category>(categories.values()));
 
     return report;
   }