blob: e2f7d88977f32ed7b26c5ce9c9867898dfc8ff77 [file] [log] [blame]
Ben Dodson920dbbb2010-08-04 15:21:06 -07001/*
2 * Copyright (C) 2010 Google Inc.
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
17package com.google.doclava;
18
19import com.google.clearsilver.jsilver.JSilver;
20import com.google.clearsilver.jsilver.data.Data;
21import com.google.clearsilver.jsilver.resourceloader.ClassResourceLoader;
22import com.google.clearsilver.jsilver.resourceloader.CompositeResourceLoader;
23import com.google.clearsilver.jsilver.resourceloader.FileSystemResourceLoader;
24import com.google.clearsilver.jsilver.resourceloader.ResourceLoader;
25
26import com.sun.javadoc.*;
27
28import java.util.*;
29import java.util.jar.JarFile;
Scott Main71bf8d22013-03-08 18:59:15 -080030import java.util.regex.Matcher;
Ben Dodson920dbbb2010-08-04 15:21:06 -070031import java.io.*;
32import java.lang.reflect.Proxy;
33import java.lang.reflect.Array;
34import java.lang.reflect.InvocationHandler;
35import java.lang.reflect.InvocationTargetException;
36import java.lang.reflect.Method;
37import java.net.MalformedURLException;
38import java.net.URL;
39
40public class Doclava {
41 private static final String SDK_CONSTANT_ANNOTATION = "android.annotation.SdkConstant";
42 private static final String SDK_CONSTANT_TYPE_ACTIVITY_ACTION =
43 "android.annotation.SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION";
44 private static final String SDK_CONSTANT_TYPE_BROADCAST_ACTION =
45 "android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION";
46 private static final String SDK_CONSTANT_TYPE_SERVICE_ACTION =
47 "android.annotation.SdkConstant.SdkConstantType.SERVICE_INTENT_ACTION";
48 private static final String SDK_CONSTANT_TYPE_CATEGORY =
49 "android.annotation.SdkConstant.SdkConstantType.INTENT_CATEGORY";
50 private static final String SDK_CONSTANT_TYPE_FEATURE =
51 "android.annotation.SdkConstant.SdkConstantType.FEATURE";
52 private static final String SDK_WIDGET_ANNOTATION = "android.annotation.Widget";
53 private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout";
54
55 private static final int TYPE_NONE = 0;
56 private static final int TYPE_WIDGET = 1;
57 private static final int TYPE_LAYOUT = 2;
58 private static final int TYPE_LAYOUT_PARAM = 3;
59
60 public static final int SHOW_PUBLIC = 0x00000001;
61 public static final int SHOW_PROTECTED = 0x00000003;
62 public static final int SHOW_PACKAGE = 0x00000007;
63 public static final int SHOW_PRIVATE = 0x0000000f;
64 public static final int SHOW_HIDDEN = 0x0000001f;
65
66 public static int showLevel = SHOW_PROTECTED;
67
Dirk Dougherty815f9342013-09-12 00:30:28 -070068 public static final boolean SORT_BY_NAV_GROUPS = true;
Dirk Doughertyc770a6e2014-02-07 20:04:27 -080069 /* Debug output for PageMetadata, format urls from site root */
70 public static boolean META_DBG=false;
Dirk Dougherty815f9342013-09-12 00:30:28 -070071
Dirk Dougherty9b316c82013-01-28 10:53:33 -080072 public static String outputPathBase = "/";
73 public static ArrayList<String> inputPathHtmlDirs = new ArrayList<String>();
74 public static ArrayList<String> inputPathHtmlDir2 = new ArrayList<String>();
75 public static String outputPathHtmlDirs;
76 public static String outputPathHtmlDir2;
Dirk Dougherty9b316c82013-01-28 10:53:33 -080077 public static final String devsiteRoot = "en/";
78 public static String javadocDir = "reference/";
Ben Dodson920dbbb2010-08-04 15:21:06 -070079 public static String htmlExtension;
80
81 public static RootDoc root;
82 public static ArrayList<String[]> mHDFData = new ArrayList<String[]>();
Dirk Dougherty42303cc2013-09-13 19:45:17 -070083 public static List<PageMetadata.Node> sTaglist = new ArrayList<PageMetadata.Node>();
Dirk Dougherty5ce827a2013-11-21 18:08:58 -080084 public static ArrayList<SampleCode> sampleCodes = new ArrayList<SampleCode>();
Dirk Dougherty815f9342013-09-12 00:30:28 -070085 public static ArrayList<SampleCode> sampleCodeGroups = new ArrayList<SampleCode>();
Dirk Dougherty75372292013-11-26 18:45:31 -080086 public static Data samplesNavTree;
Ben Dodson920dbbb2010-08-04 15:21:06 -070087 public static Map<Character, String> escapeChars = new HashMap<Character, String>();
88 public static String title = "";
89 public static SinceTagger sinceTagger = new SinceTagger();
Joe Onorato7a6456c2010-09-16 12:36:36 -040090 public static HashSet<String> knownTags = new HashSet<String>();
Ben Dodson920dbbb2010-08-04 15:21:06 -070091 public static FederationTagger federationTagger = new FederationTagger();
Dirk Dougherty9b316c82013-01-28 10:53:33 -080092 public static Set<String> showAnnotations = new HashSet<String>();
Alex Klyubincbc60de2014-06-03 16:53:17 -070093 public static Set<String> hiddenPackages = new HashSet<String>();
Dirk Dougherty9b316c82013-01-28 10:53:33 -080094 public static boolean includeDefaultAssets = true;
Ben Dodson9ccd9e32010-08-06 17:18:52 -070095 private static boolean generateDocs = true;
96 private static boolean parseComments = false;
Chirag Shah18b25552012-08-09 13:57:54 -070097 private static String yamlNavFile = null;
Jeff Arnesonb2a6c042015-03-27 14:54:47 -070098 public static boolean documentAnnotations = false;
99 public static String documentAnnotationsPath = null;
100 public static Map<String, String> annotationDocumentationMap = null;
Scott Main3c1a6b22010-10-15 17:34:04 -0700101
Ben Dodson920dbbb2010-08-04 15:21:06 -0700102 public static JSilver jSilver = null;
103
Robert Ly5ab9cb52012-11-30 02:57:14 -0800104 private static boolean gmsRef = false;
105 private static boolean gcmRef = false;
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700106 private static boolean samplesRef = false;
Robert Ly88c435b2013-02-01 11:55:04 -0800107 private static boolean sac = false;
Robert Ly5ab9cb52012-11-30 02:57:14 -0800108
Ben Dodson920dbbb2010-08-04 15:21:06 -0700109 public static boolean checkLevel(int level) {
110 return (showLevel & level) == level;
111 }
Scott Main3c1a6b22010-10-15 17:34:04 -0700112
Ben Dodson920dbbb2010-08-04 15:21:06 -0700113 /**
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700114 * Returns true if we should parse javadoc comments,
115 * reporting errors in the process.
Ben Dodson920dbbb2010-08-04 15:21:06 -0700116 */
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700117 public static boolean parseComments() {
118 return generateDocs || parseComments;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700119 }
120
121 public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp, boolean priv,
122 boolean hidden) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700123 if (hidden && !checkLevel(SHOW_HIDDEN)) {
124 return false;
125 }
126 if (pub && checkLevel(SHOW_PUBLIC)) {
127 return true;
128 }
129 if (prot && checkLevel(SHOW_PROTECTED)) {
130 return true;
131 }
132 if (pkgp && checkLevel(SHOW_PACKAGE)) {
133 return true;
134 }
135 if (priv && checkLevel(SHOW_PRIVATE)) {
136 return true;
137 }
138 return false;
139 }
140
141 public static void main(String[] args) {
142 com.sun.tools.javadoc.Main.execute(args);
143 }
144
145 public static boolean start(RootDoc r) {
Andrew Sappersteinf959ed12011-06-23 10:24:40 -0700146 long startTime = System.nanoTime();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700147 String keepListFile = null;
Jeff Hamilton970f13f2012-06-22 00:21:56 -0500148 String proguardFile = null;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700149 String proofreadFile = null;
150 String todoFile = null;
151 String sdkValuePath = null;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700152 String stubsDir = null;
Dirk Dougherty41d86562010-08-20 15:21:11 -0700153 // Create the dependency graph for the stubs directory
Ben Dodson920dbbb2010-08-04 15:21:06 -0700154 boolean offlineMode = false;
155 String apiFile = null;
Hui Shu5118ffe2014-02-18 14:06:42 -0800156 String removedApiFile = null;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700157 String debugStubsFile = "";
158 HashSet<String> stubPackages = null;
Joe Onorato7a6456c2010-09-16 12:36:36 -0400159 ArrayList<String> knownTagsFiles = new ArrayList<String>();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700160
161 root = r;
162
163 String[][] options = r.options();
164 for (String[] a : options) {
165 if (a[0].equals("-d")) {
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800166 outputPathBase = outputPathHtmlDirs = ClearPage.outputDir = a[1];
Ben Dodson920dbbb2010-08-04 15:21:06 -0700167 } else if (a[0].equals("-templatedir")) {
168 ClearPage.addTemplateDir(a[1]);
169 } else if (a[0].equals("-hdf")) {
170 mHDFData.add(new String[] {a[1], a[2]});
Joe Onorato7a6456c2010-09-16 12:36:36 -0400171 } else if (a[0].equals("-knowntags")) {
172 knownTagsFiles.add(a[1]);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700173 } else if (a[0].equals("-toroot")) {
174 ClearPage.toroot = a[1];
175 } else if (a[0].equals("-samplecode")) {
176 sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
Dirk Dougherty815f9342013-09-12 00:30:28 -0700177 } else if (a[0].equals("-samplegroup")) {
178 sampleCodeGroups.add(new SampleCode(null, null, a[1]));
Dirk Dougherty5ce827a2013-11-21 18:08:58 -0800179 } else if (a[0].equals("-samplesdir")) {
180 getSampleProjects(new File(a[1]));
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800181 //the destination output path for main htmldir
Ben Dodson920dbbb2010-08-04 15:21:06 -0700182 } else if (a[0].equals("-htmldir")) {
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800183 inputPathHtmlDirs.add(a[1]);
184 ClearPage.htmlDirs = inputPathHtmlDirs;
185 //the destination output path for additional htmldir
186 } else if (a[0].equals("-htmldir2")) {
187 if (a[2].equals("default")) {
188 inputPathHtmlDirs.add(a[1]);
189 } else {
190 inputPathHtmlDir2.add(a[1]);
191 outputPathHtmlDir2 = a[2];
192 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700193 } else if (a[0].equals("-title")) {
194 Doclava.title = a[1];
195 } else if (a[0].equals("-werror")) {
196 Errors.setWarningsAreErrors(true);
197 } else if (a[0].equals("-error") || a[0].equals("-warning") || a[0].equals("-hide")) {
198 try {
199 int level = -1;
200 if (a[0].equals("-error")) {
201 level = Errors.ERROR;
202 } else if (a[0].equals("-warning")) {
203 level = Errors.WARNING;
204 } else if (a[0].equals("-hide")) {
205 level = Errors.HIDDEN;
206 }
207 Errors.setErrorLevel(Integer.parseInt(a[1]), level);
208 } catch (NumberFormatException e) {
209 // already printed below
210 return false;
211 }
212 } else if (a[0].equals("-keeplist")) {
213 keepListFile = a[1];
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800214 } else if (a[0].equals("-showAnnotation")) {
215 showAnnotations.add(a[1]);
Alex Klyubincbc60de2014-06-03 16:53:17 -0700216 } else if (a[0].equals("-hidePackage")) {
217 hiddenPackages.add(a[1]);
Jeff Hamilton970f13f2012-06-22 00:21:56 -0500218 } else if (a[0].equals("-proguard")) {
Robert Ly5ab9cb52012-11-30 02:57:14 -0800219 proguardFile = a[1];
Ben Dodson920dbbb2010-08-04 15:21:06 -0700220 } else if (a[0].equals("-proofread")) {
221 proofreadFile = a[1];
222 } else if (a[0].equals("-todo")) {
223 todoFile = a[1];
224 } else if (a[0].equals("-public")) {
225 showLevel = SHOW_PUBLIC;
226 } else if (a[0].equals("-protected")) {
227 showLevel = SHOW_PROTECTED;
228 } else if (a[0].equals("-package")) {
229 showLevel = SHOW_PACKAGE;
230 } else if (a[0].equals("-private")) {
231 showLevel = SHOW_PRIVATE;
232 } else if (a[0].equals("-hidden")) {
233 showLevel = SHOW_HIDDEN;
234 } else if (a[0].equals("-stubs")) {
235 stubsDir = a[1];
236 } else if (a[0].equals("-stubpackages")) {
237 stubPackages = new HashSet<String>();
238 for (String pkg : a[1].split(":")) {
239 stubPackages.add(pkg);
240 }
241 } else if (a[0].equals("-sdkvalues")) {
242 sdkValuePath = a[1];
Joe Onorato04099252011-03-09 13:34:18 -0800243 } else if (a[0].equals("-api")) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700244 apiFile = a[1];
Hui Shu5118ffe2014-02-18 14:06:42 -0800245 } else if (a[0].equals("-removedApi")) {
246 removedApiFile = a[1];
247 }
248 else if (a[0].equals("-nodocs")) {
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700249 generateDocs = false;
Dirk Doughertye65daec2013-04-29 18:44:48 -0700250 } else if (a[0].equals("-nodefaultassets")) {
251 includeDefaultAssets = false;
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700252 } else if (a[0].equals("-parsecomments")) {
253 parseComments = true;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700254 } else if (a[0].equals("-since")) {
255 sinceTagger.addVersion(a[1], a[2]);
256 } else if (a[0].equals("-offlinemode")) {
257 offlineMode = true;
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800258 } else if (a[0].equals("-metadataDebug")) {
259 META_DBG = true;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700260 } else if (a[0].equals("-federate")) {
261 try {
262 String name = a[1];
263 URL federationURL = new URL(a[2]);
Jeff Hamilton1e0d3702012-06-21 04:21:54 -0500264 federationTagger.addSiteUrl(name, federationURL);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700265 } catch (MalformedURLException e) {
266 System.err.println("Could not parse URL for federation: " + a[1]);
267 return false;
268 }
Jeff Hamilton1e0d3702012-06-21 04:21:54 -0500269 } else if (a[0].equals("-federationapi")) {
270 String name = a[1];
271 String file = a[2];
272 federationTagger.addSiteApi(name, file);
Chirag Shah18b25552012-08-09 13:57:54 -0700273 } else if (a[0].equals("-yaml")) {
274 yamlNavFile = a[1];
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800275 } else if (a[0].equals("-devsite")) {
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800276 // Don't copy the doclava assets to devsite output (ie use proj assets only)
277 includeDefaultAssets = false;
278 outputPathHtmlDirs = outputPathHtmlDirs + "/" + devsiteRoot;
Jeff Arnesonb2a6c042015-03-27 14:54:47 -0700279 } else if (a[0].equals("-documentannotations")) {
280 documentAnnotations = true;
281 documentAnnotationsPath = a[1];
Ben Dodson920dbbb2010-08-04 15:21:06 -0700282 }
283 }
284
Joe Onorato7a6456c2010-09-16 12:36:36 -0400285 if (!readKnownTagsFiles(knownTags, knownTagsFiles)) {
286 return false;
287 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700288
289 // Set up the data structures
290 Converter.makeInfo(r);
291
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700292 if (generateDocs) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700293 ClearPage.addBundledTemplateDir("assets/customizations");
294 ClearPage.addBundledTemplateDir("assets/templates");
295
296 List<ResourceLoader> resourceLoaders = new ArrayList<ResourceLoader>();
297 List<String> templates = ClearPage.getTemplateDirs();
298 for (String tmpl : templates) {
299 resourceLoaders.add(new FileSystemResourceLoader(tmpl));
300 }
301
302 templates = ClearPage.getBundledTemplateDirs();
303 for (String tmpl : templates) {
Andrew Sappersteinf959ed12011-06-23 10:24:40 -0700304 // TODO - remove commented line - it's here for debugging purposes
305 // resourceLoaders.add(new FileSystemResourceLoader("/Volumes/Android/master/external/doclava/res/" + tmpl));
Ben Dodson920dbbb2010-08-04 15:21:06 -0700306 resourceLoaders.add(new ClassResourceLoader(Doclava.class, '/'+tmpl));
307 }
308
309 ResourceLoader compositeResourceLoader = new CompositeResourceLoader(resourceLoaders);
310 jSilver = new JSilver(compositeResourceLoader);
311
312 if (!Doclava.readTemplateSettings()) {
313 return false;
314 }
315
Andrew Sappersteinf959ed12011-06-23 10:24:40 -0700316 //startTime = System.nanoTime();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700317
318 // Apply @since tags from the XML file
319 sinceTagger.tagAll(Converter.rootClasses());
Scott Main3c1a6b22010-10-15 17:34:04 -0700320
Ben Dodson920dbbb2010-08-04 15:21:06 -0700321 // Apply details of federated documentation
322 federationTagger.tagAll(Converter.rootClasses());
323
324 // Files for proofreading
325 if (proofreadFile != null) {
326 Proofread.initProofread(proofreadFile);
327 }
328 if (todoFile != null) {
329 TodoFile.writeTodoFile(todoFile);
330 }
331
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800332 if (samplesRef) {
Dirk Dougherty75372292013-11-26 18:45:31 -0800333 // always write samples without offlineMode behaviors
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800334 writeSamples(false, sampleCodes, SORT_BY_NAV_GROUPS);
335 }
Dirk Dougherty75372292013-11-26 18:45:31 -0800336
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800337 // HTML2 Pages -- Generate Pages from optional secondary dir
338 if (!inputPathHtmlDir2.isEmpty()) {
339 if (!outputPathHtmlDir2.isEmpty()) {
340 ClearPage.outputDir = outputPathBase + "/" + outputPathHtmlDir2;
341 }
342 ClearPage.htmlDirs = inputPathHtmlDir2;
343 writeHTMLPages();
344 ClearPage.htmlDirs = inputPathHtmlDirs;
345 }
346
Ben Dodson920dbbb2010-08-04 15:21:06 -0700347 // HTML Pages
Bill Napier4bac50a2010-08-25 18:17:16 -0700348 if (!ClearPage.htmlDirs.isEmpty()) {
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800349 ClearPage.htmlDirs = inputPathHtmlDirs;
350 ClearPage.outputDir = outputPathHtmlDirs;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700351 writeHTMLPages();
352 }
353
354 writeAssets();
Hui Shu5118ffe2014-02-18 14:06:42 -0800355
Ben Dodson920dbbb2010-08-04 15:21:06 -0700356 // Navigation tree
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700357 String refPrefix = new String();
Robert Ly5ab9cb52012-11-30 02:57:14 -0800358 if(gmsRef){
359 refPrefix = "gms-";
360 } else if(gcmRef){
361 refPrefix = "gcm-";
362 }
363 NavTree.writeNavTree(javadocDir, refPrefix);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700364
Chirag Shah18b25552012-08-09 13:57:54 -0700365 // Write yaml tree.
366 if (yamlNavFile != null){
367 NavTree.writeYamlTree(javadocDir, yamlNavFile);
368 }
369
Ben Dodson920dbbb2010-08-04 15:21:06 -0700370 // Packages Pages
Robert Ly5ab9cb52012-11-30 02:57:14 -0800371 writePackages(javadocDir + refPrefix + "packages" + htmlExtension);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700372
373 // Classes
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800374 writeClassLists();
375 writeClasses();
376 writeHierarchy();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700377 // writeKeywords();
378
379 // Lists for JavaScript
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800380 writeLists();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700381 if (keepListFile != null) {
382 writeKeepList(keepListFile);
383 }
384
Ben Dodson920dbbb2010-08-04 15:21:06 -0700385 // Index page
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800386 writeIndex();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700387
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800388 Proofread.finishProofread(proofreadFile);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700389
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800390 if (sdkValuePath != null) {
391 writeSdkValues(sdkValuePath);
392 }
Dirk Dougherty42303cc2013-09-13 19:45:17 -0700393 // Write metadata for all processed files to jd_lists_unified.js in out dir
394 if (!sTaglist.isEmpty()) {
395 PageMetadata.WriteList(sTaglist);
396 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700397 }
398
399 // Stubs
Hui Shu5118ffe2014-02-18 14:06:42 -0800400 if (stubsDir != null || apiFile != null || proguardFile != null || removedApiFile != null) {
401 Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, removedApiFile, stubPackages);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700402 }
403
404 Errors.printErrors();
Scott Main3c1a6b22010-10-15 17:34:04 -0700405
Andrew Sappersteinf959ed12011-06-23 10:24:40 -0700406 long time = System.nanoTime() - startTime;
407 System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to "
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800408 + outputPathBase );
Andrew Sappersteinf959ed12011-06-23 10:24:40 -0700409
Ben Dodson920dbbb2010-08-04 15:21:06 -0700410 return !Errors.hadError;
411 }
412
413 private static void writeIndex() {
414 Data data = makeHDF();
415 ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension);
416 }
417
418 private static boolean readTemplateSettings() {
419 Data data = makeHDF();
420
421 // The .html extension is hard-coded in several .cs files,
422 // and so you cannot currently set it as a property.
423 htmlExtension = ".html";
424 // htmlExtension = data.getValue("template.extension", ".html");
425 int i = 0;
426 while (true) {
427 String k = data.getValue("template.escape." + i + ".key", "");
428 String v = data.getValue("template.escape." + i + ".value", "");
429 if ("".equals(k)) {
430 break;
431 }
432 if (k.length() != 1) {
433 System.err.println("template.escape." + i + ".key must have a length of 1: " + k);
434 return false;
435 }
436 escapeChars.put(k.charAt(0), v);
437 i++;
438 }
439 return true;
440 }
441
Joe Onorato7a6456c2010-09-16 12:36:36 -0400442 private static boolean readKnownTagsFiles(HashSet<String> knownTags,
443 ArrayList<String> knownTagsFiles) {
444 for (String fn: knownTagsFiles) {
445 BufferedReader in = null;
446 try {
447 in = new BufferedReader(new FileReader(fn));
448 int lineno = 0;
449 boolean fail = false;
450 while (true) {
451 lineno++;
452 String line = in.readLine();
453 if (line == null) {
454 break;
455 }
456 line = line.trim();
457 if (line.length() == 0) {
458 continue;
459 } else if (line.charAt(0) == '#') {
460 continue;
461 }
462 String[] words = line.split("\\s+", 2);
463 if (words.length == 2) {
464 if (words[1].charAt(0) != '#') {
465 System.err.println(fn + ":" + lineno
466 + ": Only one tag allowed per line: " + line);
467 fail = true;
468 continue;
469 }
470 }
471 knownTags.add(words[0]);
472 }
473 if (fail) {
474 return false;
475 }
476 } catch (IOException ex) {
477 System.err.println("Error reading file: " + fn + " (" + ex.getMessage() + ")");
478 return false;
479 } finally {
480 if (in != null) {
481 try {
482 in.close();
483 } catch (IOException e) {
484 }
485 }
486 }
487 }
488 return true;
489 }
490
Ben Dodson920dbbb2010-08-04 15:21:06 -0700491 public static String escape(String s) {
492 if (escapeChars.size() == 0) {
493 return s;
494 }
495 StringBuffer b = null;
496 int begin = 0;
497 final int N = s.length();
498 for (int i = 0; i < N; i++) {
499 char c = s.charAt(i);
500 String mapped = escapeChars.get(c);
501 if (mapped != null) {
502 if (b == null) {
503 b = new StringBuffer(s.length() + mapped.length());
504 }
505 if (begin != i) {
506 b.append(s.substring(begin, i));
507 }
508 b.append(mapped);
509 begin = i + 1;
510 }
511 }
512 if (b != null) {
513 if (begin != N) {
514 b.append(s.substring(begin, N));
515 }
516 return b.toString();
517 }
518 return s;
519 }
520
521 public static void setPageTitle(Data data, String title) {
522 String s = title;
523 if (Doclava.title.length() > 0) {
524 s += " - " + Doclava.title;
525 }
526 data.setValue("page.title", s);
527 }
528
529
530 public static LanguageVersion languageVersion() {
531 return LanguageVersion.JAVA_1_5;
532 }
533
534
535 public static int optionLength(String option) {
536 if (option.equals("-d")) {
537 return 2;
538 }
539 if (option.equals("-templatedir")) {
540 return 2;
541 }
542 if (option.equals("-hdf")) {
543 return 3;
544 }
Joe Onorato7a6456c2010-09-16 12:36:36 -0400545 if (option.equals("-knowntags")) {
546 return 2;
547 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700548 if (option.equals("-toroot")) {
549 return 2;
550 }
551 if (option.equals("-samplecode")) {
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700552 samplesRef = true;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700553 return 4;
554 }
Dirk Dougherty815f9342013-09-12 00:30:28 -0700555 if (option.equals("-samplegroup")) {
556 return 2;
557 }
Dirk Dougherty5ce827a2013-11-21 18:08:58 -0800558 if (option.equals("-samplesdir")) {
559 samplesRef = true;
560 return 2;
561 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700562 if (option.equals("-devsite")) {
563 return 1;
564 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700565 if (option.equals("-htmldir")) {
566 return 2;
567 }
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800568 if (option.equals("-htmldir2")) {
569 return 3;
570 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700571 if (option.equals("-title")) {
572 return 2;
573 }
574 if (option.equals("-werror")) {
575 return 1;
576 }
577 if (option.equals("-hide")) {
578 return 2;
579 }
580 if (option.equals("-warning")) {
581 return 2;
582 }
583 if (option.equals("-error")) {
584 return 2;
585 }
586 if (option.equals("-keeplist")) {
587 return 2;
588 }
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800589 if (option.equals("-showAnnotation")) {
590 return 2;
591 }
Alex Klyubincbc60de2014-06-03 16:53:17 -0700592 if (option.equals("-hidePackage")) {
593 return 2;
594 }
Jeff Hamilton970f13f2012-06-22 00:21:56 -0500595 if (option.equals("-proguard")) {
596 return 2;
597 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700598 if (option.equals("-proofread")) {
599 return 2;
600 }
601 if (option.equals("-todo")) {
602 return 2;
603 }
604 if (option.equals("-public")) {
605 return 1;
606 }
607 if (option.equals("-protected")) {
608 return 1;
609 }
610 if (option.equals("-package")) {
611 return 1;
612 }
613 if (option.equals("-private")) {
614 return 1;
615 }
616 if (option.equals("-hidden")) {
617 return 1;
618 }
619 if (option.equals("-stubs")) {
620 return 2;
621 }
622 if (option.equals("-stubpackages")) {
623 return 2;
624 }
625 if (option.equals("-sdkvalues")) {
626 return 2;
627 }
Joe Onorato04099252011-03-09 13:34:18 -0800628 if (option.equals("-api")) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700629 return 2;
630 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800631 if (option.equals("-removedApi")) {
632 return 2;
633 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700634 if (option.equals("-nodocs")) {
635 return 1;
636 }
Dirk Doughertye65daec2013-04-29 18:44:48 -0700637 if (option.equals("-nodefaultassets")) {
638 return 1;
639 }
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700640 if (option.equals("-parsecomments")) {
641 return 1;
642 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700643 if (option.equals("-since")) {
644 return 3;
645 }
646 if (option.equals("-offlinemode")) {
647 return 1;
648 }
649 if (option.equals("-federate")) {
650 return 3;
651 }
Jeff Hamilton1e0d3702012-06-21 04:21:54 -0500652 if (option.equals("-federationapi")) {
653 return 3;
654 }
Chirag Shah18b25552012-08-09 13:57:54 -0700655 if (option.equals("-yaml")) {
656 return 2;
657 }
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800658 if (option.equals("-devsite")) {
659 return 1;
660 }
Robert Ly5ab9cb52012-11-30 02:57:14 -0800661 if (option.equals("-gmsref")) {
662 gmsRef = true;
663 return 1;
664 }
665 if (option.equals("-gcmref")) {
666 gcmRef = true;
667 return 1;
668 }
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800669 if (option.equals("-metadataDebug")) {
670 return 1;
671 }
Jeff Arnesonb2a6c042015-03-27 14:54:47 -0700672 if (option.equals("-documentannotations")) {
673 return 2;
674 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700675 return 0;
676 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700677 public static boolean validOptions(String[][] options, DocErrorReporter r) {
678 for (String[] a : options) {
679 if (a[0].equals("-error") || a[0].equals("-warning") || a[0].equals("-hide")) {
680 try {
681 Integer.parseInt(a[1]);
682 } catch (NumberFormatException e) {
683 r.printError("bad -" + a[0] + " value must be a number: " + a[1]);
684 return false;
685 }
686 }
687 }
688
689 return true;
690 }
691
692 public static Data makeHDF() {
693 Data data = jSilver.createData();
694
695 for (String[] p : mHDFData) {
696 data.setValue(p[0], p[1]);
697 }
698
699 return data;
700 }
701
702
703
704 public static Data makePackageHDF() {
705 Data data = makeHDF();
706 ClassInfo[] classes = Converter.rootClasses();
707
708 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
709 for (ClassInfo cl : classes) {
710 PackageInfo pkg = cl.containingPackage();
711 String name;
712 if (pkg == null) {
713 name = "";
714 } else {
715 name = pkg.name();
716 }
717 sorted.put(name, pkg);
718 }
719
720 int i = 0;
C. Sean Youngf8e1e392015-06-12 12:13:59 -0500721 for (Map.Entry<String, PackageInfo> entry : sorted.entrySet()) {
722 String s = entry.getKey();
723 PackageInfo pkg = entry.getValue();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700724
Hui Shu5118ffe2014-02-18 14:06:42 -0800725 if (pkg.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700726 continue;
727 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800728 boolean allHiddenOrRemoved = true;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700729 int pass = 0;
730 ClassInfo[] classesToCheck = null;
Brett Chabot700b9f22013-10-04 16:57:39 -0700731 while (pass < 6) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700732 switch (pass) {
733 case 0:
734 classesToCheck = pkg.ordinaryClasses();
735 break;
736 case 1:
737 classesToCheck = pkg.enums();
738 break;
739 case 2:
740 classesToCheck = pkg.errors();
741 break;
742 case 3:
743 classesToCheck = pkg.exceptions();
744 break;
745 case 4:
746 classesToCheck = pkg.interfaces();
747 break;
Brett Chabot700b9f22013-10-04 16:57:39 -0700748 case 5:
749 classesToCheck = pkg.annotations();
750 break;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700751 default:
752 System.err.println("Error reading package: " + pkg.name());
753 break;
754 }
755 for (ClassInfo cl : classesToCheck) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800756 if (!cl.isHiddenOrRemoved()) {
757 allHiddenOrRemoved = false;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700758 break;
759 }
760 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800761 if (!allHiddenOrRemoved) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700762 break;
763 }
764 pass++;
765 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800766 if (allHiddenOrRemoved) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700767 continue;
768 }
Robert Ly5ab9cb52012-11-30 02:57:14 -0800769 if(gmsRef){
770 data.setValue("reference.gms", "true");
771 } else if(gcmRef){
772 data.setValue("reference.gcm", "true");
773 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700774 data.setValue("reference", "1");
775 data.setValue("reference.apilevels", sinceTagger.hasVersions() ? "1" : "0");
776 data.setValue("docs.packages." + i + ".name", s);
777 data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
778 data.setValue("docs.packages." + i + ".since", pkg.getSince());
779 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr", pkg.firstSentenceTags());
780 i++;
781 }
782
783 sinceTagger.writeVersionNames(data);
784 return data;
785 }
786
787 private static void writeDirectory(File dir, String relative, JSilver js) {
788 File[] files = dir.listFiles();
789 int i, count = files.length;
790 for (i = 0; i < count; i++) {
791 File f = files[i];
792 if (f.isFile()) {
793 String templ = relative + f.getName();
794 int len = templ.length();
795 if (len > 3 && ".cs".equals(templ.substring(len - 3))) {
796 Data data = makeHDF();
797 String filename = templ.substring(0, len - 3) + htmlExtension;
798 ClearPage.write(data, templ, filename, js);
799 } else if (len > 3 && ".jd".equals(templ.substring(len - 3))) {
Dirk Dougherty42303cc2013-09-13 19:45:17 -0700800 Data data = makeHDF();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700801 String filename = templ.substring(0, len - 3) + htmlExtension;
Dirk Dougherty42303cc2013-09-13 19:45:17 -0700802 DocFile.writePage(f.getAbsolutePath(), relative, filename, data);
Robert Ly88c435b2013-02-01 11:55:04 -0800803 } else if(!f.getName().equals(".DS_Store")){
804 Data data = makeHDF();
805 String hdfValue = data.getValue("sac") == null ? "" : data.getValue("sac");
806 boolean allowExcepted = hdfValue.equals("true") ? true : false;
807 ClearPage.copyFile(allowExcepted, f, templ);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700808 }
809 } else if (f.isDirectory()) {
810 writeDirectory(f, relative + f.getName() + "/", js);
811 }
812 }
813 }
814
815 public static void writeHTMLPages() {
Bill Napier4bac50a2010-08-25 18:17:16 -0700816 for (String htmlDir : ClearPage.htmlDirs) {
817 File f = new File(htmlDir);
818 if (!f.isDirectory()) {
819 System.err.println("htmlDir not a directory: " + htmlDir);
820 continue;
821 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700822
Bill Napier4bac50a2010-08-25 18:17:16 -0700823 ResourceLoader loader = new FileSystemResourceLoader(f);
824 JSilver js = new JSilver(loader);
825 writeDirectory(f, "", js);
826 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700827 }
828
829 public static void writeAssets() {
830 JarFile thisJar = JarUtils.jarForClass(Doclava.class, null);
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800831 if ((thisJar != null) && (includeDefaultAssets)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700832 try {
833 List<String> templateDirs = ClearPage.getBundledTemplateDirs();
834 for (String templateDir : templateDirs) {
835 String assetsDir = templateDir + "/assets";
836 JarUtils.copyResourcesToDirectory(thisJar, assetsDir, ClearPage.outputDir + "/assets");
837 }
838 } catch (IOException e) {
839 System.err.println("Error copying assets directory.");
840 e.printStackTrace();
841 return;
842 }
843 }
844
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800845 //write the project-specific assets
Ben Dodson920dbbb2010-08-04 15:21:06 -0700846 List<String> templateDirs = ClearPage.getTemplateDirs();
847 for (String templateDir : templateDirs) {
848 File assets = new File(templateDir + "/assets");
849 if (assets.isDirectory()) {
850 writeDirectory(assets, "assets/", null);
851 }
852 }
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800853
854 // Create the timestamp.js file based on .cs file
Scott Main460dd2a2013-01-29 14:18:45 -0800855 Data timedata = Doclava.makeHDF();
856 ClearPage.write(timedata, "timestamp.cs", "timestamp.js");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700857 }
858
Scott Main71bf8d22013-03-08 18:59:15 -0800859 /** Go through the docs and generate meta-data about each
860 page to use in search suggestions */
Ben Dodson920dbbb2010-08-04 15:21:06 -0700861 public static void writeLists() {
Scott Main71bf8d22013-03-08 18:59:15 -0800862
863 // Write the lists for API references
Ben Dodson920dbbb2010-08-04 15:21:06 -0700864 Data data = makeHDF();
865
866 ClassInfo[] classes = Converter.rootClasses();
867
868 SortedMap<String, Object> sorted = new TreeMap<String, Object>();
869 for (ClassInfo cl : classes) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800870 if (cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700871 continue;
872 }
873 sorted.put(cl.qualifiedName(), cl);
874 PackageInfo pkg = cl.containingPackage();
875 String name;
876 if (pkg == null) {
877 name = "";
878 } else {
879 name = pkg.name();
880 }
881 sorted.put(name, pkg);
882 }
883
884 int i = 0;
885 for (String s : sorted.keySet()) {
886 data.setValue("docs.pages." + i + ".id", "" + i);
887 data.setValue("docs.pages." + i + ".label", s);
888
889 Object o = sorted.get(s);
890 if (o instanceof PackageInfo) {
891 PackageInfo pkg = (PackageInfo) o;
892 data.setValue("docs.pages." + i + ".link", pkg.htmlPage());
893 data.setValue("docs.pages." + i + ".type", "package");
Scott Main68a238a2013-04-30 12:34:19 -0700894 data.setValue("docs.pages." + i + ".deprecated", pkg.isDeprecated() ? "true" : "false");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700895 } else if (o instanceof ClassInfo) {
896 ClassInfo cl = (ClassInfo) o;
897 data.setValue("docs.pages." + i + ".link", cl.htmlPage());
898 data.setValue("docs.pages." + i + ".type", "class");
Scott Main68a238a2013-04-30 12:34:19 -0700899 data.setValue("docs.pages." + i + ".deprecated", cl.isDeprecated() ? "true" : "false");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700900 }
901 i++;
902 }
Robert Ly57657b92013-02-21 02:00:37 +0000903 ClearPage.write(data, "lists.cs", javadocDir + "lists.js");
Scott Main71bf8d22013-03-08 18:59:15 -0800904
905
Scott Main77918022013-05-21 11:46:35 -0700906 // Write the lists for JD documents (if there are HTML directories to process)
907 if (inputPathHtmlDirs.size() > 0) {
908 Data jddata = makeHDF();
909 Iterator counter = new Iterator();
910 for (String htmlDir : inputPathHtmlDirs) {
911 File dir = new File(htmlDir);
912 if (!dir.isDirectory()) {
913 continue;
914 }
915 writeJdDirList(dir, jddata, counter);
Scott Main71bf8d22013-03-08 18:59:15 -0800916 }
Scott Main77918022013-05-21 11:46:35 -0700917 ClearPage.write(jddata, "jd_lists.cs", javadocDir + "jd_lists.js");
Scott Main71bf8d22013-03-08 18:59:15 -0800918 }
Scott Main71bf8d22013-03-08 18:59:15 -0800919 }
920
921 private static class Iterator {
922 int i = 0;
923 }
924
925 /** Write meta-data for a JD file, used for search suggestions */
926 private static void writeJdDirList(File dir, Data data, Iterator counter) {
927 File[] files = dir.listFiles();
928 int i, count = files.length;
929 // Loop all files in given directory
930 for (i = 0; i < count; i++) {
931 File f = files[i];
932 if (f.isFile()) {
933 String filePath = f.getAbsolutePath();
934 String templ = f.getName();
935 int len = templ.length();
936 // If it's a .jd file we want to process
937 if (len > 3 && ".jd".equals(templ.substring(len - 3))) {
938 // remove the directories below the site root
Hui Shu5118ffe2014-02-18 14:06:42 -0800939 String webPath = filePath.substring(filePath.indexOf("docs/html/") + 10,
940 filePath.length());
Scott Main71bf8d22013-03-08 18:59:15 -0800941 // replace .jd with .html
942 webPath = webPath.substring(0, webPath.length() - 3) + htmlExtension;
943 // Parse the .jd file for properties data at top of page
944 Data hdf = Doclava.makeHDF();
945 String filedata = DocFile.readFile(filePath);
946 Matcher lines = DocFile.LINE.matcher(filedata);
947 String line = null;
948 // Get each line to add the key-value to hdf
949 while (lines.find()) {
950 line = lines.group(1);
951 if (line.length() > 0) {
952 // Stop when we hit the body
953 if (line.equals("@jd:body")) {
954 break;
955 }
956 Matcher prop = DocFile.PROP.matcher(line);
957 if (prop.matches()) {
958 String key = prop.group(1);
959 String value = prop.group(2);
960 hdf.setValue(key, value);
961 } else {
962 break;
963 }
964 }
965 } // done gathering page properties
966
967 // Insert the goods into HDF data (title, link, tags, type)
968 String title = hdf.getValue("page.title", "");
Scott Main9950dd02013-04-11 19:03:58 -0700969 title = title.replaceAll("\"", "'");
970 // if there's a <span> in the title, get rid of it
971 if (title.indexOf("<span") != -1) {
972 String[] splitTitle = title.split("<span(.*?)</span>");
973 title = splitTitle[0];
974 for (int j = 1; j < splitTitle.length; j++) {
975 title.concat(splitTitle[j]);
976 }
977 }
Dirk Doughertyb4d562b2013-09-06 13:55:20 -0700978
979 StringBuilder tags = new StringBuilder();
Dirk Dougherty42303cc2013-09-13 19:45:17 -0700980 String tagsList = hdf.getValue("page.tags", "");
981 if (!tagsList.equals("")) {
982 tagsList = tagsList.replaceAll("\"", "");
983 String[] tagParts = tagsList.split(",");
Dirk Doughertyb4d562b2013-09-06 13:55:20 -0700984 for (int iter = 0; iter < tagParts.length; iter++) {
985 tags.append("\"");
986 tags.append(tagParts[iter].trim());
987 tags.append("\"");
988 if (iter < tagParts.length - 1) {
989 tags.append(",");
990 }
991 }
992 }
993
Scott Main71bf8d22013-03-08 18:59:15 -0800994 String dirName = (webPath.indexOf("/") != -1)
995 ? webPath.substring(0, webPath.indexOf("/")) : "";
996
Scott Main489864e2013-04-12 11:20:51 -0700997 if (!"".equals(title) &&
998 !"intl".equals(dirName) &&
999 !hdf.getBooleanValue("excludeFromSuggestions")) {
Scott Main9950dd02013-04-11 19:03:58 -07001000 data.setValue("docs.pages." + counter.i + ".label", title);
Scott Main71bf8d22013-03-08 18:59:15 -08001001 data.setValue("docs.pages." + counter.i + ".link", webPath);
Dirk Doughertyb4d562b2013-09-06 13:55:20 -07001002 data.setValue("docs.pages." + counter.i + ".tags", tags.toString());
Scott Main71bf8d22013-03-08 18:59:15 -08001003 data.setValue("docs.pages." + counter.i + ".type", dirName);
1004 counter.i++;
1005 }
1006 }
1007 } else if (f.isDirectory()) {
1008 writeJdDirList(f, data, counter);
1009 }
1010 }
Ben Dodson920dbbb2010-08-04 15:21:06 -07001011 }
1012
1013 public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) {
1014 if (!notStrippable.add(cl)) {
1015 // slight optimization: if it already contains cl, it already contains
1016 // all of cl's parents
1017 return;
1018 }
1019 ClassInfo supr = cl.superclass();
1020 if (supr != null) {
1021 cantStripThis(supr, notStrippable);
1022 }
1023 for (ClassInfo iface : cl.interfaces()) {
1024 cantStripThis(iface, notStrippable);
1025 }
1026 }
1027
1028 private static String getPrintableName(ClassInfo cl) {
1029 ClassInfo containingClass = cl.containingClass();
1030 if (containingClass != null) {
1031 // This is an inner class.
1032 String baseName = cl.name();
1033 baseName = baseName.substring(baseName.lastIndexOf('.') + 1);
1034 return getPrintableName(containingClass) + '$' + baseName;
1035 }
1036 return cl.qualifiedName();
1037 }
1038
1039 /**
1040 * Writes the list of classes that must be present in order to provide the non-hidden APIs known
1041 * to javadoc.
Scott Main3c1a6b22010-10-15 17:34:04 -07001042 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001043 * @param filename the path to the file to write the list to
1044 */
1045 public static void writeKeepList(String filename) {
1046 HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
1047 ClassInfo[] all = Converter.allClasses();
1048 Arrays.sort(all); // just to make the file a little more readable
1049
1050 // If a class is public and not hidden, then it and everything it derives
1051 // from cannot be stripped. Otherwise we can strip it.
1052 for (ClassInfo cl : all) {
Hui Shu5118ffe2014-02-18 14:06:42 -08001053 if (cl.isPublic() && !cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001054 cantStripThis(cl, notStrippable);
1055 }
1056 }
1057 PrintStream stream = null;
1058 try {
Brian Carlstromed8f7972011-06-10 11:10:48 -07001059 stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(filename)));
Ben Dodson920dbbb2010-08-04 15:21:06 -07001060 for (ClassInfo cl : notStrippable) {
1061 stream.println(getPrintableName(cl));
1062 }
1063 } catch (FileNotFoundException e) {
1064 System.err.println("error writing file: " + filename);
1065 } finally {
1066 if (stream != null) {
1067 stream.close();
1068 }
1069 }
1070 }
1071
1072 private static PackageInfo[] sVisiblePackages = null;
1073
1074 public static PackageInfo[] choosePackages() {
1075 if (sVisiblePackages != null) {
1076 return sVisiblePackages;
1077 }
1078
1079 ClassInfo[] classes = Converter.rootClasses();
1080 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
1081 for (ClassInfo cl : classes) {
1082 PackageInfo pkg = cl.containingPackage();
1083 String name;
1084 if (pkg == null) {
1085 name = "";
1086 } else {
1087 name = pkg.name();
1088 }
1089 sorted.put(name, pkg);
1090 }
1091
1092 ArrayList<PackageInfo> result = new ArrayList<PackageInfo>();
1093
1094 for (String s : sorted.keySet()) {
1095 PackageInfo pkg = sorted.get(s);
1096
Hui Shu5118ffe2014-02-18 14:06:42 -08001097 if (pkg.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001098 continue;
1099 }
Hui Shu5118ffe2014-02-18 14:06:42 -08001100
1101 boolean allHiddenOrRemoved = true;
Ben Dodson920dbbb2010-08-04 15:21:06 -07001102 int pass = 0;
1103 ClassInfo[] classesToCheck = null;
Brett Chabot700b9f22013-10-04 16:57:39 -07001104 while (pass < 6) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001105 switch (pass) {
1106 case 0:
1107 classesToCheck = pkg.ordinaryClasses();
1108 break;
1109 case 1:
1110 classesToCheck = pkg.enums();
1111 break;
1112 case 2:
1113 classesToCheck = pkg.errors();
1114 break;
1115 case 3:
1116 classesToCheck = pkg.exceptions();
1117 break;
1118 case 4:
1119 classesToCheck = pkg.interfaces();
1120 break;
Brett Chabot700b9f22013-10-04 16:57:39 -07001121 case 5:
1122 classesToCheck = pkg.annotations();
1123 break;
Ben Dodson920dbbb2010-08-04 15:21:06 -07001124 default:
1125 System.err.println("Error reading package: " + pkg.name());
1126 break;
1127 }
1128 for (ClassInfo cl : classesToCheck) {
Hui Shu5118ffe2014-02-18 14:06:42 -08001129 if (!cl.isHiddenOrRemoved()) {
1130 allHiddenOrRemoved = false;
Ben Dodson920dbbb2010-08-04 15:21:06 -07001131 break;
1132 }
1133 }
Hui Shu5118ffe2014-02-18 14:06:42 -08001134 if (!allHiddenOrRemoved) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001135 break;
1136 }
1137 pass++;
1138 }
Hui Shu5118ffe2014-02-18 14:06:42 -08001139 if (allHiddenOrRemoved) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001140 continue;
1141 }
1142
1143 result.add(pkg);
1144 }
1145
1146 sVisiblePackages = result.toArray(new PackageInfo[result.size()]);
1147 return sVisiblePackages;
1148 }
1149
1150 public static void writePackages(String filename) {
1151 Data data = makePackageHDF();
1152
1153 int i = 0;
1154 for (PackageInfo pkg : choosePackages()) {
1155 writePackage(pkg);
1156
1157 data.setValue("docs.packages." + i + ".name", pkg.name());
1158 data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
1159 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr", pkg.firstSentenceTags());
1160
1161 i++;
1162 }
1163
1164 setPageTitle(data, "Package Index");
1165
1166 TagInfo.makeHDF(data, "root.descr", Converter.convertTags(root.inlineTags(), null));
1167
1168 ClearPage.write(data, "packages.cs", filename);
1169 ClearPage.write(data, "package-list.cs", javadocDir + "package-list");
1170
1171 Proofread.writePackages(filename, Converter.convertTags(root.inlineTags(), null));
1172 }
1173
1174 public static void writePackage(PackageInfo pkg) {
1175 // these this and the description are in the same directory,
1176 // so it's okay
1177 Data data = makePackageHDF();
1178
1179 String name = pkg.name();
1180
1181 data.setValue("package.name", name);
1182 data.setValue("package.since", pkg.getSince());
1183 data.setValue("package.descr", "...description...");
1184 pkg.setFederatedReferences(data, "package");
1185
Brett Chabot700b9f22013-10-04 16:57:39 -07001186 makeClassListHDF(data, "package.annotations", ClassInfo.sortByName(pkg.annotations()));
Ben Dodson920dbbb2010-08-04 15:21:06 -07001187 makeClassListHDF(data, "package.interfaces", ClassInfo.sortByName(pkg.interfaces()));
1188 makeClassListHDF(data, "package.classes", ClassInfo.sortByName(pkg.ordinaryClasses()));
1189 makeClassListHDF(data, "package.enums", ClassInfo.sortByName(pkg.enums()));
1190 makeClassListHDF(data, "package.exceptions", ClassInfo.sortByName(pkg.exceptions()));
1191 makeClassListHDF(data, "package.errors", ClassInfo.sortByName(pkg.errors()));
1192 TagInfo.makeHDF(data, "package.shortDescr", pkg.firstSentenceTags());
1193 TagInfo.makeHDF(data, "package.descr", pkg.inlineTags());
1194
1195 String filename = pkg.htmlPage();
1196 setPageTitle(data, name);
1197 ClearPage.write(data, "package.cs", filename);
1198
Ben Dodson920dbbb2010-08-04 15:21:06 -07001199 Proofread.writePackage(filename, pkg.inlineTags());
1200 }
1201
1202 public static void writeClassLists() {
1203 int i;
1204 Data data = makePackageHDF();
1205
Hui Shu5118ffe2014-02-18 14:06:42 -08001206 ClassInfo[] classes = PackageInfo.filterHiddenAndRemoved(
1207 Converter.convertClasses(root.classes()));
Ben Dodson920dbbb2010-08-04 15:21:06 -07001208 if (classes.length == 0) {
1209 return;
1210 }
1211
1212 Sorter[] sorted = new Sorter[classes.length];
1213 for (i = 0; i < sorted.length; i++) {
1214 ClassInfo cl = classes[i];
1215 String name = cl.name();
1216 sorted[i] = new Sorter(name, cl);
1217 }
1218
1219 Arrays.sort(sorted);
1220
1221 // make a pass and resolve ones that have the same name
1222 int firstMatch = 0;
1223 String lastName = sorted[0].label;
1224 for (i = 1; i < sorted.length; i++) {
1225 String s = sorted[i].label;
1226 if (!lastName.equals(s)) {
1227 if (firstMatch != i - 1) {
1228 // there were duplicates
1229 for (int j = firstMatch; j < i; j++) {
1230 PackageInfo pkg = ((ClassInfo) sorted[j].data).containingPackage();
1231 if (pkg != null) {
1232 sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
1233 }
1234 }
1235 }
1236 firstMatch = i;
1237 lastName = s;
1238 }
1239 }
1240
1241 // and sort again
1242 Arrays.sort(sorted);
1243
1244 for (i = 0; i < sorted.length; i++) {
1245 String s = sorted[i].label;
1246 ClassInfo cl = (ClassInfo) sorted[i].data;
1247 char first = Character.toUpperCase(s.charAt(0));
1248 cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i);
1249 }
1250
1251 setPageTitle(data, "Class Index");
1252 ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension);
1253 }
1254
1255 // we use the word keywords because "index" means something else in html land
1256 // the user only ever sees the word index
1257 /*
1258 * public static void writeKeywords() { ArrayList<KeywordEntry> keywords = new
1259 * ArrayList<KeywordEntry>();
Scott Main3c1a6b22010-10-15 17:34:04 -07001260 *
Hui Shu5118ffe2014-02-18 14:06:42 -08001261 * ClassInfo[] classes = PackageInfo.filterHiddenAndRemoved(Converter.convertClasses(root.classes()));
Scott Main3c1a6b22010-10-15 17:34:04 -07001262 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001263 * for (ClassInfo cl: classes) { cl.makeKeywordEntries(keywords); }
Scott Main3c1a6b22010-10-15 17:34:04 -07001264 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001265 * HDF data = makeHDF();
Scott Main3c1a6b22010-10-15 17:34:04 -07001266 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001267 * Collections.sort(keywords);
Scott Main3c1a6b22010-10-15 17:34:04 -07001268 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001269 * int i=0; for (KeywordEntry entry: keywords) { String base = "keywords." + entry.firstChar() +
1270 * "." + i; entry.makeHDF(data, base); i++; }
Scott Main3c1a6b22010-10-15 17:34:04 -07001271 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001272 * setPageTitle(data, "Index"); ClearPage.write(data, "keywords.cs", javadocDir + "keywords" +
1273 * htmlExtension); }
1274 */
1275
1276 public static void writeHierarchy() {
1277 ClassInfo[] classes = Converter.rootClasses();
1278 ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
1279 for (ClassInfo cl : classes) {
Hui Shu5118ffe2014-02-18 14:06:42 -08001280 if (!cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001281 info.add(cl);
1282 }
1283 }
1284 Data data = makePackageHDF();
1285 Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()]));
1286 setPageTitle(data, "Class Hierarchy");
1287 ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension);
1288 }
1289
1290 public static void writeClasses() {
1291 ClassInfo[] classes = Converter.rootClasses();
1292
1293 for (ClassInfo cl : classes) {
1294 Data data = makePackageHDF();
Hui Shu5118ffe2014-02-18 14:06:42 -08001295 if (!cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001296 writeClass(cl, data);
1297 }
1298 }
1299 }
1300
1301 public static void writeClass(ClassInfo cl, Data data) {
1302 cl.makeHDF(data);
Ben Dodson920dbbb2010-08-04 15:21:06 -07001303 setPageTitle(data, cl.name());
Dirk Dougherty9b316c82013-01-28 10:53:33 -08001304 String outfile = cl.htmlPage();
1305 ClearPage.write(data, "class.cs", outfile);
Ben Dodson920dbbb2010-08-04 15:21:06 -07001306 Proofread.writeClass(cl.htmlPage(), cl);
1307 }
1308
1309 public static void makeClassListHDF(Data data, String base, ClassInfo[] classes) {
1310 for (int i = 0; i < classes.length; i++) {
1311 ClassInfo cl = classes[i];
Hui Shu5118ffe2014-02-18 14:06:42 -08001312 if (!cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001313 cl.makeShortDescrHDF(data, base + "." + i);
1314 }
1315 }
1316 }
1317
1318 public static String linkTarget(String source, String target) {
1319 String[] src = source.split("/");
1320 String[] tgt = target.split("/");
1321
1322 int srclen = src.length;
1323 int tgtlen = tgt.length;
1324
1325 int same = 0;
1326 while (same < (srclen - 1) && same < (tgtlen - 1) && (src[same].equals(tgt[same]))) {
1327 same++;
1328 }
1329
1330 String s = "";
1331
1332 int up = srclen - same - 1;
1333 for (int i = 0; i < up; i++) {
1334 s += "../";
1335 }
1336
1337
1338 int N = tgtlen - 1;
1339 for (int i = same; i < N; i++) {
1340 s += tgt[i] + '/';
1341 }
1342 s += tgt[tgtlen - 1];
1343
1344 return s;
1345 }
1346
1347 /**
Hui Shu5118ffe2014-02-18 14:06:42 -08001348 * Returns true if the given element has an @hide, @removed or @pending annotation.
Ben Dodson920dbbb2010-08-04 15:21:06 -07001349 */
Hui Shu5118ffe2014-02-18 14:06:42 -08001350 private static boolean hasHideOrRemovedAnnotation(Doc doc) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001351 String comment = doc.getRawCommentText();
Hui Shu5118ffe2014-02-18 14:06:42 -08001352 return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1 ||
1353 comment.indexOf("@removed") != -1;
Ben Dodson920dbbb2010-08-04 15:21:06 -07001354 }
1355
1356 /**
1357 * Returns true if the given element is hidden.
1358 */
Hui Shu5118ffe2014-02-18 14:06:42 -08001359 private static boolean isHiddenOrRemoved(Doc doc) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001360 // Methods, fields, constructors.
1361 if (doc instanceof MemberDoc) {
Hui Shu5118ffe2014-02-18 14:06:42 -08001362 return hasHideOrRemovedAnnotation(doc);
Ben Dodson920dbbb2010-08-04 15:21:06 -07001363 }
1364
1365 // Classes, interfaces, enums, annotation types.
1366 if (doc instanceof ClassDoc) {
1367 ClassDoc classDoc = (ClassDoc) doc;
1368
1369 // Check the containing package.
Hui Shu5118ffe2014-02-18 14:06:42 -08001370 if (hasHideOrRemovedAnnotation(classDoc.containingPackage())) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001371 return true;
1372 }
1373
1374 // Check the class doc and containing class docs if this is a
1375 // nested class.
1376 ClassDoc current = classDoc;
1377 do {
Hui Shu5118ffe2014-02-18 14:06:42 -08001378 if (hasHideOrRemovedAnnotation(current)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001379 return true;
1380 }
1381
1382 current = current.containingClass();
1383 } while (current != null);
1384 }
1385
1386 return false;
1387 }
1388
1389 /**
Hui Shu5118ffe2014-02-18 14:06:42 -08001390 * Filters out hidden and removed elements.
Ben Dodson920dbbb2010-08-04 15:21:06 -07001391 */
Hui Shu5118ffe2014-02-18 14:06:42 -08001392 private static Object filterHiddenAndRemoved(Object o, Class<?> expected) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001393 if (o == null) {
1394 return null;
1395 }
1396
1397 Class type = o.getClass();
1398 if (type.getName().startsWith("com.sun.")) {
1399 // TODO: Implement interfaces from superclasses, too.
1400 return Proxy
1401 .newProxyInstance(type.getClassLoader(), type.getInterfaces(), new HideHandler(o));
1402 } else if (o instanceof Object[]) {
1403 Class<?> componentType = expected.getComponentType();
1404 Object[] array = (Object[]) o;
1405 List<Object> list = new ArrayList<Object>(array.length);
1406 for (Object entry : array) {
Hui Shu5118ffe2014-02-18 14:06:42 -08001407 if ((entry instanceof Doc) && isHiddenOrRemoved((Doc) entry)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001408 continue;
1409 }
Hui Shu5118ffe2014-02-18 14:06:42 -08001410 list.add(filterHiddenAndRemoved(entry, componentType));
Ben Dodson920dbbb2010-08-04 15:21:06 -07001411 }
1412 return list.toArray((Object[]) Array.newInstance(componentType, list.size()));
1413 } else {
1414 return o;
1415 }
1416 }
1417
1418 /**
1419 * Filters hidden elements out of method return values.
1420 */
1421 private static class HideHandler implements InvocationHandler {
1422
1423 private final Object target;
1424
1425 public HideHandler(Object target) {
1426 this.target = target;
1427 }
1428
1429 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
1430 String methodName = method.getName();
1431 if (args != null) {
1432 if (methodName.equals("compareTo") || methodName.equals("equals")
1433 || methodName.equals("overrides") || methodName.equals("subclassOf")) {
1434 args[0] = unwrap(args[0]);
1435 }
1436 }
1437
1438 if (methodName.equals("getRawCommentText")) {
1439 return filterComment((String) method.invoke(target, args));
1440 }
1441
1442 // escape "&" in disjunctive types.
1443 if (proxy instanceof Type && methodName.equals("toString")) {
1444 return ((String) method.invoke(target, args)).replace("&", "&amp;");
1445 }
1446
1447 try {
Hui Shu5118ffe2014-02-18 14:06:42 -08001448 return filterHiddenAndRemoved(method.invoke(target, args), method.getReturnType());
Ben Dodson920dbbb2010-08-04 15:21:06 -07001449 } catch (InvocationTargetException e) {
1450 throw e.getTargetException();
1451 }
1452 }
1453
1454 private String filterComment(String s) {
1455 if (s == null) {
1456 return null;
1457 }
1458
1459 s = s.trim();
1460
1461 // Work around off by one error
1462 while (s.length() >= 5 && s.charAt(s.length() - 5) == '{') {
1463 s += "&nbsp;";
1464 }
1465
1466 return s;
1467 }
1468
1469 private static Object unwrap(Object proxy) {
1470 if (proxy instanceof Proxy) return ((HideHandler) Proxy.getInvocationHandler(proxy)).target;
1471 return proxy;
1472 }
1473 }
1474
1475 /**
1476 * Collect the values used by the Dev tools and write them in files packaged with the SDK
Scott Main3c1a6b22010-10-15 17:34:04 -07001477 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001478 * @param output the ouput directory for the files.
1479 */
1480 private static void writeSdkValues(String output) {
1481 ArrayList<String> activityActions = new ArrayList<String>();
1482 ArrayList<String> broadcastActions = new ArrayList<String>();
1483 ArrayList<String> serviceActions = new ArrayList<String>();
1484 ArrayList<String> categories = new ArrayList<String>();
1485 ArrayList<String> features = new ArrayList<String>();
1486
1487 ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
1488 ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
1489 ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
1490
1491 ClassInfo[] classes = Converter.allClasses();
1492
Deepanshu Guptadf8d48b2014-06-10 15:04:58 -07001493 // The topmost LayoutParams class - android.view.ViewGroup.LayoutParams
1494 ClassInfo topLayoutParams = null;
1495
Ben Dodson920dbbb2010-08-04 15:21:06 -07001496 // Go through all the fields of all the classes, looking SDK stuff.
1497 for (ClassInfo clazz : classes) {
1498
1499 // first check constant fields for the SdkConstant annotation.
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001500 ArrayList<FieldInfo> fields = clazz.allSelfFields();
Ben Dodson920dbbb2010-08-04 15:21:06 -07001501 for (FieldInfo field : fields) {
1502 Object cValue = field.constantValue();
1503 if (cValue != null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001504 ArrayList<AnnotationInstanceInfo> annotations = field.annotations();
1505 if (!annotations.isEmpty()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001506 for (AnnotationInstanceInfo annotation : annotations) {
1507 if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001508 if (!annotation.elementValues().isEmpty()) {
1509 String type = annotation.elementValues().get(0).valueString();
Ben Dodson920dbbb2010-08-04 15:21:06 -07001510 if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) {
1511 activityActions.add(cValue.toString());
1512 } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) {
1513 broadcastActions.add(cValue.toString());
1514 } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) {
1515 serviceActions.add(cValue.toString());
1516 } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) {
1517 categories.add(cValue.toString());
1518 } else if (SDK_CONSTANT_TYPE_FEATURE.equals(type)) {
1519 features.add(cValue.toString());
1520 }
1521 }
1522 break;
1523 }
1524 }
1525 }
1526 }
1527 }
1528
1529 // Now check the class for @Widget or if its in the android.widget package
1530 // (unless the class is hidden or abstract, or non public)
Hui Shu5118ffe2014-02-18 14:06:42 -08001531 if (clazz.isHiddenOrRemoved() == false && clazz.isPublic() && clazz.isAbstract() == false) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001532 boolean annotated = false;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001533 ArrayList<AnnotationInstanceInfo> annotations = clazz.annotations();
1534 if (!annotations.isEmpty()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001535 for (AnnotationInstanceInfo annotation : annotations) {
1536 if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) {
1537 widgets.add(clazz);
1538 annotated = true;
1539 break;
1540 } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) {
1541 layouts.add(clazz);
1542 annotated = true;
1543 break;
1544 }
1545 }
1546 }
1547
1548 if (annotated == false) {
Deepanshu Guptadf8d48b2014-06-10 15:04:58 -07001549 if (topLayoutParams == null
1550 && "android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
1551 topLayoutParams = clazz;
1552 }
1553 // let's check if this is inside android.widget or android.view
1554 if (isIncludedPackage(clazz)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001555 // now we check what this class inherits either from android.view.ViewGroup
1556 // or android.view.View, or android.view.ViewGroup.LayoutParams
1557 int type = checkInheritance(clazz);
1558 switch (type) {
1559 case TYPE_WIDGET:
1560 widgets.add(clazz);
1561 break;
1562 case TYPE_LAYOUT:
1563 layouts.add(clazz);
1564 break;
1565 case TYPE_LAYOUT_PARAM:
1566 layoutParams.add(clazz);
1567 break;
1568 }
1569 }
1570 }
1571 }
1572 }
1573
1574 // now write the files, whether or not the list are empty.
1575 // the SDK built requires those files to be present.
1576
1577 Collections.sort(activityActions);
1578 writeValues(output + "/activity_actions.txt", activityActions);
1579
1580 Collections.sort(broadcastActions);
1581 writeValues(output + "/broadcast_actions.txt", broadcastActions);
1582
1583 Collections.sort(serviceActions);
1584 writeValues(output + "/service_actions.txt", serviceActions);
1585
1586 Collections.sort(categories);
1587 writeValues(output + "/categories.txt", categories);
1588
1589 Collections.sort(features);
1590 writeValues(output + "/features.txt", features);
1591
1592 // before writing the list of classes, we do some checks, to make sure the layout params
1593 // are enclosed by a layout class (and not one that has been declared as a widget)
1594 for (int i = 0; i < layoutParams.size();) {
Deepanshu Guptadf8d48b2014-06-10 15:04:58 -07001595 ClassInfo clazz = layoutParams.get(i);
1596 ClassInfo containingClass = clazz.containingClass();
1597 boolean remove = containingClass == null || layouts.indexOf(containingClass) == -1;
1598 // Also ensure that super classes of the layout params are in android.widget or android.view.
1599 while (!remove && (clazz = clazz.superclass()) != null && !clazz.equals(topLayoutParams)) {
1600 remove = !isIncludedPackage(clazz);
1601 }
1602 if (remove) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001603 layoutParams.remove(i);
1604 } else {
1605 i++;
1606 }
1607 }
1608
1609 writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
1610 }
1611
1612 /**
Deepanshu Guptadf8d48b2014-06-10 15:04:58 -07001613 * Check if the clazz is in package android.view or android.widget
1614 */
1615 private static boolean isIncludedPackage(ClassInfo clazz) {
1616 String pckg = clazz.containingPackage().name();
1617 return "android.widget".equals(pckg) || "android.view".equals(pckg);
1618 }
1619
1620 /**
Ben Dodson920dbbb2010-08-04 15:21:06 -07001621 * Writes a list of values into a text files.
Scott Main3c1a6b22010-10-15 17:34:04 -07001622 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001623 * @param pathname the absolute os path of the output file.
1624 * @param values the list of values to write.
1625 */
1626 private static void writeValues(String pathname, ArrayList<String> values) {
1627 FileWriter fw = null;
1628 BufferedWriter bw = null;
1629 try {
1630 fw = new FileWriter(pathname, false);
1631 bw = new BufferedWriter(fw);
1632
1633 for (String value : values) {
1634 bw.append(value).append('\n');
1635 }
1636 } catch (IOException e) {
1637 // pass for now
1638 } finally {
1639 try {
1640 if (bw != null) bw.close();
1641 } catch (IOException e) {
1642 // pass for now
1643 }
1644 try {
1645 if (fw != null) fw.close();
1646 } catch (IOException e) {
1647 // pass for now
1648 }
1649 }
1650 }
1651
1652 /**
1653 * Writes the widget/layout/layout param classes into a text files.
Scott Main3c1a6b22010-10-15 17:34:04 -07001654 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001655 * @param pathname the absolute os path of the output file.
1656 * @param widgets the list of widget classes to write.
1657 * @param layouts the list of layout classes to write.
1658 * @param layoutParams the list of layout param classes to write.
1659 */
1660 private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets,
1661 ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) {
1662 FileWriter fw = null;
1663 BufferedWriter bw = null;
1664 try {
1665 fw = new FileWriter(pathname, false);
1666 bw = new BufferedWriter(fw);
1667
1668 // write the 3 types of classes.
1669 for (ClassInfo clazz : widgets) {
1670 writeClass(bw, clazz, 'W');
1671 }
1672 for (ClassInfo clazz : layoutParams) {
1673 writeClass(bw, clazz, 'P');
1674 }
1675 for (ClassInfo clazz : layouts) {
1676 writeClass(bw, clazz, 'L');
1677 }
1678 } catch (IOException e) {
1679 // pass for now
1680 } finally {
1681 try {
1682 if (bw != null) bw.close();
1683 } catch (IOException e) {
1684 // pass for now
1685 }
1686 try {
1687 if (fw != null) fw.close();
1688 } catch (IOException e) {
1689 // pass for now
1690 }
1691 }
1692 }
1693
1694 /**
1695 * Writes a class name and its super class names into a {@link BufferedWriter}.
Scott Main3c1a6b22010-10-15 17:34:04 -07001696 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001697 * @param writer the BufferedWriter to write into
1698 * @param clazz the class to write
1699 * @param prefix the prefix to put at the beginning of the line.
1700 * @throws IOException
1701 */
1702 private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)
1703 throws IOException {
1704 writer.append(prefix).append(clazz.qualifiedName());
1705 ClassInfo superClass = clazz;
1706 while ((superClass = superClass.superclass()) != null) {
1707 writer.append(' ').append(superClass.qualifiedName());
1708 }
1709 writer.append('\n');
1710 }
1711
1712 /**
1713 * Checks the inheritance of {@link ClassInfo} objects. This method return
1714 * <ul>
1715 * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li>
1716 * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
1717 * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends
1718 * <code>android.view.ViewGroup$LayoutParams</code></li>
1719 * <li>{@link #TYPE_NONE}: in all other cases</li>
1720 * </ul>
Scott Main3c1a6b22010-10-15 17:34:04 -07001721 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001722 * @param clazz the {@link ClassInfo} to check.
1723 */
1724 private static int checkInheritance(ClassInfo clazz) {
1725 if ("android.view.ViewGroup".equals(clazz.qualifiedName())) {
1726 return TYPE_LAYOUT;
1727 } else if ("android.view.View".equals(clazz.qualifiedName())) {
1728 return TYPE_WIDGET;
1729 } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
1730 return TYPE_LAYOUT_PARAM;
1731 }
1732
1733 ClassInfo parent = clazz.superclass();
1734 if (parent != null) {
1735 return checkInheritance(parent);
1736 }
1737
1738 return TYPE_NONE;
1739 }
Chirag Shah18b25552012-08-09 13:57:54 -07001740
1741 /**
1742 * Ensures a trailing '/' at the end of a string.
1743 */
1744 static String ensureSlash(String path) {
1745 return path.endsWith("/") ? path : path + "/";
1746 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -07001747
1748 /**
Dirk Dougherty75372292013-11-26 18:45:31 -08001749 * Process sample projects. Generate the TOC for the samples groups and project
1750 * and write it to a cs var, which is then written to files during templating to
1751 * html output. Collect metadata from sample project _index.jd files. Copy html
1752 * and specific source file types to the output directory.
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -07001753 */
Dirk Dougherty815f9342013-09-12 00:30:28 -07001754 public static void writeSamples(boolean offlineMode, ArrayList<SampleCode> sampleCodes,
1755 boolean sortNavByGroups) {
Dirk Dougherty75372292013-11-26 18:45:31 -08001756 samplesNavTree = makeHDF();
1757
1758 // Go through samples processing files. Create a root list for SC nodes,
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -07001759 // pass it to SCs for their NavTree children and append them.
1760 List<SampleCode.Node> samplesList = new ArrayList<SampleCode.Node>();
Dirk Dougherty815f9342013-09-12 00:30:28 -07001761 List<SampleCode.Node> sampleGroupsRootNodes = null;
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -07001762 for (SampleCode sc : sampleCodes) {
Dirk Dougherty75372292013-11-26 18:45:31 -08001763 samplesList.add(sc.setSamplesTOC(offlineMode));
1764 }
Dirk Dougherty815f9342013-09-12 00:30:28 -07001765 if (sortNavByGroups) {
1766 sampleGroupsRootNodes = new ArrayList<SampleCode.Node>();
1767 for (SampleCode gsc : sampleCodeGroups) {
Dirk Dougherty75372292013-11-26 18:45:31 -08001768 String link = ClearPage.toroot + "samples/" + gsc.mTitle.replaceAll(" ", "").trim().toLowerCase() + ".html";
Dirk Dougherty42303cc2013-09-13 19:45:17 -07001769 sampleGroupsRootNodes.add(new SampleCode.Node.Builder().setLabel(gsc.mTitle).setLink(link).setType("groupholder").build());
Dirk Dougherty815f9342013-09-12 00:30:28 -07001770 }
1771 }
Dirk Dougherty75372292013-11-26 18:45:31 -08001772 // Pass full samplesList to SC to render the samples TOC to sampleNavTree hdf
Dirk Doughertyda7497f2013-08-22 15:22:36 -07001773 if (!offlineMode) {
Dirk Dougherty815f9342013-09-12 00:30:28 -07001774 SampleCode.writeSamplesNavTree(samplesList, sampleGroupsRootNodes);
Dirk Doughertyda7497f2013-08-22 15:22:36 -07001775 }
Dirk Dougherty75372292013-11-26 18:45:31 -08001776 // Iterate the samplecode projects writing the files to out
1777 for (SampleCode sc : sampleCodes) {
1778 sc.writeSamplesFiles(offlineMode);
1779 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -07001780 }
1781
Dirk Dougherty5ce827a2013-11-21 18:08:58 -08001782 /**
1783 * Given an initial samples directory root, walk through the directory collecting
1784 * sample code project roots and adding them to an array of SampleCodes.
1785 * @param rootDir Root directory holding all browseable sample code projects,
1786 * defined in frameworks/base/Android.mk as "-sampleDir path".
1787 */
1788 public static void getSampleProjects(File rootDir) {
1789 for (File f : rootDir.listFiles()) {
1790 String name = f.getName();
1791 if (f.isDirectory()) {
1792 if (isValidSampleProjectRoot(f)) {
1793 sampleCodes.add(new SampleCode(f.getAbsolutePath(), "samples/" + name, name));
1794 } else {
1795 getSampleProjects(f);
1796 }
1797 }
1798 }
1799 }
1800
1801 /**
1802 * Test whether a given directory is the root directory for a sample code project.
Renato Manginie11680d2014-08-15 13:17:12 -07001803 * Root directories must contain a valid _index.jd file and a src/ directory
1804 * or a module directory that contains a src/ directory.
Dirk Dougherty5ce827a2013-11-21 18:08:58 -08001805 */
1806 public static boolean isValidSampleProjectRoot(File dir) {
Renato Manginie11680d2014-08-15 13:17:12 -07001807 File indexJd = new File(dir, "_index.jd");
1808 if (!indexJd.exists()) {
1809 return false;
1810 }
1811 File srcDir = new File(dir, "src");
1812 if (srcDir.exists()) {
Dirk Dougherty5ce827a2013-11-21 18:08:58 -08001813 return true;
1814 } else {
Renato Manginie11680d2014-08-15 13:17:12 -07001815 // Look for a src/ directory one level below the root directory, so
1816 // modules are supported.
1817 for (File childDir : dir.listFiles()) {
1818 if (childDir.isDirectory()) {
1819 srcDir = new File(childDir, "src");
1820 if (srcDir.exists()) {
1821 return true;
1822 }
1823 }
1824 }
Dirk Dougherty5ce827a2013-11-21 18:08:58 -08001825 return false;
1826 }
1827 }
Hui Shu5118ffe2014-02-18 14:06:42 -08001828
Jeff Arnesonb2a6c042015-03-27 14:54:47 -07001829 public static String getDocumentationStringForAnnotation(String annotationName) {
1830 if (!documentAnnotations) return null;
1831 if (annotationDocumentationMap == null) {
1832 // parse the file for map
1833 annotationDocumentationMap = new HashMap<String, String>();
1834 try {
1835 BufferedReader in = new BufferedReader(
1836 new FileReader(documentAnnotationsPath));
1837 try {
1838 String line = in.readLine();
1839 String[] split;
1840 while (line != null) {
1841 split = line.split(":");
1842 annotationDocumentationMap.put(split[0], split[1]);
1843 line = in.readLine();
1844 }
1845 } finally {
1846 in.close();
1847 }
1848 } catch (IOException e) {
1849 System.err.println("Unable to open annotations documentation file for reading: "
1850 + documentAnnotationsPath);
1851 }
1852 }
1853 return annotationDocumentationMap.get(annotationName);
1854 }
1855
Ben Dodson920dbbb2010-08-04 15:21:06 -07001856}