all the source code pages are NOT generated for the offline version of the docs (only show source in html for online docs). This won't work until a companion change from framwork/base/Android.mk is submitted (but shouldn't break the build). Change-Id: I06c404540870071c2a5a8aa460e156506fd975cb
1379 lines
45 KiB
Java
1379 lines
45 KiB
Java
/*
|
|
* 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;
|
|
boolean offlineMode = 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]);
|
|
}
|
|
else if (a[0].equals("-offlinemode")) {
|
|
offlineMode = true;
|
|
}
|
|
}
|
|
|
|
// 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(offlineMode);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
if (option.equals("-offlinemode")) {
|
|
return 1;
|
|
}
|
|
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 or @pending annotation.
|
|
*/
|
|
private static boolean hasHideAnnotation(Doc doc) {
|
|
String comment = doc.getRawCommentText();
|
|
return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -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;
|
|
}
|
|
}
|