Initial import of Doclava project
Change-Id: Ia5ae56f1700fce98e0ae6954fa2df617ec0537bb
diff --git a/src/com/google/doclava/DoclavaDiff.java b/src/com/google/doclava/DoclavaDiff.java
new file mode 100644
index 0000000..e043246
--- /dev/null
+++ b/src/com/google/doclava/DoclavaDiff.java
@@ -0,0 +1,349 @@
+/*
+ * 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<FederatedSite> sites = new ArrayList<FederatedSite>();
+
+ 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<ResourceLoader> resourceLoaders = new ArrayList<ResourceLoader>();
+ 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:
+ * <pre>
+ * sites.0.name = Android
+ * sites.0.url = http://developer.android.com/reference
+ * sites.1.name = GWT
+ * sites.1.url = http://gwt.googlecode.com
+ * packages.0.name = java.lang
+ * packages.0.sites.0.hasPackage = 1
+ * packages.0.sites.0.link = http://developer.android.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://developer.android.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://developer.android.com/reference/java/lang/Object#wait
+ * packages.0.classes.0.methods.0.sites.1.hasMethod = 0
+ * </pre>
+ */
+ 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<String> 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<String> 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<MethodInfo> 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<String,MethodInfo> 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<String> knownPackages(List<FederatedSite> sites) {
+ Set<String> allPackages = new LinkedHashSet<String>();
+ for (FederatedSite site : sites) {
+ Map<String, PackageInfo> packages = site.apiInfo().getPackages();
+ for (String pkg : packages.keySet()) {
+ allPackages.add(pkg);
+ }
+ }
+
+ List<String> packages = new ArrayList<String>(allPackages);
+ Collections.sort(packages);
+ return packages;
+ }
+
+ /**
+ * Returns all known classes from all sites for a given package.
+ */
+ private List<String> knownClassesForPackage(String pkg, List<FederatedSite> sites) {
+ Set<String> allClasses = new LinkedHashSet<String>();
+ for (FederatedSite site : sites) {
+ PackageInfo packageInfo = site.apiInfo().getPackages().get(pkg);
+ if (packageInfo == null) {
+ continue;
+ }
+ HashMap<String, ClassInfo> classes = packageInfo.allClasses();
+ for (Map.Entry<String, ClassInfo> entry : classes.entrySet()) {
+ allClasses.add(entry.getValue().qualifiedName());
+ }
+ }
+
+ List<String> classes = new ArrayList<String>(allClasses);
+ Collections.sort(classes);
+ return classes;
+ }
+
+ /**
+ * Returns all known methods from all sites for a given class.
+ */
+ private List<MethodInfo> knownMethodsForClass(String qualifiedClassName,
+ List<FederatedSite> sites) {
+
+ Map<String, MethodInfo> allMethods = new HashMap<String, MethodInfo>();
+ for (FederatedSite site : sites) {
+ ClassInfo classInfo = site.apiInfo().findClass(qualifiedClassName);
+ if (classInfo == null) {
+ continue;
+ }
+
+ for (Map.Entry<String, MethodInfo> entry: classInfo.allMethods().entrySet()) {
+ allMethods.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ List<MethodInfo> methods = new ArrayList<MethodInfo>();
+ 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<FederatedSite> sites) {
+ for (FederatedSite site : sites) {
+ if (site.apiInfo().getPackages().get(pkg) == null) {
+ return false;
+ }
+ }
+
+ List<String> 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<FederatedSite> sites) {
+ List<MethodInfo> 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<FederatedSite> 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<FederatedSite> 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<FederatedSite> 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) {}
+ }
+ }
+ }
+}
\ No newline at end of file