1b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler/*
2b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler * Copyright (C) 2015 The Android Open Source Project
3b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler *
4b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler * Licensed under the Apache License, Version 2.0 (the "License");
5b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler * you may not use this file except in compliance with the License.
6b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler * You may obtain a copy of the License at
7b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler *
8b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler *      http://www.apache.org/licenses/LICENSE-2.0
9b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler *
10b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler * Unless required by applicable law or agreed to in writing, software
11b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler * distributed under the License is distributed on an "AS IS" BASIS,
12b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler * See the License for the specific language governing permissions and
14b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler * limitations under the License.
15b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler */
16b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
17b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhlerpackage com.android.ahat;
18b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
19b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhlerimport java.io.PrintStream;
20b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhlerimport java.net.URI;
21b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhlerimport java.util.List;
22b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
23b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler/**
24b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler * An Html implementation of Doc.
25b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler */
26b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhlerpublic class HtmlDoc implements Doc {
27b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  private PrintStream ps;
28b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  private Column[] mCurrentTableColumns;
29b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
30b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  /**
31b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler   * Create an HtmlDoc that writes to the given print stream.
32b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler   * @param title - The main page title.
33b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler   * @param style - A URI link to a stylesheet to link to.
34b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler   */
35b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public HtmlDoc(PrintStream ps, DocString title, URI style) {
36b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    this.ps = ps;
37b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
38b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("<!DOCTYPE html>");
39b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("<html>");
40b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("<head>");
41b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.format("<title>%s</title>\n", title.html());
42b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.format("<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\">\n",
43b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler        style.toASCIIString());
44b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("</head>");
45b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("<body>");
46b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
47b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
48b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
49b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void title(String format, Object... args) {
50b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.print("<h1>");
51b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.print(DocString.text(String.format(format, args)).html());
52b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("</h1>");
53b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
54b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
55b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
56b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void menu(DocString string) {
57b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.format("<div class=\"menu\">%s</div>", string.html());
58b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
59b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
60b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
61b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void section(String title) {
62b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.print("<h2>");
63b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.print(DocString.text(title).html());
64b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println(":</h2>");
65b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
66b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
67b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
68b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void println(DocString string) {
69b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.print(string.html());
70b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("<br />");
71b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
72b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
73b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
74b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void big(DocString str) {
75b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.print("<h2>");
76b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.print(str.html());
77b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("</h2>");
78b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
79b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
80b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
81b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void table(Column... columns) {
82b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    if (columns.length == 0) {
83b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      throw new IllegalArgumentException("No columns specified");
84b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    }
85b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
86b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    mCurrentTableColumns = columns;
87b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("<table>");
88b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    for (int i = 0; i < columns.length - 1; i++) {
89b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      ps.format("<th>%s</th>", columns[i].heading.html());
90b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    }
91b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
92b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    // Align the last header to the left so it's easier to see if the last
93b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    // column is very wide.
94b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.format("<th align=\"left\">%s</th>", columns[columns.length - 1].heading.html());
95b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
96b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
97b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
98b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void table(DocString description, List<Column> subcols, List<Column> cols) {
99b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    mCurrentTableColumns = new Column[subcols.size() + cols.size()];
100b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    int j = 0;
101b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    for (Column col : subcols) {
102b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      mCurrentTableColumns[j] = col;
103b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      j++;
104b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    }
105b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    for (Column col : cols) {
106b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      mCurrentTableColumns[j] = col;
107b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      j++;
108b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    }
109b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
110b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("<table>");
111b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.format("<tr><th colspan=\"%d\">%s</th>", subcols.size(), description.html());
112b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    for (int i = 0; i < cols.size() - 1; i++) {
113b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      ps.format("<th rowspan=\"2\">%s</th>", cols.get(i).heading.html());
114b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    }
115b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    if (!cols.isEmpty()) {
116b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      // Align the last column header to the left so it can still be seen if
117b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      // the last column is very wide.
118b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      ps.format("<th align=\"left\" rowspan=\"2\">%s</th>",
119b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler          cols.get(cols.size() - 1).heading.html());
120b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    }
121b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("</tr>");
122b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
123b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.print("<tr>");
124b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    for (Column subcol : subcols) {
125b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      ps.format("<th>%s</th>", subcol.heading.html());
126b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    }
127b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("</tr>");
128b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
129b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
130b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
131b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void row(DocString... values) {
132b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    if (mCurrentTableColumns == null) {
133b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      throw new IllegalStateException("table method must be called before row");
134b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    }
135b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
136b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    if (mCurrentTableColumns.length != values.length) {
137b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      throw new IllegalArgumentException(String.format(
138b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler          "Wrong number of row values. Expected %d, but got %d",
139b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler          mCurrentTableColumns.length, values.length));
140b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    }
141b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
142b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.print("<tr>");
143b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    for (int i = 0; i < values.length; i++) {
144b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      ps.print("<td");
145b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      if (mCurrentTableColumns[i].align == Column.Align.RIGHT) {
146b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler        ps.print(" align=\"right\"");
147b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      }
148b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler      ps.format(">%s</td>", values[i].html());
149b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    }
150b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("</tr>");
151b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
152b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
153b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
154b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void descriptions() {
155b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("<table>");
156b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
157b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
158b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
159b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void description(DocString key, DocString value) {
160b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.format("<tr><th align=\"left\">%s:</th><td>%s</td></tr>", key.html(), value.html());
161b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
162b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
163b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
164b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void end() {
165b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("</table>");
166b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    mCurrentTableColumns = null;
167b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
168b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler
169b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  @Override
170b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  public void close() {
171b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("</body>");
172b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.println("</html>");
173b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler    ps.close();
174b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler  }
175b730b78dac047c6d8ead93ad77605bcb7414f5ceRichard Uhler}
176