|  | /* | 
|  | * Copyright (C) 2008 The Android Open Source Project | 
|  | * | 
|  | * 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. | 
|  | */ | 
|  |  | 
|  | import com.sun.javadoc.*; | 
|  |  | 
|  | import org.clearsilver.HDF; | 
|  |  | 
|  | import java.util.*; | 
|  | import java.io.*; | 
|  | import java.lang.reflect.Proxy; | 
|  | import java.lang.reflect.Array; | 
|  | import java.lang.reflect.InvocationHandler; | 
|  | import java.lang.reflect.InvocationTargetException; | 
|  | import java.lang.reflect.Method; | 
|  |  | 
|  | public class DroidDoc | 
|  | { | 
|  | private static final String SDK_CONSTANT_ANNOTATION = "android.annotation.SdkConstant"; | 
|  | private static final String SDK_CONSTANT_TYPE_ACTIVITY_ACTION = "android.annotation.SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION"; | 
|  | private static final String SDK_CONSTANT_TYPE_BROADCAST_ACTION = "android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION"; | 
|  | private static final String SDK_CONSTANT_TYPE_SERVICE_ACTION = "android.annotation.SdkConstant.SdkConstantType.SERVICE_INTENT_ACTION"; | 
|  | private static final String SDK_CONSTANT_TYPE_CATEGORY = "android.annotation.SdkConstant.SdkConstantType.INTENT_CATEGORY"; | 
|  | private static final String SDK_WIDGET_ANNOTATION = "android.annotation.Widget"; | 
|  | private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout"; | 
|  |  | 
|  | private static final int TYPE_NONE = 0; | 
|  | private static final int TYPE_WIDGET = 1; | 
|  | private static final int TYPE_LAYOUT = 2; | 
|  | private static final int TYPE_LAYOUT_PARAM = 3; | 
|  |  | 
|  | public static final int SHOW_PUBLIC = 0x00000001; | 
|  | public static final int SHOW_PROTECTED = 0x00000003; | 
|  | public static final int SHOW_PACKAGE = 0x00000007; | 
|  | public static final int SHOW_PRIVATE = 0x0000000f; | 
|  | public static final int SHOW_HIDDEN = 0x0000001f; | 
|  |  | 
|  | public static int showLevel = SHOW_PROTECTED; | 
|  |  | 
|  | public static final String javadocDir = "reference/"; | 
|  | public static String htmlExtension; | 
|  |  | 
|  | public static RootDoc root; | 
|  | public static ArrayList<String[]> mHDFData = new ArrayList<String[]>(); | 
|  | public static Map<Character,String> escapeChars = new HashMap<Character,String>(); | 
|  | public static String title = ""; | 
|  | public static SinceTagger sinceTagger = new SinceTagger(); | 
|  |  | 
|  | public static boolean checkLevel(int level) | 
|  | { | 
|  | return (showLevel & level) == level; | 
|  | } | 
|  |  | 
|  | public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp, | 
|  | boolean priv, boolean hidden) | 
|  | { | 
|  | int level = 0; | 
|  | if (hidden && !checkLevel(SHOW_HIDDEN)) { | 
|  | return false; | 
|  | } | 
|  | if (pub && checkLevel(SHOW_PUBLIC)) { | 
|  | return true; | 
|  | } | 
|  | if (prot && checkLevel(SHOW_PROTECTED)) { | 
|  | return true; | 
|  | } | 
|  | if (pkgp && checkLevel(SHOW_PACKAGE)) { | 
|  | return true; | 
|  | } | 
|  | if (priv && checkLevel(SHOW_PRIVATE)) { | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public static boolean start(RootDoc r) | 
|  | { | 
|  | String keepListFile = null; | 
|  | String proofreadFile = null; | 
|  | String todoFile = null; | 
|  | String sdkValuePath = null; | 
|  | ArrayList<SampleCode> sampleCodes = new ArrayList<SampleCode>(); | 
|  | String stubsDir = null; | 
|  | //Create the dependency graph for the stubs directory | 
|  | boolean apiXML = false; | 
|  | boolean noDocs = false; | 
|  | String apiFile = null; | 
|  | String debugStubsFile = ""; | 
|  | HashSet<String> stubPackages = null; | 
|  |  | 
|  | root = r; | 
|  |  | 
|  | String[][] options = r.options(); | 
|  | for (String[] a: options) { | 
|  | if (a[0].equals("-d")) { | 
|  | ClearPage.outputDir = a[1]; | 
|  | } | 
|  | else if (a[0].equals("-templatedir")) { | 
|  | ClearPage.addTemplateDir(a[1]); | 
|  | } | 
|  | else if (a[0].equals("-hdf")) { | 
|  | mHDFData.add(new String[] {a[1], a[2]}); | 
|  | } | 
|  | else if (a[0].equals("-toroot")) { | 
|  | ClearPage.toroot = a[1]; | 
|  | } | 
|  | else if (a[0].equals("-samplecode")) { | 
|  | sampleCodes.add(new SampleCode(a[1], a[2], a[3])); | 
|  | } | 
|  | else if (a[0].equals("-htmldir")) { | 
|  | ClearPage.htmlDir = a[1]; | 
|  | } | 
|  | else if (a[0].equals("-title")) { | 
|  | DroidDoc.title = a[1]; | 
|  | } | 
|  | else if (a[0].equals("-werror")) { | 
|  | Errors.setWarningsAreErrors(true); | 
|  | } | 
|  | else if (a[0].equals("-error") || a[0].equals("-warning") | 
|  | || a[0].equals("-hide")) { | 
|  | try { | 
|  | int level = -1; | 
|  | if (a[0].equals("-error")) { | 
|  | level = Errors.ERROR; | 
|  | } | 
|  | else if (a[0].equals("-warning")) { | 
|  | level = Errors.WARNING; | 
|  | } | 
|  | else if (a[0].equals("-hide")) { | 
|  | level = Errors.HIDDEN; | 
|  | } | 
|  | Errors.setErrorLevel(Integer.parseInt(a[1]), level); | 
|  | } | 
|  | catch (NumberFormatException e) { | 
|  | // already printed below | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else if (a[0].equals("-keeplist")) { | 
|  | keepListFile = a[1]; | 
|  | } | 
|  | else if (a[0].equals("-proofread")) { | 
|  | proofreadFile = a[1]; | 
|  | } | 
|  | else if (a[0].equals("-todo")) { | 
|  | todoFile = a[1]; | 
|  | } | 
|  | else if (a[0].equals("-public")) { | 
|  | showLevel = SHOW_PUBLIC; | 
|  | } | 
|  | else if (a[0].equals("-protected")) { | 
|  | showLevel = SHOW_PROTECTED; | 
|  | } | 
|  | else if (a[0].equals("-package")) { | 
|  | showLevel = SHOW_PACKAGE; | 
|  | } | 
|  | else if (a[0].equals("-private")) { | 
|  | showLevel = SHOW_PRIVATE; | 
|  | } | 
|  | else if (a[0].equals("-hidden")) { | 
|  | showLevel = SHOW_HIDDEN; | 
|  | } | 
|  | else if (a[0].equals("-stubs")) { | 
|  | stubsDir = a[1]; | 
|  | } | 
|  | else if (a[0].equals("-stubpackages")) { | 
|  | stubPackages = new HashSet(); | 
|  | for (String pkg: a[1].split(":")) { | 
|  | stubPackages.add(pkg); | 
|  | } | 
|  | } | 
|  | else if (a[0].equals("-sdkvalues")) { | 
|  | sdkValuePath = a[1]; | 
|  | } | 
|  | else if (a[0].equals("-apixml")) { | 
|  | apiXML = true; | 
|  | apiFile = a[1]; | 
|  | } | 
|  | else if (a[0].equals("-nodocs")) { | 
|  | noDocs = true; | 
|  | } | 
|  | else if (a[0].equals("-since")) { | 
|  | sinceTagger.addVersion(a[1], a[2]); | 
|  | } | 
|  | } | 
|  |  | 
|  | // read some prefs from the template | 
|  | if (!readTemplateSettings()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Set up the data structures | 
|  | Converter.makeInfo(r); | 
|  |  | 
|  | if (!noDocs) { | 
|  | long startTime = System.nanoTime(); | 
|  |  | 
|  | // Apply @since tags from the XML file | 
|  | sinceTagger.tagAll(Converter.rootClasses()); | 
|  |  | 
|  | // Files for proofreading | 
|  | if (proofreadFile != null) { | 
|  | Proofread.initProofread(proofreadFile); | 
|  | } | 
|  | if (todoFile != null) { | 
|  | TodoFile.writeTodoFile(todoFile); | 
|  | } | 
|  |  | 
|  | // HTML Pages | 
|  | if (ClearPage.htmlDir != null) { | 
|  | writeHTMLPages(); | 
|  | } | 
|  |  | 
|  | // Navigation tree | 
|  | NavTree.writeNavTree(javadocDir); | 
|  |  | 
|  | // Packages Pages | 
|  | writePackages(javadocDir | 
|  | + (ClearPage.htmlDir!=null | 
|  | ? "packages" + htmlExtension | 
|  | : "index" + htmlExtension)); | 
|  |  | 
|  | // Classes | 
|  | writeClassLists(); | 
|  | writeClasses(); | 
|  | writeHierarchy(); | 
|  | //      writeKeywords(); | 
|  |  | 
|  | // Lists for JavaScript | 
|  | writeLists(); | 
|  | if (keepListFile != null) { | 
|  | writeKeepList(keepListFile); | 
|  | } | 
|  |  | 
|  | // Sample Code | 
|  | for (SampleCode sc: sampleCodes) { | 
|  | sc.write(); | 
|  | } | 
|  |  | 
|  | // Index page | 
|  | writeIndex(); | 
|  |  | 
|  | Proofread.finishProofread(proofreadFile); | 
|  |  | 
|  | if (sdkValuePath != null) { | 
|  | writeSdkValues(sdkValuePath); | 
|  | } | 
|  |  | 
|  | long time = System.nanoTime() - startTime; | 
|  | System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to " | 
|  | + ClearPage.outputDir); | 
|  | } | 
|  |  | 
|  | // Stubs | 
|  | if (stubsDir != null) { | 
|  | Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages); | 
|  | } | 
|  |  | 
|  | Errors.printErrors(); | 
|  | return !Errors.hadError; | 
|  | } | 
|  |  | 
|  | private static void writeIndex() { | 
|  | HDF data = makeHDF(); | 
|  | ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension); | 
|  | } | 
|  |  | 
|  | private static boolean readTemplateSettings() | 
|  | { | 
|  | HDF data = makeHDF(); | 
|  | htmlExtension = data.getValue("template.extension", ".html"); | 
|  | int i=0; | 
|  | while (true) { | 
|  | String k = data.getValue("template.escape." + i + ".key", ""); | 
|  | String v = data.getValue("template.escape." + i + ".value", ""); | 
|  | if ("".equals(k)) { | 
|  | break; | 
|  | } | 
|  | if (k.length() != 1) { | 
|  | System.err.println("template.escape." + i + ".key must have a length of 1: " + k); | 
|  | return false; | 
|  | } | 
|  | escapeChars.put(k.charAt(0), v); | 
|  | i++; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public static String escape(String s) { | 
|  | if (escapeChars.size() == 0) { | 
|  | return s; | 
|  | } | 
|  | StringBuffer b = null; | 
|  | int begin = 0; | 
|  | final int N = s.length(); | 
|  | for (int i=0; i<N; i++) { | 
|  | char c = s.charAt(i); | 
|  | String mapped = escapeChars.get(c); | 
|  | if (mapped != null) { | 
|  | if (b == null) { | 
|  | b = new StringBuffer(s.length() + mapped.length()); | 
|  | } | 
|  | if (begin != i) { | 
|  | b.append(s.substring(begin, i)); | 
|  | } | 
|  | b.append(mapped); | 
|  | begin = i+1; | 
|  | } | 
|  | } | 
|  | if (b != null) { | 
|  | if (begin != N) { | 
|  | b.append(s.substring(begin, N)); | 
|  | } | 
|  | return b.toString(); | 
|  | } | 
|  | return s; | 
|  | } | 
|  |  | 
|  | public static void setPageTitle(HDF data, String title) | 
|  | { | 
|  | String s = title; | 
|  | if (DroidDoc.title.length() > 0) { | 
|  | s += " - " + DroidDoc.title; | 
|  | } | 
|  | data.setValue("page.title", s); | 
|  | } | 
|  |  | 
|  | public static LanguageVersion languageVersion() | 
|  | { | 
|  | return LanguageVersion.JAVA_1_5; | 
|  | } | 
|  |  | 
|  | public static int optionLength(String option) | 
|  | { | 
|  | if (option.equals("-d")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-templatedir")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-hdf")) { | 
|  | return 3; | 
|  | } | 
|  | if (option.equals("-toroot")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-samplecode")) { | 
|  | return 4; | 
|  | } | 
|  | if (option.equals("-htmldir")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-title")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-werror")) { | 
|  | return 1; | 
|  | } | 
|  | if (option.equals("-hide")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-warning")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-error")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-keeplist")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-proofread")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-todo")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-public")) { | 
|  | return 1; | 
|  | } | 
|  | if (option.equals("-protected")) { | 
|  | return 1; | 
|  | } | 
|  | if (option.equals("-package")) { | 
|  | return 1; | 
|  | } | 
|  | if (option.equals("-private")) { | 
|  | return 1; | 
|  | } | 
|  | if (option.equals("-hidden")) { | 
|  | return 1; | 
|  | } | 
|  | if (option.equals("-stubs")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-stubpackages")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-sdkvalues")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-apixml")) { | 
|  | return 2; | 
|  | } | 
|  | if (option.equals("-nodocs")) { | 
|  | return 1; | 
|  | } | 
|  | if (option.equals("-since")) { | 
|  | return 3; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | public static boolean validOptions(String[][] options, DocErrorReporter r) | 
|  | { | 
|  | for (String[] a: options) { | 
|  | if (a[0].equals("-error") || a[0].equals("-warning") | 
|  | || a[0].equals("-hide")) { | 
|  | try { | 
|  | Integer.parseInt(a[1]); | 
|  | } | 
|  | catch (NumberFormatException e) { | 
|  | r.printError("bad -" + a[0] + " value must be a number: " | 
|  | + a[1]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public static HDF makeHDF() | 
|  | { | 
|  | HDF data = new HDF(); | 
|  |  | 
|  | for (String[] p: mHDFData) { | 
|  | data.setValue(p[0], p[1]); | 
|  | } | 
|  |  | 
|  | try { | 
|  | for (String p: ClearPage.hdfFiles) { | 
|  | data.readFile(p); | 
|  | } | 
|  | } | 
|  | catch (IOException e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  |  | 
|  | return data; | 
|  | } | 
|  |  | 
|  | public static HDF makePackageHDF() | 
|  | { | 
|  | HDF data = makeHDF(); | 
|  | ClassInfo[] classes = Converter.rootClasses(); | 
|  |  | 
|  | SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>(); | 
|  | for (ClassInfo cl: classes) { | 
|  | PackageInfo pkg = cl.containingPackage(); | 
|  | String name; | 
|  | if (pkg == null) { | 
|  | name = ""; | 
|  | } else { | 
|  | name = pkg.name(); | 
|  | } | 
|  | sorted.put(name, pkg); | 
|  | } | 
|  |  | 
|  | int i = 0; | 
|  | for (String s: sorted.keySet()) { | 
|  | PackageInfo pkg = sorted.get(s); | 
|  |  | 
|  | if (pkg.isHidden()) { | 
|  | continue; | 
|  | } | 
|  | Boolean allHidden = true; | 
|  | int pass = 0; | 
|  | ClassInfo[] classesToCheck = null; | 
|  | while (pass < 5 ) { | 
|  | switch(pass) { | 
|  | case 0: | 
|  | classesToCheck = pkg.ordinaryClasses(); | 
|  | break; | 
|  | case 1: | 
|  | classesToCheck = pkg.enums(); | 
|  | break; | 
|  | case 2: | 
|  | classesToCheck = pkg.errors(); | 
|  | break; | 
|  | case 3: | 
|  | classesToCheck = pkg.exceptions(); | 
|  | break; | 
|  | case 4: | 
|  | classesToCheck = pkg.interfaces(); | 
|  | break; | 
|  | default: | 
|  | System.err.println("Error reading package: " + pkg.name()); | 
|  | break; | 
|  | } | 
|  | for (ClassInfo cl : classesToCheck) { | 
|  | if (!cl.isHidden()) { | 
|  | allHidden = false; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (!allHidden) { | 
|  | break; | 
|  | } | 
|  | pass++; | 
|  | } | 
|  | if (allHidden) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | data.setValue("reference", "true"); | 
|  | data.setValue("docs.packages." + i + ".name", s); | 
|  | data.setValue("docs.packages." + i + ".link", pkg.htmlPage()); | 
|  | data.setValue("docs.packages." + i + ".since", pkg.getSince()); | 
|  | TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr", | 
|  | pkg.firstSentenceTags()); | 
|  | i++; | 
|  | } | 
|  |  | 
|  | sinceTagger.writeVersionNames(data); | 
|  | return data; | 
|  | } | 
|  |  | 
|  | public static void writeDirectory(File dir, String relative) | 
|  | { | 
|  | File[] files = dir.listFiles(); | 
|  | int i, count = files.length; | 
|  | for (i=0; i<count; i++) { | 
|  | File f = files[i]; | 
|  | if (f.isFile()) { | 
|  | String templ = relative + f.getName(); | 
|  | int len = templ.length(); | 
|  | if (len > 3 && ".cs".equals(templ.substring(len-3))) { | 
|  | HDF data = makeHDF(); | 
|  | String filename = templ.substring(0,len-3) + htmlExtension; | 
|  | ClearPage.write(data, templ, filename); | 
|  | } | 
|  | else if (len > 3 && ".jd".equals(templ.substring(len-3))) { | 
|  | String filename = templ.substring(0,len-3) + htmlExtension; | 
|  | DocFile.writePage(f.getAbsolutePath(), relative, filename); | 
|  | } | 
|  | else { | 
|  | ClearPage.copyFile(f, templ); | 
|  | } | 
|  | } | 
|  | else if (f.isDirectory()) { | 
|  | writeDirectory(f, relative + f.getName() + "/"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public static void writeHTMLPages() | 
|  | { | 
|  | File f = new File(ClearPage.htmlDir); | 
|  | if (!f.isDirectory()) { | 
|  | System.err.println("htmlDir not a directory: " + ClearPage.htmlDir); | 
|  | } | 
|  | writeDirectory(f, ""); | 
|  | } | 
|  |  | 
|  | public static void writeLists() | 
|  | { | 
|  | HDF data = makeHDF(); | 
|  |  | 
|  | ClassInfo[] classes = Converter.rootClasses(); | 
|  |  | 
|  | SortedMap<String, Object> sorted = new TreeMap<String, Object>(); | 
|  | for (ClassInfo cl: classes) { | 
|  | if (cl.isHidden()) { | 
|  | continue; | 
|  | } | 
|  | sorted.put(cl.qualifiedName(), cl); | 
|  | PackageInfo pkg = cl.containingPackage(); | 
|  | String name; | 
|  | if (pkg == null) { | 
|  | name = ""; | 
|  | } else { | 
|  | name = pkg.name(); | 
|  | } | 
|  | sorted.put(name, pkg); | 
|  | } | 
|  |  | 
|  | int i = 0; | 
|  | for (String s: sorted.keySet()) { | 
|  | data.setValue("docs.pages." + i + ".id" , ""+i); | 
|  | data.setValue("docs.pages." + i + ".label" , s); | 
|  |  | 
|  | Object o = sorted.get(s); | 
|  | if (o instanceof PackageInfo) { | 
|  | PackageInfo pkg = (PackageInfo)o; | 
|  | data.setValue("docs.pages." + i + ".link" , pkg.htmlPage()); | 
|  | data.setValue("docs.pages." + i + ".type" , "package"); | 
|  | } | 
|  | else if (o instanceof ClassInfo) { | 
|  | ClassInfo cl = (ClassInfo)o; | 
|  | data.setValue("docs.pages." + i + ".link" , cl.htmlPage()); | 
|  | data.setValue("docs.pages." + i + ".type" , "class"); | 
|  | } | 
|  | i++; | 
|  | } | 
|  |  | 
|  | ClearPage.write(data, "lists.cs", javadocDir + "lists.js"); | 
|  | } | 
|  |  | 
|  | public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) { | 
|  | if (!notStrippable.add(cl)) { | 
|  | // slight optimization: if it already contains cl, it already contains | 
|  | // all of cl's parents | 
|  | return; | 
|  | } | 
|  | ClassInfo supr = cl.superclass(); | 
|  | if (supr != null) { | 
|  | cantStripThis(supr, notStrippable); | 
|  | } | 
|  | for (ClassInfo iface: cl.interfaces()) { | 
|  | cantStripThis(iface, notStrippable); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static String getPrintableName(ClassInfo cl) { | 
|  | ClassInfo containingClass = cl.containingClass(); | 
|  | if (containingClass != null) { | 
|  | // This is an inner class. | 
|  | String baseName = cl.name(); | 
|  | baseName = baseName.substring(baseName.lastIndexOf('.') + 1); | 
|  | return getPrintableName(containingClass) + '$' + baseName; | 
|  | } | 
|  | return cl.qualifiedName(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Writes the list of classes that must be present in order to | 
|  | * provide the non-hidden APIs known to javadoc. | 
|  | * | 
|  | * @param filename the path to the file to write the list to | 
|  | */ | 
|  | public static void writeKeepList(String filename) { | 
|  | HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>(); | 
|  | ClassInfo[] all = Converter.allClasses(); | 
|  | Arrays.sort(all); // just to make the file a little more readable | 
|  |  | 
|  | // If a class is public and not hidden, then it and everything it derives | 
|  | // from cannot be stripped.  Otherwise we can strip it. | 
|  | for (ClassInfo cl: all) { | 
|  | if (cl.isPublic() && !cl.isHidden()) { | 
|  | cantStripThis(cl, notStrippable); | 
|  | } | 
|  | } | 
|  | PrintStream stream = null; | 
|  | try { | 
|  | stream = new PrintStream(filename); | 
|  | for (ClassInfo cl: notStrippable) { | 
|  | stream.println(getPrintableName(cl)); | 
|  | } | 
|  | } | 
|  | catch (FileNotFoundException e) { | 
|  | System.err.println("error writing file: " + filename); | 
|  | } | 
|  | finally { | 
|  | if (stream != null) { | 
|  | stream.close(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private static PackageInfo[] sVisiblePackages = null; | 
|  | public static PackageInfo[] choosePackages() { | 
|  | if (sVisiblePackages != null) { | 
|  | return sVisiblePackages; | 
|  | } | 
|  |  | 
|  | ClassInfo[] classes = Converter.rootClasses(); | 
|  | SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>(); | 
|  | for (ClassInfo cl: classes) { | 
|  | PackageInfo pkg = cl.containingPackage(); | 
|  | String name; | 
|  | if (pkg == null) { | 
|  | name = ""; | 
|  | } else { | 
|  | name = pkg.name(); | 
|  | } | 
|  | sorted.put(name, pkg); | 
|  | } | 
|  |  | 
|  | ArrayList<PackageInfo> result = new ArrayList(); | 
|  |  | 
|  | for (String s: sorted.keySet()) { | 
|  | PackageInfo pkg = sorted.get(s); | 
|  |  | 
|  | if (pkg.isHidden()) { | 
|  | continue; | 
|  | } | 
|  | Boolean allHidden = true; | 
|  | int pass = 0; | 
|  | ClassInfo[] classesToCheck = null; | 
|  | while (pass < 5 ) { | 
|  | switch(pass) { | 
|  | case 0: | 
|  | classesToCheck = pkg.ordinaryClasses(); | 
|  | break; | 
|  | case 1: | 
|  | classesToCheck = pkg.enums(); | 
|  | break; | 
|  | case 2: | 
|  | classesToCheck = pkg.errors(); | 
|  | break; | 
|  | case 3: | 
|  | classesToCheck = pkg.exceptions(); | 
|  | break; | 
|  | case 4: | 
|  | classesToCheck = pkg.interfaces(); | 
|  | break; | 
|  | default: | 
|  | System.err.println("Error reading package: " + pkg.name()); | 
|  | break; | 
|  | } | 
|  | for (ClassInfo cl : classesToCheck) { | 
|  | if (!cl.isHidden()) { | 
|  | allHidden = false; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (!allHidden) { | 
|  | break; | 
|  | } | 
|  | pass++; | 
|  | } | 
|  | if (allHidden) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | result.add(pkg); | 
|  | } | 
|  |  | 
|  | sVisiblePackages = result.toArray(new PackageInfo[result.size()]); | 
|  | return sVisiblePackages; | 
|  | } | 
|  |  | 
|  | public static void writePackages(String filename) | 
|  | { | 
|  | HDF data = makePackageHDF(); | 
|  |  | 
|  | int i = 0; | 
|  | for (PackageInfo pkg: choosePackages()) { | 
|  | writePackage(pkg); | 
|  |  | 
|  | data.setValue("docs.packages." + i + ".name", pkg.name()); | 
|  | data.setValue("docs.packages." + i + ".link", pkg.htmlPage()); | 
|  | TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr", | 
|  | pkg.firstSentenceTags()); | 
|  |  | 
|  | i++; | 
|  | } | 
|  |  | 
|  | setPageTitle(data, "Package Index"); | 
|  |  | 
|  | TagInfo.makeHDF(data, "root.descr", | 
|  | Converter.convertTags(root.inlineTags(), null)); | 
|  |  | 
|  | ClearPage.write(data, "packages.cs", filename); | 
|  | ClearPage.write(data, "package-list.cs", javadocDir + "package-list"); | 
|  |  | 
|  | Proofread.writePackages(filename, | 
|  | Converter.convertTags(root.inlineTags(), null)); | 
|  | } | 
|  |  | 
|  | public static void writePackage(PackageInfo pkg) | 
|  | { | 
|  | // these this and the description are in the same directory, | 
|  | // so it's okay | 
|  | HDF data = makePackageHDF(); | 
|  |  | 
|  | String name = pkg.name(); | 
|  |  | 
|  | data.setValue("package.name", name); | 
|  | data.setValue("package.since", pkg.getSince()); | 
|  | data.setValue("package.descr", "...description..."); | 
|  |  | 
|  | makeClassListHDF(data, "package.interfaces", | 
|  | ClassInfo.sortByName(pkg.interfaces())); | 
|  | makeClassListHDF(data, "package.classes", | 
|  | ClassInfo.sortByName(pkg.ordinaryClasses())); | 
|  | makeClassListHDF(data, "package.enums", | 
|  | ClassInfo.sortByName(pkg.enums())); | 
|  | makeClassListHDF(data, "package.exceptions", | 
|  | ClassInfo.sortByName(pkg.exceptions())); | 
|  | makeClassListHDF(data, "package.errors", | 
|  | ClassInfo.sortByName(pkg.errors())); | 
|  | TagInfo.makeHDF(data, "package.shortDescr", | 
|  | pkg.firstSentenceTags()); | 
|  | TagInfo.makeHDF(data, "package.descr", pkg.inlineTags()); | 
|  |  | 
|  | String filename = pkg.htmlPage(); | 
|  | setPageTitle(data, name); | 
|  | ClearPage.write(data, "package.cs", filename); | 
|  |  | 
|  | filename = pkg.fullDescriptionHtmlPage(); | 
|  | setPageTitle(data, name + " Details"); | 
|  | ClearPage.write(data, "package-descr.cs", filename); | 
|  |  | 
|  | Proofread.writePackage(filename, pkg.inlineTags()); | 
|  | } | 
|  |  | 
|  | public static void writeClassLists() | 
|  | { | 
|  | int i; | 
|  | HDF data = makePackageHDF(); | 
|  |  | 
|  | ClassInfo[] classes = PackageInfo.filterHidden( | 
|  | Converter.convertClasses(root.classes())); | 
|  | if (classes.length == 0) { | 
|  | return ; | 
|  | } | 
|  |  | 
|  | Sorter[] sorted = new Sorter[classes.length]; | 
|  | for (i=0; i<sorted.length; i++) { | 
|  | ClassInfo cl = classes[i]; | 
|  | String name = cl.name(); | 
|  | sorted[i] = new Sorter(name, cl); | 
|  | } | 
|  |  | 
|  | Arrays.sort(sorted); | 
|  |  | 
|  | // make a pass and resolve ones that have the same name | 
|  | int firstMatch = 0; | 
|  | String lastName = sorted[0].label; | 
|  | for (i=1; i<sorted.length; i++) { | 
|  | String s = sorted[i].label; | 
|  | if (!lastName.equals(s)) { | 
|  | if (firstMatch != i-1) { | 
|  | // there were duplicates | 
|  | for (int j=firstMatch; j<i; j++) { | 
|  | PackageInfo pkg = ((ClassInfo)sorted[j].data).containingPackage(); | 
|  | if (pkg != null) { | 
|  | sorted[j].label = sorted[j].label + " (" + pkg.name() + ")"; | 
|  | } | 
|  | } | 
|  | } | 
|  | firstMatch = i; | 
|  | lastName = s; | 
|  | } | 
|  | } | 
|  |  | 
|  | // and sort again | 
|  | Arrays.sort(sorted); | 
|  |  | 
|  | for (i=0; i<sorted.length; i++) { | 
|  | String s = sorted[i].label; | 
|  | ClassInfo cl = (ClassInfo)sorted[i].data; | 
|  | char first = Character.toUpperCase(s.charAt(0)); | 
|  | cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i); | 
|  | } | 
|  |  | 
|  | setPageTitle(data, "Class Index"); | 
|  | ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension); | 
|  | } | 
|  |  | 
|  | // we use the word keywords because "index" means something else in html land | 
|  | // the user only ever sees the word index | 
|  | /*    public static void writeKeywords() | 
|  | { | 
|  | ArrayList<KeywordEntry> keywords = new ArrayList<KeywordEntry>(); | 
|  |  | 
|  | ClassInfo[] classes = PackageInfo.filterHidden(Converter.convertClasses(root.classes())); | 
|  |  | 
|  | for (ClassInfo cl: classes) { | 
|  | cl.makeKeywordEntries(keywords); | 
|  | } | 
|  |  | 
|  | HDF data = makeHDF(); | 
|  |  | 
|  | Collections.sort(keywords); | 
|  |  | 
|  | int i=0; | 
|  | for (KeywordEntry entry: keywords) { | 
|  | String base = "keywords." + entry.firstChar() + "." + i; | 
|  | entry.makeHDF(data, base); | 
|  | i++; | 
|  | } | 
|  |  | 
|  | setPageTitle(data, "Index"); | 
|  | ClearPage.write(data, "keywords.cs", javadocDir + "keywords" + htmlExtension); | 
|  | } */ | 
|  |  | 
|  | public static void writeHierarchy() | 
|  | { | 
|  | ClassInfo[] classes = Converter.rootClasses(); | 
|  | ArrayList<ClassInfo> info = new ArrayList<ClassInfo>(); | 
|  | for (ClassInfo cl: classes) { | 
|  | if (!cl.isHidden()) { | 
|  | info.add(cl); | 
|  | } | 
|  | } | 
|  | HDF data = makePackageHDF(); | 
|  | Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()])); | 
|  | setPageTitle(data, "Class Hierarchy"); | 
|  | ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension); | 
|  | } | 
|  |  | 
|  | public static void writeClasses() | 
|  | { | 
|  | ClassInfo[] classes = Converter.rootClasses(); | 
|  |  | 
|  | for (ClassInfo cl: classes) { | 
|  | HDF data = makePackageHDF(); | 
|  | if (!cl.isHidden()) { | 
|  | writeClass(cl, data); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public static void writeClass(ClassInfo cl, HDF data) | 
|  | { | 
|  | cl.makeHDF(data); | 
|  |  | 
|  | setPageTitle(data, cl.name()); | 
|  | ClearPage.write(data, "class.cs", cl.htmlPage()); | 
|  |  | 
|  | Proofread.writeClass(cl.htmlPage(), cl); | 
|  | } | 
|  |  | 
|  | public static void makeClassListHDF(HDF data, String base, | 
|  | ClassInfo[] classes) | 
|  | { | 
|  | for (int i=0; i<classes.length; i++) { | 
|  | ClassInfo cl = classes[i]; | 
|  | if (!cl.isHidden()) { | 
|  | cl.makeShortDescrHDF(data, base + "." + i); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public static String linkTarget(String source, String target) | 
|  | { | 
|  | String[] src = source.split("/"); | 
|  | String[] tgt = target.split("/"); | 
|  |  | 
|  | int srclen = src.length; | 
|  | int tgtlen = tgt.length; | 
|  |  | 
|  | int same = 0; | 
|  | while (same < (srclen-1) | 
|  | && same < (tgtlen-1) | 
|  | && (src[same].equals(tgt[same]))) { | 
|  | same++; | 
|  | } | 
|  |  | 
|  | String s = ""; | 
|  |  | 
|  | int up = srclen-same-1; | 
|  | for (int i=0; i<up; i++) { | 
|  | s += "../"; | 
|  | } | 
|  |  | 
|  |  | 
|  | int N = tgtlen-1; | 
|  | for (int i=same; i<N; i++) { | 
|  | s += tgt[i] + '/'; | 
|  | } | 
|  | s += tgt[tgtlen-1]; | 
|  |  | 
|  | return s; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns true if the given element has an @hide annotation. | 
|  | */ | 
|  | private static boolean hasHideAnnotation(Doc doc) { | 
|  | return doc.getRawCommentText().indexOf("@hide") != -1; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns true if the given element is hidden. | 
|  | */ | 
|  | private static boolean isHidden(Doc doc) { | 
|  | // Methods, fields, constructors. | 
|  | if (doc instanceof MemberDoc) { | 
|  | return hasHideAnnotation(doc); | 
|  | } | 
|  |  | 
|  | // Classes, interfaces, enums, annotation types. | 
|  | if (doc instanceof ClassDoc) { | 
|  | ClassDoc classDoc = (ClassDoc) doc; | 
|  |  | 
|  | // Check the containing package. | 
|  | if (hasHideAnnotation(classDoc.containingPackage())) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Check the class doc and containing class docs if this is a | 
|  | // nested class. | 
|  | ClassDoc current = classDoc; | 
|  | do { | 
|  | if (hasHideAnnotation(current)) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | current = current.containingClass(); | 
|  | } while (current != null); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Filters out hidden elements. | 
|  | */ | 
|  | private static Object filterHidden(Object o, Class<?> expected) { | 
|  | if (o == null) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | Class type = o.getClass(); | 
|  | if (type.getName().startsWith("com.sun.")) { | 
|  | // TODO: Implement interfaces from superclasses, too. | 
|  | return Proxy.newProxyInstance(type.getClassLoader(), | 
|  | type.getInterfaces(), new HideHandler(o)); | 
|  | } else if (o instanceof Object[]) { | 
|  | Class<?> componentType = expected.getComponentType(); | 
|  | Object[] array = (Object[]) o; | 
|  | List<Object> list = new ArrayList<Object>(array.length); | 
|  | for (Object entry : array) { | 
|  | if ((entry instanceof Doc) && isHidden((Doc) entry)) { | 
|  | continue; | 
|  | } | 
|  | list.add(filterHidden(entry, componentType)); | 
|  | } | 
|  | return list.toArray( | 
|  | (Object[]) Array.newInstance(componentType, list.size())); | 
|  | } else { | 
|  | return o; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Filters hidden elements out of method return values. | 
|  | */ | 
|  | private static class HideHandler implements InvocationHandler { | 
|  |  | 
|  | private final Object target; | 
|  |  | 
|  | public HideHandler(Object target) { | 
|  | this.target = target; | 
|  | } | 
|  |  | 
|  | public Object invoke(Object proxy, Method method, Object[] args) | 
|  | throws Throwable { | 
|  | String methodName = method.getName(); | 
|  | if (args != null) { | 
|  | if (methodName.equals("compareTo") || | 
|  | methodName.equals("equals") || | 
|  | methodName.equals("overrides") || | 
|  | methodName.equals("subclassOf")) { | 
|  | args[0] = unwrap(args[0]); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (methodName.equals("getRawCommentText")) { | 
|  | return filterComment((String) method.invoke(target, args)); | 
|  | } | 
|  |  | 
|  | // escape "&" in disjunctive types. | 
|  | if (proxy instanceof Type && methodName.equals("toString")) { | 
|  | return ((String) method.invoke(target, args)) | 
|  | .replace("&", "&"); | 
|  | } | 
|  |  | 
|  | try { | 
|  | return filterHidden(method.invoke(target, args), | 
|  | method.getReturnType()); | 
|  | } catch (InvocationTargetException e) { | 
|  | throw e.getTargetException(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private String filterComment(String s) { | 
|  | if (s == null) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | s = s.trim(); | 
|  |  | 
|  | // Work around off by one error | 
|  | while (s.length() >= 5 | 
|  | && s.charAt(s.length() - 5) == '{') { | 
|  | s += " "; | 
|  | } | 
|  |  | 
|  | return s; | 
|  | } | 
|  |  | 
|  | private static Object unwrap(Object proxy) { | 
|  | if (proxy instanceof Proxy) | 
|  | return ((HideHandler)Proxy.getInvocationHandler(proxy)).target; | 
|  | return proxy; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static String scope(Scoped scoped) { | 
|  | if (scoped.isPublic()) { | 
|  | return "public"; | 
|  | } | 
|  | else if (scoped.isProtected()) { | 
|  | return "protected"; | 
|  | } | 
|  | else if (scoped.isPackagePrivate()) { | 
|  | return ""; | 
|  | } | 
|  | else if (scoped.isPrivate()) { | 
|  | return "private"; | 
|  | } | 
|  | else { | 
|  | throw new RuntimeException("invalid scope for object " + scoped); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Collect the values used by the Dev tools and write them in files packaged with the SDK | 
|  | * @param output the ouput directory for the files. | 
|  | */ | 
|  | private static void writeSdkValues(String output) { | 
|  | ArrayList<String> activityActions = new ArrayList<String>(); | 
|  | ArrayList<String> broadcastActions = new ArrayList<String>(); | 
|  | ArrayList<String> serviceActions = new ArrayList<String>(); | 
|  | ArrayList<String> categories = new ArrayList<String>(); | 
|  |  | 
|  | ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>(); | 
|  | ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>(); | 
|  | ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>(); | 
|  |  | 
|  | ClassInfo[] classes = Converter.allClasses(); | 
|  |  | 
|  | // Go through all the fields of all the classes, looking SDK stuff. | 
|  | for (ClassInfo clazz : classes) { | 
|  |  | 
|  | // first check constant fields for the SdkConstant annotation. | 
|  | FieldInfo[] fields = clazz.allSelfFields(); | 
|  | for (FieldInfo field : fields) { | 
|  | Object cValue = field.constantValue(); | 
|  | if (cValue != null) { | 
|  | AnnotationInstanceInfo[] annotations = field.annotations(); | 
|  | if (annotations.length > 0) { | 
|  | for (AnnotationInstanceInfo annotation : annotations) { | 
|  | if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) { | 
|  | AnnotationValueInfo[] values = annotation.elementValues(); | 
|  | if (values.length > 0) { | 
|  | String type = values[0].valueString(); | 
|  | if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) { | 
|  | activityActions.add(cValue.toString()); | 
|  | } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) { | 
|  | broadcastActions.add(cValue.toString()); | 
|  | } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) { | 
|  | serviceActions.add(cValue.toString()); | 
|  | } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) { | 
|  | categories.add(cValue.toString()); | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Now check the class for @Widget or if its in the android.widget package | 
|  | // (unless the class is hidden or abstract, or non public) | 
|  | if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) { | 
|  | boolean annotated = false; | 
|  | AnnotationInstanceInfo[] annotations = clazz.annotations(); | 
|  | if (annotations.length > 0) { | 
|  | for (AnnotationInstanceInfo annotation : annotations) { | 
|  | if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) { | 
|  | widgets.add(clazz); | 
|  | annotated = true; | 
|  | break; | 
|  | } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) { | 
|  | layouts.add(clazz); | 
|  | annotated = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (annotated == false) { | 
|  | // lets check if this is inside android.widget | 
|  | PackageInfo pckg = clazz.containingPackage(); | 
|  | String packageName = pckg.name(); | 
|  | if ("android.widget".equals(packageName) || | 
|  | "android.view".equals(packageName)) { | 
|  | // now we check what this class inherits either from android.view.ViewGroup | 
|  | // or android.view.View, or android.view.ViewGroup.LayoutParams | 
|  | int type = checkInheritance(clazz); | 
|  | switch (type) { | 
|  | case TYPE_WIDGET: | 
|  | widgets.add(clazz); | 
|  | break; | 
|  | case TYPE_LAYOUT: | 
|  | layouts.add(clazz); | 
|  | break; | 
|  | case TYPE_LAYOUT_PARAM: | 
|  | layoutParams.add(clazz); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // now write the files, whether or not the list are empty. | 
|  | // the SDK built requires those files to be present. | 
|  |  | 
|  | Collections.sort(activityActions); | 
|  | writeValues(output + "/activity_actions.txt", activityActions); | 
|  |  | 
|  | Collections.sort(broadcastActions); | 
|  | writeValues(output + "/broadcast_actions.txt", broadcastActions); | 
|  |  | 
|  | Collections.sort(serviceActions); | 
|  | writeValues(output + "/service_actions.txt", serviceActions); | 
|  |  | 
|  | Collections.sort(categories); | 
|  | writeValues(output + "/categories.txt", categories); | 
|  |  | 
|  | // before writing the list of classes, we do some checks, to make sure the layout params | 
|  | // are enclosed by a layout class (and not one that has been declared as a widget) | 
|  | for (int i = 0 ; i < layoutParams.size();) { | 
|  | ClassInfo layoutParamClass = layoutParams.get(i); | 
|  | ClassInfo containingClass = layoutParamClass.containingClass(); | 
|  | if (containingClass == null || layouts.indexOf(containingClass) == -1) { | 
|  | layoutParams.remove(i); | 
|  | } else { | 
|  | i++; | 
|  | } | 
|  | } | 
|  |  | 
|  | writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Writes a list of values into a text files. | 
|  | * @param pathname the absolute os path of the output file. | 
|  | * @param values the list of values to write. | 
|  | */ | 
|  | private static void writeValues(String pathname, ArrayList<String> values) { | 
|  | FileWriter fw = null; | 
|  | BufferedWriter bw = null; | 
|  | try { | 
|  | fw = new FileWriter(pathname, false); | 
|  | bw = new BufferedWriter(fw); | 
|  |  | 
|  | for (String value : values) { | 
|  | bw.append(value).append('\n'); | 
|  | } | 
|  | } catch (IOException e) { | 
|  | // pass for now | 
|  | } finally { | 
|  | try { | 
|  | if (bw != null) bw.close(); | 
|  | } catch (IOException e) { | 
|  | // pass for now | 
|  | } | 
|  | try { | 
|  | if (fw != null) fw.close(); | 
|  | } catch (IOException e) { | 
|  | // pass for now | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Writes the widget/layout/layout param classes into a text files. | 
|  | * @param pathname the absolute os path of the output file. | 
|  | * @param widgets the list of widget classes to write. | 
|  | * @param layouts the list of layout classes to write. | 
|  | * @param layoutParams the list of layout param classes to write. | 
|  | */ | 
|  | private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets, | 
|  | ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) { | 
|  | FileWriter fw = null; | 
|  | BufferedWriter bw = null; | 
|  | try { | 
|  | fw = new FileWriter(pathname, false); | 
|  | bw = new BufferedWriter(fw); | 
|  |  | 
|  | // write the 3 types of classes. | 
|  | for (ClassInfo clazz : widgets) { | 
|  | writeClass(bw, clazz, 'W'); | 
|  | } | 
|  | for (ClassInfo clazz : layoutParams) { | 
|  | writeClass(bw, clazz, 'P'); | 
|  | } | 
|  | for (ClassInfo clazz : layouts) { | 
|  | writeClass(bw, clazz, 'L'); | 
|  | } | 
|  | } catch (IOException e) { | 
|  | // pass for now | 
|  | } finally { | 
|  | try { | 
|  | if (bw != null) bw.close(); | 
|  | } catch (IOException e) { | 
|  | // pass for now | 
|  | } | 
|  | try { | 
|  | if (fw != null) fw.close(); | 
|  | } catch (IOException e) { | 
|  | // pass for now | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Writes a class name and its super class names into a {@link BufferedWriter}. | 
|  | * @param writer the BufferedWriter to write into | 
|  | * @param clazz the class to write | 
|  | * @param prefix the prefix to put at the beginning of the line. | 
|  | * @throws IOException | 
|  | */ | 
|  | private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix) | 
|  | throws IOException { | 
|  | writer.append(prefix).append(clazz.qualifiedName()); | 
|  | ClassInfo superClass = clazz; | 
|  | while ((superClass = superClass.superclass()) != null) { | 
|  | writer.append(' ').append(superClass.qualifiedName()); | 
|  | } | 
|  | writer.append('\n'); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Checks the inheritance of {@link ClassInfo} objects. This method return | 
|  | * <ul> | 
|  | * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li> | 
|  | * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li> | 
|  | * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends <code>android.view.ViewGroup$LayoutParams</code></li> | 
|  | * <li>{@link #TYPE_NONE}: in all other cases</li> | 
|  | * </ul> | 
|  | * @param clazz the {@link ClassInfo} to check. | 
|  | */ | 
|  | private static int checkInheritance(ClassInfo clazz) { | 
|  | if ("android.view.ViewGroup".equals(clazz.qualifiedName())) { | 
|  | return TYPE_LAYOUT; | 
|  | } else if ("android.view.View".equals(clazz.qualifiedName())) { | 
|  | return TYPE_WIDGET; | 
|  | } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) { | 
|  | return TYPE_LAYOUT_PARAM; | 
|  | } | 
|  |  | 
|  | ClassInfo parent = clazz.superclass(); | 
|  | if (parent != null) { | 
|  | return checkInheritance(parent); | 
|  | } | 
|  |  | 
|  | return TYPE_NONE; | 
|  | } | 
|  | } |