1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ahat;
18 
19 import java.net.URI;
20 import java.net.URISyntaxException;
21 
22 /**
23  * A class representing a small string of document content consisting of text,
24  * links, images, etc.
25  */
26 class DocString {
27   private StringBuilder mStringBuilder;
28 
DocString()29   public DocString() {
30     mStringBuilder = new StringBuilder();
31   }
32 
33   /**
34    * Construct a new DocString, initialized with the given text.
35    */
text(String str)36   public static DocString text(String str) {
37     DocString doc = new DocString();
38     return doc.append(str);
39   }
40 
41   /**
42    * Construct a new DocString, initialized with the given formatted text.
43    */
format(String format, Object... args)44   public static DocString format(String format, Object... args) {
45     DocString doc = new DocString();
46     return doc.appendFormat(format, args);
47   }
48 
49   /**
50    * Construct a new DocString, initialized with the given link.
51    */
link(URI uri, DocString content)52   public static DocString link(URI uri, DocString content) {
53     DocString doc = new DocString();
54     return doc.appendLink(uri, content);
55   }
56 
57   /**
58    * Construct a new DocString initialized with the given image.
59    */
image(URI uri, String alt)60   public static DocString image(URI uri, String alt) {
61     return (new DocString()).appendImage(uri, alt);
62   }
63 
64   /**
65    * Append literal text to the given doc string.
66    * Returns this object.
67    */
append(String text)68   public DocString append(String text) {
69     mStringBuilder.append(HtmlEscaper.escape(text));
70     return this;
71   }
72 
73   /**
74    * Append formatted text to the given doc string.
75    * Returns this object.
76    */
appendFormat(String format, Object... args)77   public DocString appendFormat(String format, Object... args) {
78     append(String.format(format, args));
79     return this;
80   }
81 
append(DocString str)82   public DocString append(DocString str) {
83     mStringBuilder.append(str.html());
84     return this;
85   }
86 
87   /**
88    * Adorn the given string to indicate it represents something added relative
89    * to a baseline.
90    */
added(DocString str)91   public static DocString added(DocString str) {
92     DocString string = new DocString();
93     string.mStringBuilder.append("<span class=\"added\">");
94     string.mStringBuilder.append(str.html());
95     string.mStringBuilder.append("</span>");
96     return string;
97   }
98 
99   /**
100    * Adorn the given string to indicate it represents something added relative
101    * to a baseline.
102    */
added(String str)103   public static DocString added(String str) {
104     return added(text(str));
105   }
106 
107   /**
108    * Adorn the given string to indicate it represents something removed relative
109    * to a baseline.
110    */
removed(DocString str)111   public static DocString removed(DocString str) {
112     DocString string = new DocString();
113     string.mStringBuilder.append("<span class=\"removed\">");
114     string.mStringBuilder.append(str.html());
115     string.mStringBuilder.append("</span>");
116     return string;
117   }
118 
119   /**
120    * Adorn the given string to indicate it represents something removed relative
121    * to a baseline.
122    */
removed(String str)123   public static DocString removed(String str) {
124     return removed(text(str));
125   }
126 
127   /**
128    * Standard formatted DocString for describing a size.
129    *
130    * Nothing is printed for a size of zero.
131    * Set isPlaceHolder to true to indicate that the size field corresponds to
132    * for a placeholder object that should be annotated specially.
133    */
size(long size, boolean isPlaceHolder)134   public static DocString size(long size, boolean isPlaceHolder) {
135     DocString string = new DocString();
136     if (isPlaceHolder) {
137       string.append(DocString.removed("del"));
138     } else if (size != 0) {
139       string.appendFormat("%,14d", size);
140     }
141     return string;
142   }
143 
144   /**
145    * Standard formatted DocString for describing a change in size relative to
146    * a baseline.
147    * @param noCurrent - whether no current object exists.
148    * @param noBaseline - whether no basline object exists.
149    * @param current - the size of the current object.
150    * @param baseline - the size of the baseline object.
151    */
delta(boolean noCurrent, boolean noBaseline, long current, long baseline)152   public static DocString delta(boolean noCurrent, boolean noBaseline,
153       long current, long baseline) {
154     DocString doc = new DocString();
155     return doc.appendDelta(noCurrent, noBaseline, current, baseline);
156   }
157 
158   /**
159    * Standard formatted DocString for describing a change in size relative to
160    * a baseline.
161    */
appendDelta(boolean noCurrent, boolean noBaseline, long current, long baseline)162   public DocString appendDelta(boolean noCurrent, boolean noBaseline,
163       long current, long baseline) {
164     if (noCurrent) {
165       append(removed(format("%+,14d", 0 - baseline)));
166     } else if (noBaseline) {
167       append(added("new"));
168     } else if (current > baseline) {
169       append(added(format("%+,14d", current - baseline)));
170     } else if (current < baseline) {
171       append(removed(format("%+,14d", current - baseline)));
172     }
173     return this;
174   }
175 
appendLink(URI uri, DocString content)176   public DocString appendLink(URI uri, DocString content) {
177     mStringBuilder.append("<a href=\"");
178     mStringBuilder.append(uri.toASCIIString());
179     mStringBuilder.append("\">");
180     mStringBuilder.append(content.html());
181     mStringBuilder.append("</a>");
182     return this;
183   }
184 
appendImage(URI uri, String alt)185   public DocString appendImage(URI uri, String alt) {
186     mStringBuilder.append("<img alt=\"");
187     mStringBuilder.append(HtmlEscaper.escape(alt));
188     mStringBuilder.append("\" src=\"");
189     mStringBuilder.append(uri.toASCIIString());
190     mStringBuilder.append("\" />");
191     return this;
192   }
193 
appendThumbnail(URI uri, String alt)194   public DocString appendThumbnail(URI uri, String alt) {
195     mStringBuilder.append("<img height=\"16\" alt=\"");
196     mStringBuilder.append(HtmlEscaper.escape(alt));
197     mStringBuilder.append("\" src=\"");
198     mStringBuilder.append(uri.toASCIIString());
199     mStringBuilder.append("\" />");
200     return this;
201   }
202 
203   /**
204    * Convenience function for constructing a URI from a string with a uri
205    * known to be valid.
206    */
uri(String uriString)207   public static URI uri(String uriString) {
208     try {
209       return new URI(uriString);
210     } catch (URISyntaxException e) {
211       throw new IllegalStateException("Known good uri has syntax error: " + uriString, e);
212     }
213   }
214 
215   /**
216    * Convenience function for constructing a URI from a formatted string with
217    * a uri known to be valid.
218    */
formattedUri(String format, Object... args)219   public static URI formattedUri(String format, Object... args) {
220     return uri(String.format(format, args));
221   }
222 
223   /**
224    * Render the DocString as html.
225    */
html()226   public String html() {
227     return mStringBuilder.toString();
228   }
229 }
230