blob: 284f9a316d4f5d56f87f8d021b9db3578230b806 [file] [log] [blame]
Ben Dodson920dbbb2010-08-04 15:21:06 -07001/*
2 * Copyright (C) 2010 Google Inc.
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
17package com.google.doclava;
18
19import com.google.clearsilver.jsilver.JSilver;
20import com.google.clearsilver.jsilver.data.Data;
21import com.google.clearsilver.jsilver.resourceloader.CompositeResourceLoader;
22import com.google.clearsilver.jsilver.resourceloader.FileSystemResourceLoader;
23import com.google.clearsilver.jsilver.resourceloader.ResourceLoader;
24import java.io.File;
25import java.io.FileOutputStream;
26import java.io.IOException;
27import java.io.OutputStreamWriter;
28import java.net.URL;
29import java.util.ArrayList;
30import java.util.Collections;
31import java.util.HashMap;
32import java.util.LinkedHashSet;
33import java.util.List;
34import java.util.Map;
35import java.util.Set;
36
37/**
38 * This class is used to generate a web page highlighting the differences and
39 * similarities among various Java libraries.
40 *
41 */
42public final class DoclavaDiff {
43 private final String outputDir;
44 private final JSilver jSilver;
45 private final List<FederatedSite> sites = new ArrayList<FederatedSite>();
46
47 public static void main(String[] args) {
48 new DoclavaDiff(args).generateSite();
49 }
50
51 public DoclavaDiff(String[] args) {
52 // TODO: options parsing
53 try {
54 sites.add(new FederatedSite("Android", new URL("http://manatee/doclava/android")));
55 sites.add(new FederatedSite("GWT", new URL("http://manatee/doclava/gwt")));
56 //sites.add(new FederatedSite("Crore", new URL("http://manatee/doclava/crore")));
57 outputDir = "build";
58 } catch (Exception e) {
59 throw new AssertionError(e);
60 }
61
62 // TODO: accept external templates
63 List<ResourceLoader> resourceLoaders = new ArrayList<ResourceLoader>();
64 resourceLoaders.add(new FileSystemResourceLoader("assets/templates"));
65
66 ResourceLoader compositeResourceLoader = new CompositeResourceLoader(resourceLoaders);
67 jSilver = new JSilver(compositeResourceLoader);
68 }
69
70 public void generateSite() {
71 Data data = generateHdf();
72 generateHtml("diff.cs", data, new File(outputDir + "/diff.html"));
73 }
74
75 /**
76 * Creates an HDF with this structure:
77 * <pre>
Dirk Dougherty41d86562010-08-20 15:21:11 -070078 * sites.0.name = projectA
79 * sites.0.url = http://proja.domain.com/reference
80 * sites.1.name = projectB
81 * sites.1.url = http://projb.domain.com
Ben Dodson920dbbb2010-08-04 15:21:06 -070082 * packages.0.name = java.lang
83 * packages.0.sites.0.hasPackage = 1
Dirk Dougherty41d86562010-08-20 15:21:11 -070084 * packages.0.sites.0.link = http://proja.domain.com/reference/java/lang
Ben Dodson920dbbb2010-08-04 15:21:06 -070085 * packages.0.sites.1.hasPackage = 0
86 * packages.0.classes.0.qualifiedName = java.lang.Object
87 * packages.0.classes.0.sites.0.hasClass = 1
Dirk Dougherty41d86562010-08-20 15:21:11 -070088 * packages.0.classes.0.sites.0.link = http://proja.domain.com/reference/java/lang/Object
Ben Dodson920dbbb2010-08-04 15:21:06 -070089 * packages.0.classes.0.sites.1.hasClass = 0
90 * packages.0.classes.0.methods.0.signature = wait()
91 * packages.0.classes.0.methods.0.sites.0.hasMethod = 1
Dirk Dougherty41d86562010-08-20 15:21:11 -070092 * packages.0.classes.0.methods.0.sites.0.link = http://proja.domain.com/reference/java/lang/Object#wait
Ben Dodson920dbbb2010-08-04 15:21:06 -070093 * packages.0.classes.0.methods.0.sites.1.hasMethod = 0
94 * </pre>
95 */
96 private Data generateHdf() {
97 Data data = jSilver.createData();
98
99 data.setValue("triangle.opened", "../assets/templates/assets/images/triangle-opened.png");
100 data.setValue("triangle.closed", "../assets/templates/assets/images/triangle-closed.png");
101
102 int i = 0;
103 for (FederatedSite site : sites) {
104 String base = "sites." + (i++);
105 data.setValue(base + ".name", site.name());
106 data.setValue(base + ".url", site.baseUrl().toString());
107 }
108
109 List<String> allPackages = knownPackages(sites);
110
111 int p = 0;
112 for (String pkg : allPackages) {
113 PackageInfo packageInfo = new PackageInfo(pkg);
114 String packageBase = "packages." + (p++);
115 data.setValue(packageBase + ".name", pkg);
116
117 int s = 0;
118 for (FederatedSite site : sites) {
119 String siteBase = packageBase + ".sites." + (s++);
120 if (site.apiInfo().getPackages().containsKey(pkg)) {
121 data.setValue(siteBase + ".hasPackage", "1");
122 data.setValue(siteBase + ".link", site.linkFor(packageInfo.htmlPage()));
123 } else {
124 data.setValue(siteBase + ".hasPackage", "0");
125 }
126 }
127
128 if (packageUniqueToSite(pkg, sites)) {
129 continue;
130 }
131
132 List<String> packageClasses = knownClassesForPackage(pkg, sites);
133 int c = 0;
134 for (String qualifiedClassName : packageClasses) {
135 String classBase = packageBase + ".classes." + (c++);
136 data.setValue(classBase + ".qualifiedName", qualifiedClassName);
137
138 s = 0;
139 for (FederatedSite site : sites) {
140 String siteBase = classBase + ".sites." + (s++);
141 ClassInfo classInfo = site.apiInfo().findClass(qualifiedClassName);
142 if (classInfo != null) {
143 data.setValue(siteBase + ".hasClass", "1");
144 data.setValue(siteBase + ".link", site.linkFor(classInfo.htmlPage()));
145 } else {
146 data.setValue(siteBase + ".hasClass", "0");
147 }
148 }
149
150 if (agreeOnClass(qualifiedClassName, sites)) {
151 continue;
152 }
153
154 if (classUniqueToSite(qualifiedClassName, sites)) {
155 continue;
156 }
157
158 int m = 0;
159 List<MethodInfo> methods = knownMethodsForClass(qualifiedClassName, sites);
160 for (MethodInfo method : methods) {
161 if (agreeOnMethod(qualifiedClassName, method, sites)) {
162 continue;
163 }
164
165 String methodBase = classBase + ".methods." + (m++);
166 data.setValue(methodBase + ".signature", method.prettySignature());
167 int k = 0;
168 for (FederatedSite site : sites) {
169 String siteBase = methodBase + ".sites." + (k++);
170 if (site.apiInfo().findClass(qualifiedClassName) == null) {
171 data.setValue(siteBase + ".hasMethod", "0");
172 continue;
173 }
174 Map<String,MethodInfo> siteMethods
175 = site.apiInfo().findClass(qualifiedClassName).allMethods();
176 if (siteMethods.containsKey(method.getHashableName())) {
177 data.setValue(siteBase + ".hasMethod", "1");
178 data.setValue(siteBase + ".link", site.linkFor(method.htmlPage()));
179 } else {
180 data.setValue(siteBase + ".hasMethod", "0");
181 }
182 }
183 }
184 }
185 }
186
187 return data;
188 }
189
190 /**
191 * Returns a list of all known packages from all sites.
192 */
193 private List<String> knownPackages(List<FederatedSite> sites) {
194 Set<String> allPackages = new LinkedHashSet<String>();
195 for (FederatedSite site : sites) {
196 Map<String, PackageInfo> packages = site.apiInfo().getPackages();
197 for (String pkg : packages.keySet()) {
198 allPackages.add(pkg);
199 }
200 }
201
202 List<String> packages = new ArrayList<String>(allPackages);
203 Collections.sort(packages);
204 return packages;
205 }
206
207 /**
208 * Returns all known classes from all sites for a given package.
209 */
210 private List<String> knownClassesForPackage(String pkg, List<FederatedSite> sites) {
211 Set<String> allClasses = new LinkedHashSet<String>();
212 for (FederatedSite site : sites) {
213 PackageInfo packageInfo = site.apiInfo().getPackages().get(pkg);
214 if (packageInfo == null) {
215 continue;
216 }
217 HashMap<String, ClassInfo> classes = packageInfo.allClasses();
218 for (Map.Entry<String, ClassInfo> entry : classes.entrySet()) {
219 allClasses.add(entry.getValue().qualifiedName());
220 }
221 }
222
223 List<String> classes = new ArrayList<String>(allClasses);
224 Collections.sort(classes);
225 return classes;
226 }
227
228 /**
229 * Returns all known methods from all sites for a given class.
230 */
231 private List<MethodInfo> knownMethodsForClass(String qualifiedClassName,
232 List<FederatedSite> sites) {
233
234 Map<String, MethodInfo> allMethods = new HashMap<String, MethodInfo>();
235 for (FederatedSite site : sites) {
236 ClassInfo classInfo = site.apiInfo().findClass(qualifiedClassName);
237 if (classInfo == null) {
238 continue;
239 }
240
241 for (Map.Entry<String, MethodInfo> entry: classInfo.allMethods().entrySet()) {
242 allMethods.put(entry.getKey(), entry.getValue());
243 }
244 }
245
246 List<MethodInfo> methods = new ArrayList<MethodInfo>();
247 methods.addAll(allMethods.values());
248 return methods;
249 }
250
251 /**
252 * Returns true if the list of sites all completely agree on the given
253 * package. All sites must possess the package, all classes it contains, and
254 * all methods of each class.
255 */
256 private boolean agreeOnPackage(String pkg, List<FederatedSite> sites) {
257 for (FederatedSite site : sites) {
258 if (site.apiInfo().getPackages().get(pkg) == null) {
259 return false;
260 }
261 }
262
263 List<String> classes = knownClassesForPackage(pkg, sites);
264 for (String clazz : classes) {
265 if (!agreeOnClass(clazz, sites)) {
266 return false;
267 }
268 }
269 return true;
270 }
271
272 /**
273 * Returns true if the list of sites all agree on the given class. Each site
274 * must have the class and agree on its methods.
275 */
276 private boolean agreeOnClass(String qualifiedClassName, List<FederatedSite> sites) {
277 List<MethodInfo> methods = knownMethodsForClass(qualifiedClassName, sites);
278 for (MethodInfo method : methods) {
279 if (!agreeOnMethod(qualifiedClassName, method, sites)) {
280 return false;
281 }
282 }
283 return true;
284 }
285
286 /**
287 * Returns true if the list of sites all contain the given method.
288 */
289 private boolean agreeOnMethod(String qualifiedClassName, MethodInfo method,
290 List<FederatedSite> sites) {
291
292 for (FederatedSite site : sites) {
293 ClassInfo siteClass = site.apiInfo().findClass(qualifiedClassName);
294 if (siteClass == null) {
295 return false;
296 }
297
298 if (!siteClass.supportsMethod(method)) {
299 return false;
300 }
301 }
302 return true;
303 }
304
305 /**
306 * Returns true if the given package is known to exactly one of the given sites.
307 */
308 private boolean packageUniqueToSite(String pkg, List<FederatedSite> sites) {
309 int numSites = 0;
310 for (FederatedSite site : sites) {
311 if (site.apiInfo().getPackages().containsKey(pkg)) {
312 numSites++;
313 }
314 }
315 return numSites == 1;
316 }
317
318 /**
319 * Returns true if the given class is known to exactly one of the given sites.
320 */
321 private boolean classUniqueToSite(String qualifiedClassName, List<FederatedSite> sites) {
322 int numSites = 0;
323 for (FederatedSite site : sites) {
324 if (site.apiInfo().findClass(qualifiedClassName) != null) {
325 numSites++;
326 }
327 }
328 return numSites == 1;
329 }
330
331 private void generateHtml(String template, Data data, File file) {
332 ClearPage.ensureDirectory(file);
333
334 OutputStreamWriter stream = null;
335 try {
336 stream = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
337 String rendered = jSilver.render(template, data);
338 stream.write(rendered, 0, rendered.length());
339 } catch (IOException e) {
340 System.out.println("error: " + e.getMessage() + "; when writing file: " + file.getAbsolutePath());
341 } finally {
342 if (stream != null) {
343 try {
344 stream.close();
345 } catch (IOException ignored) {}
346 }
347 }
348 }
Brian Carlstromed8f7972011-06-10 11:10:48 -0700349}