Add Gerrit screen for showing project documentation

Add a new Gerrit screen that shows the rendered project documentation
and some information about the viewed file in a header above it.
For now the header only shows the project, the file name and the
revision. In the future more functionality can be added here, e.g.
switch to edit mode or browsing of the file hierarchy.

The screen is available under
  #/x/project/<project-name>/<file-name>
and
  #/x/project/<project-name>/rev/<revision>/<file-name>

Change-Id: Ie9025cab92bdeea4d925c11fd8f60f5dc512e955
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/BUCK b/BUCK
index 15e3ecf..99a8eca 100644
--- a/BUCK
+++ b/BUCK
@@ -1,12 +1,15 @@
 include_defs('//bucklets/gerrit_plugin.bucklet')
 
+MODULE = 'com.googlesource.gerrit.plugins.xdocs.XDocs'
+
 ASCIIDOCTOR = '//lib/asciidoctor:asciidoc_lib' if __standalone_mode__ \
   else '//plugins/x-docs/lib/asciidoctor:asciidoc_lib'
 
 gerrit_plugin(
   name = 'x-docs',
   srcs = glob(['src/main/java/**/*.java']),
-  resources = glob(['src/main/resources/**/*']),
+  resources = glob(['src/main/**/*']),
+  gwt_module = MODULE,
   manifest_entries = [
     'Gerrit-PluginName: xdocs',
     'Gerrit-ApiType: plugin',
diff --git a/lib/gerrit/BUCK b/lib/gerrit/BUCK
index a197eb1..df798a7 100644
--- a/lib/gerrit/BUCK
+++ b/lib/gerrit/BUCK
@@ -10,3 +10,11 @@
   repository = REPO,
   license = 'Apache2.0',
 )
+
+maven_jar(
+  name = 'gwtui-api',
+  id = 'com.google.gerrit:gerrit-plugin-gwtui:' + VER,
+  attach_source = False,
+  repository = REPO,
+  license = 'Apache2.0',
+)
diff --git a/lib/gwt/BUCK b/lib/gwt/BUCK
new file mode 100644
index 0000000..4135b4c
--- /dev/null
+++ b/lib/gwt/BUCK
@@ -0,0 +1,34 @@
+include_defs('//bucklets/maven_jar.bucklet')
+
+VERSION = '2.6.1'
+
+maven_jar(
+  name = 'user',
+  id = 'com.google.gwt:gwt-user:' + VERSION,
+  sha1 = 'c078b1b8cc0281214b0eb458d2c283d039374fad',
+  license = 'Apache2.0',
+  attach_source = False,
+)
+
+maven_jar(
+  name = 'dev',
+  id = 'com.google.gwt:gwt-dev:' + VERSION,
+  sha1 = 'db237e4be0aa1fe43425d2c51ab5485dba211ddd',
+  license = 'Apache2.0',
+  deps = [
+    ':javax-validation',
+    ':javax-validation_src',
+  ],
+  attach_source = False,
+  exclude = ['org/eclipse/jetty/*'],
+)
+
+maven_jar(
+  name = 'javax-validation',
+  id = 'javax.validation:validation-api:1.0.0.GA',
+  bin_sha1 = 'b6bd7f9d78f6fdaa3c37dae18a4bd298915f328e',
+  src_sha1 = '7a561191db2203550fbfa40d534d4997624cd369',
+  license = 'Apache2.0',
+  visibility = [],
+)
+
diff --git a/pom.xml b/pom.xml
index ed4a7da..492a79b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,6 +68,25 @@
       </plugin>
 
       <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>gwt-maven-plugin</artifactId>
+        <version>2.6.1</version>
+        <configuration>
+          <module>com.googlesource.gerrit.plugins.xdocs.XDocs</module>
+          <disableClassMetadata>true</disableClassMetadata>
+          <disableCastChecking>true</disableCastChecking>
+          <webappDirectory>${project.build.directory}/classes/static</webappDirectory>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>compile</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-shade-plugin</artifactId>
         <version>2.3</version>
@@ -95,6 +114,18 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
+      <groupId>com.google.gerrit</groupId>
+      <artifactId>gerrit-plugin-gwtui</artifactId>
+      <version>${Gerrit-ApiVersion}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.gwt</groupId>
+      <artifactId>gwt-user</artifactId>
+      <version>2.6.1</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
       <groupId>org.asciidoctor</groupId>
       <artifactId>asciidoctorj</artifactId>
       <version>1.5.1</version>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/HttpModule.java
index 8355602..dae128e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/HttpModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/HttpModule.java
@@ -14,6 +14,9 @@
 
 package com.googlesource.gerrit.plugins.xdocs;
 
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.webui.GwtPlugin;
+import com.google.gerrit.extensions.webui.WebUiPlugin;
 import com.google.inject.servlet.ServletModule;
 
 class HttpModule extends ServletModule {
@@ -21,5 +24,8 @@
   protected void configureServlets() {
     serveRegex("^" + XDocServlet.PATH_PREFIX + "(.+)?$")
         .with(XDocServlet.class);
+
+    DynamicSet.bind(binder(), WebUiPlugin.class)
+        .toInstance(new GwtPlugin("xdocs"));
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocWebLink.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocWebLink.java
index 400c5d7..8ee9d61 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocWebLink.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocWebLink.java
@@ -74,20 +74,21 @@
   @Override
   public WebLinkInfo getBranchWebLink(String projectName, String branchName) {
     return new WebLinkInfo(README, getImageUrl(),
-        getBranchUrl(projectName, branchName), WebLinkTarget.BLANK);
+        getBranchUrl(projectName, branchName), WebLinkTarget.SELF);
   }
 
   @Override
   public WebLinkInfo getProjectWeblink(String projectName) {
     return new WebLinkInfo(README, getImageUrl(),
-        getBranchUrl(projectName, Constants.HEAD), WebLinkTarget.BLANK);
+        getBranchUrl(projectName, Constants.HEAD), WebLinkTarget.SELF);
   }
 
   @Override
   public WebLinkInfo getFileWebLink(String projectName, String revision,
       String fileName) {
     return new WebLinkInfo(PREVIEW, getImageUrl(),
-        getFileUrl(projectName, revision, fileName), WebLinkTarget.BLANK);
+        getFileUrl(projectName, revision, fileName, false),
+        WebLinkTarget.BLANK);
   }
 
   private String getBranchUrl(String projectName, String branchName) {
@@ -97,11 +98,11 @@
       return null;
     }
     return getFileUrl(projectName, branchName,
-        cfgFactory.create(state).getIndexFile());
+        cfgFactory.create(state).getIndexFile(), true);
   }
 
   public String getFileUrl(String projectName, String revision,
-      String fileName) {
+      String fileName, boolean framed) {
     FormatterProvider formatter = formatters.get(projectName, fileName);
     if (formatter == null) {
       return null;
@@ -118,7 +119,11 @@
         Resource rsc = docCache.get(formatter, p, fileName, revId);
         if (rsc != Resource.NOT_FOUND) {
           StringBuilder url = new StringBuilder();
-          url.append("plugins/");
+          if (framed) {
+            url.append("#/x/");
+          } else {
+            url.append("plugins/");
+          }
           url.append(pluginName);
           url.append(XDocServlet.PATH_PREFIX);
           url.append(Url.encode(projectName));
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocs.gwt.xml b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocs.gwt.xml
new file mode 100644
index 0000000..7855c78
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/XDocs.gwt.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ 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.
+-->
+<module rename-to="xdocs">
+  <!-- Inherit the core Web Toolkit stuff.                        -->
+  <inherits name="com.google.gwt.user.User"/>
+  <!-- Other module inherits                                      -->
+  <inherits name="com.google.gerrit.Plugin"/>
+  <inherits name="com.google.gwt.http.HTTP"/>
+  <inherits name="com.google.gwt.json.JSON"/>
+  <inherits name='com.google.gwtexpui.clippy.Clippy'/>
+  <inherits name='com.google.gwtexpui.globalkey.GlobalKey'/>
+  <!-- Using GWT built-in themes adds a number of static          -->
+  <!-- resources to the plugin. No theme inherits lines were      -->
+  <!-- added in order to make this plugin as simple as possible   -->
+  <!-- Specify the app entry point class.                         -->
+  <entry-point class="com.googlesource.gerrit.plugins.xdocs.client.XDocsPlugin"/>
+  <stylesheet src="xdocs.css"/>
+</module>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/client/XDocScreen.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/client/XDocScreen.java
new file mode 100644
index 0000000..91f34fd
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/client/XDocScreen.java
@@ -0,0 +1,87 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// 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.googlesource.gerrit.plugins.xdocs.client;
+
+import com.google.gerrit.plugin.client.Plugin;
+import com.google.gerrit.plugin.client.screen.Screen;
+import com.google.gwt.http.client.URL;
+import com.google.gwt.user.client.ui.Frame;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.InlineHyperlink;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+public class XDocScreen extends VerticalPanel {
+  static class Factory implements Screen.EntryPoint {
+    @Override
+    public void onLoad(Screen screen) {
+      String projectName = URL.decode(screen.getToken(1));
+      String revision = URL.decode(screen.getToken(2));
+      String fileName = URL.decode(screen.getToken(3));
+      screen.show(new XDocScreen(projectName, revision, fileName));
+    }
+  }
+
+  static class HeadFactory implements Screen.EntryPoint {
+    @Override
+    public void onLoad(Screen screen) {
+      String projectName = URL.decode(screen.getToken(1));
+      String fileName = URL.decode(screen.getToken(2));
+      screen.show(new XDocScreen(projectName, "HEAD", fileName));
+    }
+  }
+
+  XDocScreen(String projectName, String revision, String fileName) {
+    HorizontalPanel p = new HorizontalPanel();
+    p.add(new InlineHyperlink(projectName, "/admin/projects/" + projectName));
+    p.add(new Label(" / " + fileName + " (" + revision + ")"));
+    add(p);
+
+    setStyleName("xdocs-panel");
+    String frameId = "xdoc_iframe";
+    Frame frame = new Frame(getUrl(projectName, revision, fileName));
+    frame.getElement().setId(frameId);
+    resize(frame, frameId);
+    add(frame);
+  }
+
+  private String getUrl(String projectName, String revision, String fileName) {
+    StringBuilder url = new StringBuilder();
+    url.append("plugins/");
+    url.append(Plugin.get().getName());
+    url.append("/project/");
+    url.append(URL.encodeQueryString(projectName));
+    if (revision != null && !"HEAD".equals(revision)) {
+      url.append("/rev/");
+      url.append(URL.encodeQueryString(revision));
+    }
+    url.append("/");
+    url.append(URL.encodeQueryString(fileName));
+    return url.toString();
+  }
+
+  private void resize(Widget w, String id) {
+    StringBuilder autoResizeScript = new StringBuilder();
+    autoResizeScript.append("if (document.getElementById) {");
+    autoResizeScript.append("var e = document.getElementById(\"");
+    autoResizeScript.append(id);
+    autoResizeScript.append("\"); ");
+    autoResizeScript.append("e.width = (e.contentWindow.document .body.scrollWidth) + \"px\"; ");
+    autoResizeScript.append("e.height = (e.contentWindow.document .body.scrollHeight) + \"px\"; ");
+    autoResizeScript.append("}");
+    w.getElement().setAttribute("onLoad", autoResizeScript.toString());
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/client/XDocsPlugin.java b/src/main/java/com/googlesource/gerrit/plugins/xdocs/client/XDocsPlugin.java
new file mode 100644
index 0000000..10c2889
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/client/XDocsPlugin.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// 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.googlesource.gerrit.plugins.xdocs.client;
+
+import com.google.gerrit.plugin.client.Plugin;
+import com.google.gerrit.plugin.client.PluginEntryPoint;
+
+public class XDocsPlugin extends PluginEntryPoint {
+
+  @Override
+  public void onPluginLoad() {
+    Plugin.get().screenRegex("project/(.*)/rev/(.*)/(.*)",
+        new XDocScreen.Factory());
+    Plugin.get().screenRegex("project/(.*)/(.*)",
+        new XDocScreen.HeadFactory());
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/xdocs/public/xdocs.css b/src/main/java/com/googlesource/gerrit/plugins/xdocs/public/xdocs.css
new file mode 100644
index 0000000..e2623eb
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/xdocs/public/xdocs.css
@@ -0,0 +1,9 @@
+.xdocs-panel {
+  border-spacing: 5px 5px;
+  width: 100%;
+}
+
+.xdocs-panel iframe {
+  width: 100%;
+}
+