/* * Copyright (C) 2010 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.doclava; import com.google.clearsilver.jsilver.JSilver; import com.google.clearsilver.jsilver.data.Data; import com.google.clearsilver.jsilver.resourceloader.CompositeResourceLoader; import com.google.clearsilver.jsilver.resourceloader.FileSystemResourceLoader; import com.google.clearsilver.jsilver.resourceloader.ResourceLoader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * This class is used to generate a web page highlighting the differences and * similarities among various Java libraries. * */ public final class DoclavaDiff { private final String outputDir; private final JSilver jSilver; private final List sites = new ArrayList(); public static void main(String[] args) { new DoclavaDiff(args).generateSite(); } public DoclavaDiff(String[] args) { // TODO: options parsing try { sites.add(new FederatedSite("Android", new URL("http://manatee/doclava/android"))); sites.add(new FederatedSite("GWT", new URL("http://manatee/doclava/gwt"))); //sites.add(new FederatedSite("Crore", new URL("http://manatee/doclava/crore"))); outputDir = "build"; } catch (Exception e) { throw new AssertionError(e); } // TODO: accept external templates List resourceLoaders = new ArrayList(); resourceLoaders.add(new FileSystemResourceLoader("assets/templates")); ResourceLoader compositeResourceLoader = new CompositeResourceLoader(resourceLoaders); jSilver = new JSilver(compositeResourceLoader); } public void generateSite() { Data data = generateHdf(); generateHtml("diff.cs", data, new File(outputDir + "/diff.html")); } /** * Creates an HDF with this structure: *
   * sites.0.name = projectA
   * sites.0.url = http://proja.domain.com/reference
   * sites.1.name = projectB
   * sites.1.url = http://projb.domain.com
   * packages.0.name = java.lang
   * packages.0.sites.0.hasPackage = 1
   * packages.0.sites.0.link = http://proja.domain.com/reference/java/lang
   * packages.0.sites.1.hasPackage = 0
   * packages.0.classes.0.qualifiedName = java.lang.Object
   * packages.0.classes.0.sites.0.hasClass = 1
   * packages.0.classes.0.sites.0.link = http://proja.domain.com/reference/java/lang/Object
   * packages.0.classes.0.sites.1.hasClass = 0 
   * packages.0.classes.0.methods.0.signature = wait()
   * packages.0.classes.0.methods.0.sites.0.hasMethod = 1
   * packages.0.classes.0.methods.0.sites.0.link = http://proja.domain.com/reference/java/lang/Object#wait
   * packages.0.classes.0.methods.0.sites.1.hasMethod = 0
   * 
*/ private Data generateHdf() { Data data = jSilver.createData(); data.setValue("triangle.opened", "../assets/templates/assets/images/triangle-opened.png"); data.setValue("triangle.closed", "../assets/templates/assets/images/triangle-closed.png"); int i = 0; for (FederatedSite site : sites) { String base = "sites." + (i++); data.setValue(base + ".name", site.name()); data.setValue(base + ".url", site.baseUrl().toString()); } List allPackages = knownPackages(sites); int p = 0; for (String pkg : allPackages) { PackageInfo packageInfo = new PackageInfo(pkg); String packageBase = "packages." + (p++); data.setValue(packageBase + ".name", pkg); int s = 0; for (FederatedSite site : sites) { String siteBase = packageBase + ".sites." + (s++); if (site.apiInfo().getPackages().containsKey(pkg)) { data.setValue(siteBase + ".hasPackage", "1"); data.setValue(siteBase + ".link", site.linkFor(packageInfo.htmlPage())); } else { data.setValue(siteBase + ".hasPackage", "0"); } } if (packageUniqueToSite(pkg, sites)) { continue; } List packageClasses = knownClassesForPackage(pkg, sites); int c = 0; for (String qualifiedClassName : packageClasses) { String classBase = packageBase + ".classes." + (c++); data.setValue(classBase + ".qualifiedName", qualifiedClassName); s = 0; for (FederatedSite site : sites) { String siteBase = classBase + ".sites." + (s++); ClassInfo classInfo = site.apiInfo().findClass(qualifiedClassName); if (classInfo != null) { data.setValue(siteBase + ".hasClass", "1"); data.setValue(siteBase + ".link", site.linkFor(classInfo.htmlPage())); } else { data.setValue(siteBase + ".hasClass", "0"); } } if (agreeOnClass(qualifiedClassName, sites)) { continue; } if (classUniqueToSite(qualifiedClassName, sites)) { continue; } int m = 0; List methods = knownMethodsForClass(qualifiedClassName, sites); for (MethodInfo method : methods) { if (agreeOnMethod(qualifiedClassName, method, sites)) { continue; } String methodBase = classBase + ".methods." + (m++); data.setValue(methodBase + ".signature", method.prettySignature()); int k = 0; for (FederatedSite site : sites) { String siteBase = methodBase + ".sites." + (k++); if (site.apiInfo().findClass(qualifiedClassName) == null) { data.setValue(siteBase + ".hasMethod", "0"); continue; } Map siteMethods = site.apiInfo().findClass(qualifiedClassName).allMethods(); if (siteMethods.containsKey(method.getHashableName())) { data.setValue(siteBase + ".hasMethod", "1"); data.setValue(siteBase + ".link", site.linkFor(method.htmlPage())); } else { data.setValue(siteBase + ".hasMethod", "0"); } } } } } return data; } /** * Returns a list of all known packages from all sites. */ private List knownPackages(List sites) { Set allPackages = new LinkedHashSet(); for (FederatedSite site : sites) { Map packages = site.apiInfo().getPackages(); for (String pkg : packages.keySet()) { allPackages.add(pkg); } } List packages = new ArrayList(allPackages); Collections.sort(packages); return packages; } /** * Returns all known classes from all sites for a given package. */ private List knownClassesForPackage(String pkg, List sites) { Set allClasses = new LinkedHashSet(); for (FederatedSite site : sites) { PackageInfo packageInfo = site.apiInfo().getPackages().get(pkg); if (packageInfo == null) { continue; } HashMap classes = packageInfo.allClasses(); for (Map.Entry entry : classes.entrySet()) { allClasses.add(entry.getValue().qualifiedName()); } } List classes = new ArrayList(allClasses); Collections.sort(classes); return classes; } /** * Returns all known methods from all sites for a given class. */ private List knownMethodsForClass(String qualifiedClassName, List sites) { Map allMethods = new HashMap(); for (FederatedSite site : sites) { ClassInfo classInfo = site.apiInfo().findClass(qualifiedClassName); if (classInfo == null) { continue; } for (Map.Entry entry: classInfo.allMethods().entrySet()) { allMethods.put(entry.getKey(), entry.getValue()); } } List methods = new ArrayList(); methods.addAll(allMethods.values()); return methods; } /** * Returns true if the list of sites all completely agree on the given * package. All sites must possess the package, all classes it contains, and * all methods of each class. */ private boolean agreeOnPackage(String pkg, List sites) { for (FederatedSite site : sites) { if (site.apiInfo().getPackages().get(pkg) == null) { return false; } } List classes = knownClassesForPackage(pkg, sites); for (String clazz : classes) { if (!agreeOnClass(clazz, sites)) { return false; } } return true; } /** * Returns true if the list of sites all agree on the given class. Each site * must have the class and agree on its methods. */ private boolean agreeOnClass(String qualifiedClassName, List sites) { List methods = knownMethodsForClass(qualifiedClassName, sites); for (MethodInfo method : methods) { if (!agreeOnMethod(qualifiedClassName, method, sites)) { return false; } } return true; } /** * Returns true if the list of sites all contain the given method. */ private boolean agreeOnMethod(String qualifiedClassName, MethodInfo method, List sites) { for (FederatedSite site : sites) { ClassInfo siteClass = site.apiInfo().findClass(qualifiedClassName); if (siteClass == null) { return false; } if (!siteClass.supportsMethod(method)) { return false; } } return true; } /** * Returns true if the given package is known to exactly one of the given sites. */ private boolean packageUniqueToSite(String pkg, List sites) { int numSites = 0; for (FederatedSite site : sites) { if (site.apiInfo().getPackages().containsKey(pkg)) { numSites++; } } return numSites == 1; } /** * Returns true if the given class is known to exactly one of the given sites. */ private boolean classUniqueToSite(String qualifiedClassName, List sites) { int numSites = 0; for (FederatedSite site : sites) { if (site.apiInfo().findClass(qualifiedClassName) != null) { numSites++; } } return numSites == 1; } private void generateHtml(String template, Data data, File file) { ClearPage.ensureDirectory(file); OutputStreamWriter stream = null; try { stream = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); String rendered = jSilver.render(template, data); stream.write(rendered, 0, rendered.length()); } catch (IOException e) { System.out.println("error: " + e.getMessage() + "; when writing file: " + file.getAbsolutePath()); } finally { if (stream != null) { try { stream.close(); } catch (IOException ignored) {} } } } }