PageRenderTime 59ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/droiddoc/src/DroidDoc.java

https://github.com/jsherman/platform_build
Java | 1339 lines | 1049 code | 148 blank | 142 comment | 293 complexity | aac2ad2c6d1eff8a2f9dab2dd2a917d6 MD5 | raw file
  1. /*
  2. * Copyright (C) 2008 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import com.sun.javadoc.*;
  17. import org.clearsilver.HDF;
  18. import java.util.*;
  19. import java.io.*;
  20. import java.lang.reflect.Proxy;
  21. import java.lang.reflect.Array;
  22. import java.lang.reflect.InvocationHandler;
  23. import java.lang.reflect.InvocationTargetException;
  24. import java.lang.reflect.Method;
  25. public class DroidDoc
  26. {
  27. private static final String SDK_CONSTANT_ANNOTATION = "android.annotation.SdkConstant";
  28. private static final String SDK_CONSTANT_TYPE_ACTIVITY_ACTION = "android.annotation.SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION";
  29. private static final String SDK_CONSTANT_TYPE_BROADCAST_ACTION = "android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION";
  30. private static final String SDK_CONSTANT_TYPE_SERVICE_ACTION = "android.annotation.SdkConstant.SdkConstantType.SERVICE_INTENT_ACTION";
  31. private static final String SDK_CONSTANT_TYPE_CATEGORY = "android.annotation.SdkConstant.SdkConstantType.INTENT_CATEGORY";
  32. private static final String SDK_WIDGET_ANNOTATION = "android.annotation.Widget";
  33. private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout";
  34. private static final int TYPE_NONE = 0;
  35. private static final int TYPE_WIDGET = 1;
  36. private static final int TYPE_LAYOUT = 2;
  37. private static final int TYPE_LAYOUT_PARAM = 3;
  38. public static final int SHOW_PUBLIC = 0x00000001;
  39. public static final int SHOW_PROTECTED = 0x00000003;
  40. public static final int SHOW_PACKAGE = 0x00000007;
  41. public static final int SHOW_PRIVATE = 0x0000000f;
  42. public static final int SHOW_HIDDEN = 0x0000001f;
  43. public static int showLevel = SHOW_PROTECTED;
  44. public static final String javadocDir = "reference/";
  45. public static String htmlExtension;
  46. public static RootDoc root;
  47. public static ArrayList<String[]> mHDFData = new ArrayList<String[]>();
  48. public static Map<Character,String> escapeChars = new HashMap<Character,String>();
  49. public static String title = "";
  50. public static boolean checkLevel(int level)
  51. {
  52. return (showLevel & level) == level;
  53. }
  54. public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp,
  55. boolean priv, boolean hidden)
  56. {
  57. int level = 0;
  58. if (hidden && !checkLevel(SHOW_HIDDEN)) {
  59. return false;
  60. }
  61. if (pub && checkLevel(SHOW_PUBLIC)) {
  62. return true;
  63. }
  64. if (prot && checkLevel(SHOW_PROTECTED)) {
  65. return true;
  66. }
  67. if (pkgp && checkLevel(SHOW_PACKAGE)) {
  68. return true;
  69. }
  70. if (priv && checkLevel(SHOW_PRIVATE)) {
  71. return true;
  72. }
  73. return false;
  74. }
  75. public static boolean start(RootDoc r)
  76. {
  77. String keepListFile = null;
  78. String proofreadFile = null;
  79. String todoFile = null;
  80. String sdkValuePath = null;
  81. ArrayList<SampleCode> sampleCodes = new ArrayList<SampleCode>();
  82. String stubsDir = null;
  83. //Create the dependency graph for the stubs directory
  84. boolean apiXML = false;
  85. String apiFile = null;
  86. String debugStubsFile = "";
  87. HashSet<String> stubPackages = null;
  88. root = r;
  89. String[][] options = r.options();
  90. for (String[] a: options) {
  91. if (a[0].equals("-d")) {
  92. ClearPage.outputDir = a[1];
  93. }
  94. else if (a[0].equals("-templatedir")) {
  95. ClearPage.addTemplateDir(a[1]);
  96. }
  97. else if (a[0].equals("-hdf")) {
  98. mHDFData.add(new String[] {a[1], a[2]});
  99. }
  100. else if (a[0].equals("-toroot")) {
  101. ClearPage.toroot = a[1];
  102. }
  103. else if (a[0].equals("-samplecode")) {
  104. sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
  105. }
  106. else if (a[0].equals("-htmldir")) {
  107. ClearPage.htmlDir = a[1];
  108. }
  109. else if (a[0].equals("-title")) {
  110. DroidDoc.title = a[1];
  111. }
  112. else if (a[0].equals("-werror")) {
  113. Errors.setWarningsAreErrors(true);
  114. }
  115. else if (a[0].equals("-error") || a[0].equals("-warning")
  116. || a[0].equals("-hide")) {
  117. try {
  118. int level = -1;
  119. if (a[0].equals("-error")) {
  120. level = Errors.ERROR;
  121. }
  122. else if (a[0].equals("-warning")) {
  123. level = Errors.WARNING;
  124. }
  125. else if (a[0].equals("-hide")) {
  126. level = Errors.HIDDEN;
  127. }
  128. Errors.setErrorLevel(Integer.parseInt(a[1]), level);
  129. }
  130. catch (NumberFormatException e) {
  131. // already printed below
  132. return false;
  133. }
  134. }
  135. else if (a[0].equals("-keeplist")) {
  136. keepListFile = a[1];
  137. }
  138. else if (a[0].equals("-proofread")) {
  139. proofreadFile = a[1];
  140. }
  141. else if (a[0].equals("-todo")) {
  142. todoFile = a[1];
  143. }
  144. else if (a[0].equals("-public")) {
  145. showLevel = SHOW_PUBLIC;
  146. }
  147. else if (a[0].equals("-protected")) {
  148. showLevel = SHOW_PROTECTED;
  149. }
  150. else if (a[0].equals("-package")) {
  151. showLevel = SHOW_PACKAGE;
  152. }
  153. else if (a[0].equals("-private")) {
  154. showLevel = SHOW_PRIVATE;
  155. }
  156. else if (a[0].equals("-hidden")) {
  157. showLevel = SHOW_HIDDEN;
  158. }
  159. else if (a[0].equals("-stubs")) {
  160. stubsDir = a[1];
  161. }
  162. else if (a[0].equals("-stubpackages")) {
  163. stubPackages = new HashSet();
  164. for (String pkg: a[1].split(":")) {
  165. stubPackages.add(pkg);
  166. }
  167. }
  168. else if (a[0].equals("-sdkvalues")) {
  169. sdkValuePath = a[1];
  170. }
  171. else if (a[0].equals("-apixml")) {
  172. apiXML = true;
  173. apiFile = a[1];
  174. }
  175. }
  176. // read some prefs from the template
  177. if (!readTemplateSettings()) {
  178. return false;
  179. }
  180. // Set up the data structures
  181. Converter.makeInfo(r);
  182. // Files for proofreading
  183. if (proofreadFile != null) {
  184. Proofread.initProofread(proofreadFile);
  185. }
  186. if (todoFile != null) {
  187. TodoFile.writeTodoFile(todoFile);
  188. }
  189. // HTML Pages
  190. if (ClearPage.htmlDir != null) {
  191. writeHTMLPages();
  192. }
  193. // Navigation tree
  194. NavTree.writeNavTree(javadocDir);
  195. // Packages Pages
  196. writePackages(javadocDir
  197. + (ClearPage.htmlDir!=null
  198. ? "packages" + htmlExtension
  199. : "index" + htmlExtension));
  200. // Classes
  201. writeClassLists();
  202. writeClasses();
  203. writeHierarchy();
  204. // writeKeywords();
  205. // Lists for JavaScript
  206. writeLists();
  207. if (keepListFile != null) {
  208. writeKeepList(keepListFile);
  209. }
  210. // Sample Code
  211. for (SampleCode sc: sampleCodes) {
  212. sc.write();
  213. }
  214. // Index page
  215. writeIndex();
  216. Proofread.finishProofread(proofreadFile);
  217. // Stubs
  218. if (stubsDir != null) {
  219. Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
  220. }
  221. if (sdkValuePath != null) {
  222. writeSdkValues(sdkValuePath);
  223. }
  224. Errors.printErrors();
  225. return !Errors.hadError;
  226. }
  227. private static void writeIndex() {
  228. HDF data = makeHDF();
  229. ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension);
  230. }
  231. private static boolean readTemplateSettings()
  232. {
  233. HDF data = makeHDF();
  234. htmlExtension = data.getValue("template.extension", ".html");
  235. int i=0;
  236. while (true) {
  237. String k = data.getValue("template.escape." + i + ".key", "");
  238. String v = data.getValue("template.escape." + i + ".value", "");
  239. if ("".equals(k)) {
  240. break;
  241. }
  242. if (k.length() != 1) {
  243. System.err.println("template.escape." + i + ".key must have a length of 1: " + k);
  244. return false;
  245. }
  246. escapeChars.put(k.charAt(0), v);
  247. i++;
  248. }
  249. return true;
  250. }
  251. public static String escape(String s) {
  252. if (escapeChars.size() == 0) {
  253. return s;
  254. }
  255. StringBuffer b = null;
  256. int begin = 0;
  257. final int N = s.length();
  258. for (int i=0; i<N; i++) {
  259. char c = s.charAt(i);
  260. String mapped = escapeChars.get(c);
  261. if (mapped != null) {
  262. if (b == null) {
  263. b = new StringBuffer(s.length() + mapped.length());
  264. }
  265. if (begin != i) {
  266. b.append(s.substring(begin, i));
  267. }
  268. b.append(mapped);
  269. begin = i+1;
  270. }
  271. }
  272. if (b != null) {
  273. if (begin != N) {
  274. b.append(s.substring(begin, N));
  275. }
  276. return b.toString();
  277. }
  278. return s;
  279. }
  280. public static void setPageTitle(HDF data, String title)
  281. {
  282. String s = title;
  283. if (DroidDoc.title.length() > 0) {
  284. s += " - " + DroidDoc.title;
  285. }
  286. data.setValue("page.title", s);
  287. }
  288. public static LanguageVersion languageVersion()
  289. {
  290. return LanguageVersion.JAVA_1_5;
  291. }
  292. public static int optionLength(String option)
  293. {
  294. if (option.equals("-d")) {
  295. return 2;
  296. }
  297. if (option.equals("-templatedir")) {
  298. return 2;
  299. }
  300. if (option.equals("-hdf")) {
  301. return 3;
  302. }
  303. if (option.equals("-toroot")) {
  304. return 2;
  305. }
  306. if (option.equals("-samplecode")) {
  307. return 4;
  308. }
  309. if (option.equals("-htmldir")) {
  310. return 2;
  311. }
  312. if (option.equals("-title")) {
  313. return 2;
  314. }
  315. if (option.equals("-werror")) {
  316. return 1;
  317. }
  318. if (option.equals("-hide")) {
  319. return 2;
  320. }
  321. if (option.equals("-warning")) {
  322. return 2;
  323. }
  324. if (option.equals("-error")) {
  325. return 2;
  326. }
  327. if (option.equals("-keeplist")) {
  328. return 2;
  329. }
  330. if (option.equals("-proofread")) {
  331. return 2;
  332. }
  333. if (option.equals("-todo")) {
  334. return 2;
  335. }
  336. if (option.equals("-public")) {
  337. return 1;
  338. }
  339. if (option.equals("-protected")) {
  340. return 1;
  341. }
  342. if (option.equals("-package")) {
  343. return 1;
  344. }
  345. if (option.equals("-private")) {
  346. return 1;
  347. }
  348. if (option.equals("-hidden")) {
  349. return 1;
  350. }
  351. if (option.equals("-stubs")) {
  352. return 2;
  353. }
  354. if (option.equals("-stubpackages")) {
  355. return 2;
  356. }
  357. if (option.equals("-sdkvalues")) {
  358. return 2;
  359. }
  360. if (option.equals("-apixml")) {
  361. return 2;
  362. }
  363. return 0;
  364. }
  365. public static boolean validOptions(String[][] options, DocErrorReporter r)
  366. {
  367. for (String[] a: options) {
  368. if (a[0].equals("-error") || a[0].equals("-warning")
  369. || a[0].equals("-hide")) {
  370. try {
  371. Integer.parseInt(a[1]);
  372. }
  373. catch (NumberFormatException e) {
  374. r.printError("bad -" + a[0] + " value must be a number: "
  375. + a[1]);
  376. return false;
  377. }
  378. }
  379. }
  380. return true;
  381. }
  382. public static HDF makeHDF()
  383. {
  384. HDF data = new HDF();
  385. for (String[] p: mHDFData) {
  386. data.setValue(p[0], p[1]);
  387. }
  388. try {
  389. for (String p: ClearPage.hdfFiles) {
  390. data.readFile(p);
  391. }
  392. }
  393. catch (IOException e) {
  394. throw new RuntimeException(e);
  395. }
  396. return data;
  397. }
  398. public static HDF makePackageHDF()
  399. {
  400. HDF data = makeHDF();
  401. ClassInfo[] classes = Converter.rootClasses();
  402. SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
  403. for (ClassInfo cl: classes) {
  404. PackageInfo pkg = cl.containingPackage();
  405. String name;
  406. if (pkg == null) {
  407. name = "";
  408. } else {
  409. name = pkg.name();
  410. }
  411. sorted.put(name, pkg);
  412. }
  413. int i = 0;
  414. for (String s: sorted.keySet()) {
  415. PackageInfo pkg = sorted.get(s);
  416. if (pkg.isHidden()) {
  417. continue;
  418. }
  419. Boolean allHidden = true;
  420. int pass = 1;
  421. ClassInfo[] classesToCheck = pkg.ordinaryClasses();
  422. while (pass < 5 ) {
  423. switch(pass) {
  424. case 1:
  425. classesToCheck = pkg.enums();
  426. break;
  427. case 2:
  428. classesToCheck = pkg.errors();
  429. break;
  430. case 3:
  431. classesToCheck = pkg.exceptions();
  432. break;
  433. case 4:
  434. classesToCheck = pkg.interfaces();
  435. break;
  436. default:
  437. System.err.println("Error reading package: " + pkg.name());
  438. break;
  439. }
  440. for (ClassInfo cl : classesToCheck) {
  441. if (!cl.isHidden()) {
  442. allHidden = false;
  443. break;
  444. }
  445. }
  446. if (!allHidden) {
  447. break;
  448. }
  449. pass++;
  450. }
  451. if (allHidden) {
  452. continue;
  453. }
  454. data.setValue("reference", "true");
  455. data.setValue("docs.packages." + i + ".name", s);
  456. data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
  457. TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
  458. pkg.firstSentenceTags());
  459. i++;
  460. }
  461. return data;
  462. }
  463. public static void writeDirectory(File dir, String relative)
  464. {
  465. File[] files = dir.listFiles();
  466. int i, count = files.length;
  467. for (i=0; i<count; i++) {
  468. File f = files[i];
  469. if (f.isFile()) {
  470. String templ = relative + f.getName();
  471. int len = templ.length();
  472. if (len > 3 && ".cs".equals(templ.substring(len-3))) {
  473. HDF data = makeHDF();
  474. String filename = templ.substring(0,len-3) + htmlExtension;
  475. ClearPage.write(data, templ, filename);
  476. }
  477. else if (len > 3 && ".jd".equals(templ.substring(len-3))) {
  478. String filename = templ.substring(0,len-3) + htmlExtension;
  479. DocFile.writePage(f.getAbsolutePath(), relative, filename);
  480. }
  481. else {
  482. ClearPage.copyFile(f, templ);
  483. }
  484. }
  485. else if (f.isDirectory()) {
  486. writeDirectory(f, relative + f.getName() + "/");
  487. }
  488. }
  489. }
  490. public static void writeHTMLPages()
  491. {
  492. File f = new File(ClearPage.htmlDir);
  493. if (!f.isDirectory()) {
  494. System.err.println("htmlDir not a directory: " + ClearPage.htmlDir);
  495. }
  496. writeDirectory(f, "");
  497. }
  498. public static void writeLists()
  499. {
  500. HDF data = makeHDF();
  501. ClassInfo[] classes = Converter.rootClasses();
  502. SortedMap<String, Object> sorted = new TreeMap<String, Object>();
  503. for (ClassInfo cl: classes) {
  504. if (cl.isHidden()) {
  505. continue;
  506. }
  507. sorted.put(cl.qualifiedName(), cl);
  508. PackageInfo pkg = cl.containingPackage();
  509. String name;
  510. if (pkg == null) {
  511. name = "";
  512. } else {
  513. name = pkg.name();
  514. }
  515. sorted.put(name, pkg);
  516. }
  517. int i = 0;
  518. for (String s: sorted.keySet()) {
  519. data.setValue("docs.pages." + i + ".id" , ""+i);
  520. data.setValue("docs.pages." + i + ".label" , s);
  521. Object o = sorted.get(s);
  522. if (o instanceof PackageInfo) {
  523. PackageInfo pkg = (PackageInfo)o;
  524. data.setValue("docs.pages." + i + ".link" , pkg.htmlPage());
  525. data.setValue("docs.pages." + i + ".type" , "package");
  526. }
  527. else if (o instanceof ClassInfo) {
  528. ClassInfo cl = (ClassInfo)o;
  529. data.setValue("docs.pages." + i + ".link" , cl.htmlPage());
  530. data.setValue("docs.pages." + i + ".type" , "class");
  531. }
  532. i++;
  533. }
  534. ClearPage.write(data, "lists.cs", javadocDir + "lists.js");
  535. }
  536. public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) {
  537. if (!notStrippable.add(cl)) {
  538. // slight optimization: if it already contains cl, it already contains
  539. // all of cl's parents
  540. return;
  541. }
  542. ClassInfo supr = cl.superclass();
  543. if (supr != null) {
  544. cantStripThis(supr, notStrippable);
  545. }
  546. for (ClassInfo iface: cl.interfaces()) {
  547. cantStripThis(iface, notStrippable);
  548. }
  549. }
  550. private static String getPrintableName(ClassInfo cl) {
  551. ClassInfo containingClass = cl.containingClass();
  552. if (containingClass != null) {
  553. // This is an inner class.
  554. String baseName = cl.name();
  555. baseName = baseName.substring(baseName.lastIndexOf('.') + 1);
  556. return getPrintableName(containingClass) + '$' + baseName;
  557. }
  558. return cl.qualifiedName();
  559. }
  560. /**
  561. * Writes the list of classes that must be present in order to
  562. * provide the non-hidden APIs known to javadoc.
  563. *
  564. * @param filename the path to the file to write the list to
  565. */
  566. public static void writeKeepList(String filename) {
  567. HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
  568. ClassInfo[] all = Converter.allClasses();
  569. Arrays.sort(all); // just to make the file a little more readable
  570. // If a class is public and not hidden, then it and everything it derives
  571. // from cannot be stripped. Otherwise we can strip it.
  572. for (ClassInfo cl: all) {
  573. if (cl.isPublic() && !cl.isHidden()) {
  574. cantStripThis(cl, notStrippable);
  575. }
  576. }
  577. PrintStream stream = null;
  578. try {
  579. stream = new PrintStream(filename);
  580. for (ClassInfo cl: notStrippable) {
  581. stream.println(getPrintableName(cl));
  582. }
  583. }
  584. catch (FileNotFoundException e) {
  585. System.err.println("error writing file: " + filename);
  586. }
  587. finally {
  588. if (stream != null) {
  589. stream.close();
  590. }
  591. }
  592. }
  593. private static PackageInfo[] sVisiblePackages = null;
  594. public static PackageInfo[] choosePackages() {
  595. if (sVisiblePackages != null) {
  596. return sVisiblePackages;
  597. }
  598. ClassInfo[] classes = Converter.rootClasses();
  599. SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
  600. for (ClassInfo cl: classes) {
  601. PackageInfo pkg = cl.containingPackage();
  602. String name;
  603. if (pkg == null) {
  604. name = "";
  605. } else {
  606. name = pkg.name();
  607. }
  608. sorted.put(name, pkg);
  609. }
  610. ArrayList<PackageInfo> result = new ArrayList();
  611. for (String s: sorted.keySet()) {
  612. PackageInfo pkg = sorted.get(s);
  613. if (pkg.isHidden()) {
  614. continue;
  615. }
  616. Boolean allHidden = true;
  617. int pass = 0;
  618. ClassInfo[] classesToCheck = null;
  619. while (pass < 5 ) {
  620. switch(pass) {
  621. case 0:
  622. classesToCheck = pkg.ordinaryClasses();
  623. break;
  624. case 1:
  625. classesToCheck = pkg.enums();
  626. break;
  627. case 2:
  628. classesToCheck = pkg.errors();
  629. break;
  630. case 3:
  631. classesToCheck = pkg.exceptions();
  632. break;
  633. case 4:
  634. classesToCheck = pkg.interfaces();
  635. break;
  636. default:
  637. System.err.println("Error reading package: " + pkg.name());
  638. break;
  639. }
  640. for (ClassInfo cl : classesToCheck) {
  641. if (!cl.isHidden()) {
  642. allHidden = false;
  643. break;
  644. }
  645. }
  646. if (!allHidden) {
  647. break;
  648. }
  649. pass++;
  650. }
  651. if (allHidden) {
  652. continue;
  653. }
  654. result.add(pkg);
  655. }
  656. sVisiblePackages = result.toArray(new PackageInfo[result.size()]);
  657. return sVisiblePackages;
  658. }
  659. public static void writePackages(String filename)
  660. {
  661. HDF data = makePackageHDF();
  662. int i = 0;
  663. for (PackageInfo pkg: choosePackages()) {
  664. writePackage(pkg);
  665. data.setValue("docs.packages." + i + ".name", pkg.name());
  666. data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
  667. TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
  668. pkg.firstSentenceTags());
  669. i++;
  670. }
  671. setPageTitle(data, "Package Index");
  672. TagInfo.makeHDF(data, "root.descr",
  673. Converter.convertTags(root.inlineTags(), null));
  674. ClearPage.write(data, "packages.cs", filename);
  675. ClearPage.write(data, "package-list.cs", javadocDir + "package-list");
  676. Proofread.writePackages(filename,
  677. Converter.convertTags(root.inlineTags(), null));
  678. }
  679. public static void writePackage(PackageInfo pkg)
  680. {
  681. // these this and the description are in the same directory,
  682. // so it's okay
  683. HDF data = makePackageHDF();
  684. String name = pkg.name();
  685. data.setValue("package.name", name);
  686. data.setValue("package.descr", "...description...");
  687. makeClassListHDF(data, "package.interfaces",
  688. ClassInfo.sortByName(pkg.interfaces()));
  689. makeClassListHDF(data, "package.classes",
  690. ClassInfo.sortByName(pkg.ordinaryClasses()));
  691. makeClassListHDF(data, "package.enums",
  692. ClassInfo.sortByName(pkg.enums()));
  693. makeClassListHDF(data, "package.exceptions",
  694. ClassInfo.sortByName(pkg.exceptions()));
  695. makeClassListHDF(data, "package.errors",
  696. ClassInfo.sortByName(pkg.errors()));
  697. TagInfo.makeHDF(data, "package.shortDescr",
  698. pkg.firstSentenceTags());
  699. TagInfo.makeHDF(data, "package.descr", pkg.inlineTags());
  700. String filename = pkg.htmlPage();
  701. setPageTitle(data, name);
  702. ClearPage.write(data, "package.cs", filename);
  703. filename = pkg.fullDescriptionHtmlPage();
  704. setPageTitle(data, name + " Details");
  705. ClearPage.write(data, "package-descr.cs", filename);
  706. Proofread.writePackage(filename, pkg.inlineTags());
  707. }
  708. public static void writeClassLists()
  709. {
  710. int i;
  711. HDF data = makePackageHDF();
  712. ClassInfo[] classes = PackageInfo.filterHidden(
  713. Converter.convertClasses(root.classes()));
  714. if (classes.length == 0) {
  715. return ;
  716. }
  717. Sorter[] sorted = new Sorter[classes.length];
  718. for (i=0; i<sorted.length; i++) {
  719. ClassInfo cl = classes[i];
  720. String name = cl.name();
  721. sorted[i] = new Sorter(name, cl);
  722. }
  723. Arrays.sort(sorted);
  724. // make a pass and resolve ones that have the same name
  725. int firstMatch = 0;
  726. String lastName = sorted[0].label;
  727. for (i=1; i<sorted.length; i++) {
  728. String s = sorted[i].label;
  729. if (!lastName.equals(s)) {
  730. if (firstMatch != i-1) {
  731. // there were duplicates
  732. for (int j=firstMatch; j<i; j++) {
  733. PackageInfo pkg = ((ClassInfo)sorted[j].data).containingPackage();
  734. if (pkg != null) {
  735. sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
  736. }
  737. }
  738. }
  739. firstMatch = i;
  740. lastName = s;
  741. }
  742. }
  743. // and sort again
  744. Arrays.sort(sorted);
  745. for (i=0; i<sorted.length; i++) {
  746. String s = sorted[i].label;
  747. ClassInfo cl = (ClassInfo)sorted[i].data;
  748. char first = Character.toUpperCase(s.charAt(0));
  749. cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i);
  750. }
  751. setPageTitle(data, "Class Index");
  752. ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension);
  753. }
  754. // we use the word keywords because "index" means something else in html land
  755. // the user only ever sees the word index
  756. /* public static void writeKeywords()
  757. {
  758. ArrayList<KeywordEntry> keywords = new ArrayList<KeywordEntry>();
  759. ClassInfo[] classes = PackageInfo.filterHidden(Converter.convertClasses(root.classes()));
  760. for (ClassInfo cl: classes) {
  761. cl.makeKeywordEntries(keywords);
  762. }
  763. HDF data = makeHDF();
  764. Collections.sort(keywords);
  765. int i=0;
  766. for (KeywordEntry entry: keywords) {
  767. String base = "keywords." + entry.firstChar() + "." + i;
  768. entry.makeHDF(data, base);
  769. i++;
  770. }
  771. setPageTitle(data, "Index");
  772. ClearPage.write(data, "keywords.cs", javadocDir + "keywords" + htmlExtension);
  773. } */
  774. public static void writeHierarchy()
  775. {
  776. ClassInfo[] classes = Converter.rootClasses();
  777. ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
  778. for (ClassInfo cl: classes) {
  779. if (!cl.isHidden()) {
  780. info.add(cl);
  781. }
  782. }
  783. HDF data = makePackageHDF();
  784. Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()]));
  785. setPageTitle(data, "Class Hierarchy");
  786. ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension);
  787. }
  788. public static void writeClasses()
  789. {
  790. ClassInfo[] classes = Converter.rootClasses();
  791. for (ClassInfo cl: classes) {
  792. HDF data = makePackageHDF();
  793. if (!cl.isHidden()) {
  794. writeClass(cl, data);
  795. }
  796. }
  797. }
  798. public static void writeClass(ClassInfo cl, HDF data)
  799. {
  800. cl.makeHDF(data);
  801. setPageTitle(data, cl.name());
  802. ClearPage.write(data, "class.cs", cl.htmlPage());
  803. Proofread.writeClass(cl.htmlPage(), cl);
  804. }
  805. public static void makeClassListHDF(HDF data, String base,
  806. ClassInfo[] classes)
  807. {
  808. for (int i=0; i<classes.length; i++) {
  809. ClassInfo cl = classes[i];
  810. if (!cl.isHidden()) {
  811. cl.makeShortDescrHDF(data, base + "." + i);
  812. }
  813. }
  814. }
  815. public static String linkTarget(String source, String target)
  816. {
  817. String[] src = source.split("/");
  818. String[] tgt = target.split("/");
  819. int srclen = src.length;
  820. int tgtlen = tgt.length;
  821. int same = 0;
  822. while (same < (srclen-1)
  823. && same < (tgtlen-1)
  824. && (src[same].equals(tgt[same]))) {
  825. same++;
  826. }
  827. String s = "";
  828. int up = srclen-same-1;
  829. for (int i=0; i<up; i++) {
  830. s += "../";
  831. }
  832. int N = tgtlen-1;
  833. for (int i=same; i<N; i++) {
  834. s += tgt[i] + '/';
  835. }
  836. s += tgt[tgtlen-1];
  837. return s;
  838. }
  839. /**
  840. * Returns true if the given element has an @hide annotation.
  841. */
  842. private static boolean hasHideAnnotation(Doc doc) {
  843. return doc.getRawCommentText().indexOf("@hide") != -1;
  844. }
  845. /**
  846. * Returns true if the given element is hidden.
  847. */
  848. private static boolean isHidden(Doc doc) {
  849. // Methods, fields, constructors.
  850. if (doc instanceof MemberDoc) {
  851. return hasHideAnnotation(doc);
  852. }
  853. // Classes, interfaces, enums, annotation types.
  854. if (doc instanceof ClassDoc) {
  855. ClassDoc classDoc = (ClassDoc) doc;
  856. // Check the containing package.
  857. if (hasHideAnnotation(classDoc.containingPackage())) {
  858. return true;
  859. }
  860. // Check the class doc and containing class docs if this is a
  861. // nested class.
  862. ClassDoc current = classDoc;
  863. do {
  864. if (hasHideAnnotation(current)) {
  865. return true;
  866. }
  867. current = current.containingClass();
  868. } while (current != null);
  869. }
  870. return false;
  871. }
  872. /**
  873. * Filters out hidden elements.
  874. */
  875. private static Object filterHidden(Object o, Class<?> expected) {
  876. if (o == null) {
  877. return null;
  878. }
  879. Class type = o.getClass();
  880. if (type.getName().startsWith("com.sun.")) {
  881. // TODO: Implement interfaces from superclasses, too.
  882. return Proxy.newProxyInstance(type.getClassLoader(),
  883. type.getInterfaces(), new HideHandler(o));
  884. } else if (o instanceof Object[]) {
  885. Class<?> componentType = expected.getComponentType();
  886. Object[] array = (Object[]) o;
  887. List<Object> list = new ArrayList<Object>(array.length);
  888. for (Object entry : array) {
  889. if ((entry instanceof Doc) && isHidden((Doc) entry)) {
  890. continue;
  891. }
  892. list.add(filterHidden(entry, componentType));
  893. }
  894. return list.toArray(
  895. (Object[]) Array.newInstance(componentType, list.size()));
  896. } else {
  897. return o;
  898. }
  899. }
  900. /**
  901. * Filters hidden elements out of method return values.
  902. */
  903. private static class HideHandler implements InvocationHandler {
  904. private final Object target;
  905. public HideHandler(Object target) {
  906. this.target = target;
  907. }
  908. public Object invoke(Object proxy, Method method, Object[] args)
  909. throws Throwable {
  910. String methodName = method.getName();
  911. if (args != null) {
  912. if (methodName.equals("compareTo") ||
  913. methodName.equals("equals") ||
  914. methodName.equals("overrides") ||
  915. methodName.equals("subclassOf")) {
  916. args[0] = unwrap(args[0]);
  917. }
  918. }
  919. if (methodName.equals("getRawCommentText")) {
  920. return filterComment((String) method.invoke(target, args));
  921. }
  922. // escape "&" in disjunctive types.
  923. if (proxy instanceof Type && methodName.equals("toString")) {
  924. return ((String) method.invoke(target, args))
  925. .replace("&", "&amp;");
  926. }
  927. try {
  928. return filterHidden(method.invoke(target, args),
  929. method.getReturnType());
  930. } catch (InvocationTargetException e) {
  931. throw e.getTargetException();
  932. }
  933. }
  934. private String filterComment(String s) {
  935. if (s == null) {
  936. return null;
  937. }
  938. s = s.trim();
  939. // Work around off by one error
  940. while (s.length() >= 5
  941. && s.charAt(s.length() - 5) == '{') {
  942. s += "&nbsp;";
  943. }
  944. return s;
  945. }
  946. private static Object unwrap(Object proxy) {
  947. if (proxy instanceof Proxy)
  948. return ((HideHandler)Proxy.getInvocationHandler(proxy)).target;
  949. return proxy;
  950. }
  951. }
  952. public static String scope(Scoped scoped) {
  953. if (scoped.isPublic()) {
  954. return "public";
  955. }
  956. else if (scoped.isProtected()) {
  957. return "protected";
  958. }
  959. else if (scoped.isPackagePrivate()) {
  960. return "";
  961. }
  962. else if (scoped.isPrivate()) {
  963. return "private";
  964. }
  965. else {
  966. throw new RuntimeException("invalid scope for object " + scoped);
  967. }
  968. }
  969. /**
  970. * Collect the values used by the Dev tools and write them in files packaged with the SDK
  971. * @param output the ouput directory for the files.
  972. */
  973. private static void writeSdkValues(String output) {
  974. ArrayList<String> activityActions = new ArrayList<String>();
  975. ArrayList<String> broadcastActions = new ArrayList<String>();
  976. ArrayList<String> serviceActions = new ArrayList<String>();
  977. ArrayList<String> categories = new ArrayList<String>();
  978. ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
  979. ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
  980. ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
  981. ClassInfo[] classes = Converter.allClasses();
  982. // Go through all the fields of all the classes, looking SDK stuff.
  983. for (ClassInfo clazz : classes) {
  984. // first check constant fields for the SdkConstant annotation.
  985. FieldInfo[] fields = clazz.allSelfFields();
  986. for (FieldInfo field : fields) {
  987. Object cValue = field.constantValue();
  988. if (cValue != null) {
  989. AnnotationInstanceInfo[] annotations = field.annotations();
  990. if (annotations.length > 0) {
  991. for (AnnotationInstanceInfo annotation : annotations) {
  992. if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) {
  993. AnnotationValueInfo[] values = annotation.elementValues();
  994. if (values.length > 0) {
  995. String type = values[0].valueString();
  996. if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) {
  997. activityActions.add(cValue.toString());
  998. } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) {
  999. broadcastActions.add(cValue.toString());
  1000. } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) {
  1001. serviceActions.add(cValue.toString());
  1002. } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) {
  1003. categories.add(cValue.toString());
  1004. }
  1005. }
  1006. break;
  1007. }
  1008. }
  1009. }
  1010. }
  1011. }
  1012. // Now check the class for @Widget or if its in the android.widget package
  1013. // (unless the class is hidden or abstract, or non public)
  1014. if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) {
  1015. boolean annotated = false;
  1016. AnnotationInstanceInfo[] annotations = clazz.annotations();
  1017. if (annotations.length > 0) {
  1018. for (AnnotationInstanceInfo annotation : annotations) {
  1019. if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) {
  1020. widgets.add(clazz);
  1021. annotated = true;
  1022. break;
  1023. } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) {
  1024. layouts.add(clazz);
  1025. annotated = true;
  1026. break;
  1027. }
  1028. }
  1029. }
  1030. if (annotated == false) {
  1031. // lets check if this is inside android.widget
  1032. PackageInfo pckg = clazz.containingPackage();
  1033. String packageName = pckg.name();
  1034. if ("android.widget".equals(packageName) ||
  1035. "android.view".equals(packageName)) {
  1036. // now we check what this class inherits either from android.view.ViewGroup
  1037. // or android.view.View, or android.view.ViewGroup.LayoutParams
  1038. int type = checkInheritance(clazz);
  1039. switch (type) {
  1040. case TYPE_WIDGET:
  1041. widgets.add(clazz);
  1042. break;
  1043. case TYPE_LAYOUT:
  1044. layouts.add(clazz);
  1045. break;
  1046. case TYPE_LAYOUT_PARAM:
  1047. layoutParams.add(clazz);
  1048. break;
  1049. }
  1050. }
  1051. }
  1052. }
  1053. }
  1054. // now write the files, whether or not the list are empty.
  1055. // the SDK built requires those files to be present.
  1056. Collections.sort(activityActions);
  1057. writeValues(output + "/activity_actions.txt", activityActions);
  1058. Collections.sort(broadcastActions);
  1059. writeValues(output + "/broadcast_actions.txt", broadcastActions);
  1060. Collections.sort(serviceActions);
  1061. writeValues(output + "/service_actions.txt", serviceActions);
  1062. Collections.sort(categories);
  1063. writeValues(output + "/categories.txt", categories);
  1064. // before writing the list of classes, we do some checks, to make sure the layout params
  1065. // are enclosed by a layout class (and not one that has been declared as a widget)
  1066. for (int i = 0 ; i < layoutParams.size();) {
  1067. ClassInfo layoutParamClass = layoutParams.get(i);
  1068. ClassInfo containingClass = layoutParamClass.containingClass();
  1069. if (containingClass == null || layouts.indexOf(containingClass) == -1) {
  1070. layoutParams.remove(i);
  1071. } else {
  1072. i++;
  1073. }
  1074. }
  1075. writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
  1076. }
  1077. /**
  1078. * Writes a list of values into a text files.
  1079. * @param pathname the absolute os path of the output file.
  1080. * @param values the list of values to write.
  1081. */
  1082. private static void writeValues(String pathname, ArrayList<String> values) {
  1083. FileWriter fw = null;
  1084. BufferedWriter bw = null;
  1085. try {
  1086. fw = new FileWriter(pathname, false);
  1087. bw = new BufferedWriter(fw);
  1088. for (String value : values) {
  1089. bw.append(value).append('\n');
  1090. }
  1091. } catch (IOException e) {
  1092. // pass for now
  1093. } finally {
  1094. try {
  1095. if (bw != null) bw.close();
  1096. } catch (IOException e) {
  1097. // pass for now
  1098. }
  1099. try {
  1100. if (fw != null) fw.close();
  1101. } catch (IOException e) {
  1102. // pass for now
  1103. }
  1104. }
  1105. }
  1106. /**
  1107. * Writes the widget/layout/layout param classes into a text files.
  1108. * @param pathname the absolute os path of the output file.
  1109. * @param widgets the list of widget classes to write.
  1110. * @param layouts the list of layout classes to write.
  1111. * @param layoutParams the list of layout param classes to write.
  1112. */
  1113. private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets,
  1114. ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) {
  1115. FileWriter fw = null;
  1116. BufferedWriter bw = null;
  1117. try {
  1118. fw = new FileWriter(pathname, false);
  1119. bw = new BufferedWriter(fw);
  1120. // write the 3 types of classes.
  1121. for (ClassInfo clazz : widgets) {
  1122. writeClass(bw, clazz, 'W');
  1123. }
  1124. for (ClassInfo clazz : layoutParams) {
  1125. writeClass(bw, clazz, 'P');
  1126. }
  1127. for (ClassInfo clazz : layouts) {
  1128. writeClass(bw, clazz, 'L');
  1129. }
  1130. } catch (IOException e) {
  1131. // pass for now
  1132. } finally {
  1133. try {
  1134. if (bw != null) bw.close();
  1135. } catch (IOException e) {
  1136. // pass for now
  1137. }
  1138. try {
  1139. if (fw != null) fw.close();
  1140. } catch (IOException e) {
  1141. // pass for now
  1142. }
  1143. }
  1144. }
  1145. /**
  1146. * Writes a class name and its super class names into a {@link BufferedWriter}.
  1147. * @param writer the BufferedWriter to write into
  1148. * @param clazz the class to write
  1149. * @param prefix the prefix to put at the beginning of the line.
  1150. * @throws IOException
  1151. */
  1152. private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)
  1153. throws IOException {
  1154. writer.append(prefix).append(clazz.qualifiedName());
  1155. ClassInfo superClass = clazz;
  1156. while ((superClass = superClass.superclass()) != null) {
  1157. writer.append(' ').append(superClass.qualifiedName());
  1158. }
  1159. writer.append('\n');
  1160. }
  1161. /**
  1162. * Checks the inheritance of {@link ClassInfo} objects. This method return
  1163. * <ul>
  1164. * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li>
  1165. * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
  1166. * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends <code>android.view.ViewGroup$LayoutParams</code></li>
  1167. * <li>{@link #TYPE_NONE}: in all other cases</li>
  1168. * </ul>
  1169. * @param clazz the {@link ClassInfo} to check.
  1170. */
  1171. private static int checkInheritance(ClassInfo clazz) {
  1172. if ("android.view.ViewGroup".equals(clazz.qualifiedName())) {
  1173. return TYPE_LAYOUT;
  1174. } else if ("android.view.View".equals(clazz.qualifiedName())) {
  1175. return TYPE_WIDGET;
  1176. } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
  1177. return TYPE_LAYOUT_PARAM;
  1178. }
  1179. ClassInfo parent = clazz.superclass();
  1180. if (parent != null) {
  1181. return checkInheritance(parent);
  1182. }
  1183. return TYPE_NONE;
  1184. }
  1185. }