blob: 6e4612359cb24b312847c5e8782bb646dec74145 [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;
Scott Main3c1a6b22010-10-15 17:34:04 -070098
Ben Dodson920dbbb2010-08-04 15:21:06 -070099 public static JSilver jSilver = null;
100
Robert Ly5ab9cb52012-11-30 02:57:14 -0800101 private static boolean gmsRef = false;
102 private static boolean gcmRef = false;
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700103 private static boolean samplesRef = false;
Robert Ly88c435b2013-02-01 11:55:04 -0800104 private static boolean sac = false;
Robert Ly5ab9cb52012-11-30 02:57:14 -0800105
Ben Dodson920dbbb2010-08-04 15:21:06 -0700106 public static boolean checkLevel(int level) {
107 return (showLevel & level) == level;
108 }
Scott Main3c1a6b22010-10-15 17:34:04 -0700109
Ben Dodson920dbbb2010-08-04 15:21:06 -0700110 /**
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700111 * Returns true if we should parse javadoc comments,
112 * reporting errors in the process.
Ben Dodson920dbbb2010-08-04 15:21:06 -0700113 */
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700114 public static boolean parseComments() {
115 return generateDocs || parseComments;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700116 }
117
118 public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp, boolean priv,
119 boolean hidden) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700120 if (hidden && !checkLevel(SHOW_HIDDEN)) {
121 return false;
122 }
123 if (pub && checkLevel(SHOW_PUBLIC)) {
124 return true;
125 }
126 if (prot && checkLevel(SHOW_PROTECTED)) {
127 return true;
128 }
129 if (pkgp && checkLevel(SHOW_PACKAGE)) {
130 return true;
131 }
132 if (priv && checkLevel(SHOW_PRIVATE)) {
133 return true;
134 }
135 return false;
136 }
137
138 public static void main(String[] args) {
139 com.sun.tools.javadoc.Main.execute(args);
140 }
141
142 public static boolean start(RootDoc r) {
Andrew Sappersteinf959ed12011-06-23 10:24:40 -0700143 long startTime = System.nanoTime();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700144 String keepListFile = null;
Jeff Hamilton970f13f2012-06-22 00:21:56 -0500145 String proguardFile = null;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700146 String proofreadFile = null;
147 String todoFile = null;
148 String sdkValuePath = null;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700149 String stubsDir = null;
Dirk Dougherty41d86562010-08-20 15:21:11 -0700150 // Create the dependency graph for the stubs directory
Ben Dodson920dbbb2010-08-04 15:21:06 -0700151 boolean offlineMode = false;
152 String apiFile = null;
Hui Shu5118ffe2014-02-18 14:06:42 -0800153 String removedApiFile = null;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700154 String debugStubsFile = "";
155 HashSet<String> stubPackages = null;
Joe Onorato7a6456c2010-09-16 12:36:36 -0400156 ArrayList<String> knownTagsFiles = new ArrayList<String>();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700157
158 root = r;
159
160 String[][] options = r.options();
161 for (String[] a : options) {
162 if (a[0].equals("-d")) {
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800163 outputPathBase = outputPathHtmlDirs = ClearPage.outputDir = a[1];
Ben Dodson920dbbb2010-08-04 15:21:06 -0700164 } else if (a[0].equals("-templatedir")) {
165 ClearPage.addTemplateDir(a[1]);
166 } else if (a[0].equals("-hdf")) {
167 mHDFData.add(new String[] {a[1], a[2]});
Joe Onorato7a6456c2010-09-16 12:36:36 -0400168 } else if (a[0].equals("-knowntags")) {
169 knownTagsFiles.add(a[1]);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700170 } else if (a[0].equals("-toroot")) {
171 ClearPage.toroot = a[1];
172 } else if (a[0].equals("-samplecode")) {
173 sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
Dirk Dougherty815f9342013-09-12 00:30:28 -0700174 } else if (a[0].equals("-samplegroup")) {
175 sampleCodeGroups.add(new SampleCode(null, null, a[1]));
Dirk Dougherty5ce827a2013-11-21 18:08:58 -0800176 } else if (a[0].equals("-samplesdir")) {
177 getSampleProjects(new File(a[1]));
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800178 //the destination output path for main htmldir
Ben Dodson920dbbb2010-08-04 15:21:06 -0700179 } else if (a[0].equals("-htmldir")) {
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800180 inputPathHtmlDirs.add(a[1]);
181 ClearPage.htmlDirs = inputPathHtmlDirs;
182 //the destination output path for additional htmldir
183 } else if (a[0].equals("-htmldir2")) {
184 if (a[2].equals("default")) {
185 inputPathHtmlDirs.add(a[1]);
186 } else {
187 inputPathHtmlDir2.add(a[1]);
188 outputPathHtmlDir2 = a[2];
189 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700190 } else if (a[0].equals("-title")) {
191 Doclava.title = a[1];
192 } else if (a[0].equals("-werror")) {
193 Errors.setWarningsAreErrors(true);
194 } else if (a[0].equals("-error") || a[0].equals("-warning") || a[0].equals("-hide")) {
195 try {
196 int level = -1;
197 if (a[0].equals("-error")) {
198 level = Errors.ERROR;
199 } else if (a[0].equals("-warning")) {
200 level = Errors.WARNING;
201 } else if (a[0].equals("-hide")) {
202 level = Errors.HIDDEN;
203 }
204 Errors.setErrorLevel(Integer.parseInt(a[1]), level);
205 } catch (NumberFormatException e) {
206 // already printed below
207 return false;
208 }
209 } else if (a[0].equals("-keeplist")) {
210 keepListFile = a[1];
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800211 } else if (a[0].equals("-showAnnotation")) {
212 showAnnotations.add(a[1]);
Alex Klyubincbc60de2014-06-03 16:53:17 -0700213 } else if (a[0].equals("-hidePackage")) {
214 hiddenPackages.add(a[1]);
Jeff Hamilton970f13f2012-06-22 00:21:56 -0500215 } else if (a[0].equals("-proguard")) {
Robert Ly5ab9cb52012-11-30 02:57:14 -0800216 proguardFile = a[1];
Ben Dodson920dbbb2010-08-04 15:21:06 -0700217 } else if (a[0].equals("-proofread")) {
218 proofreadFile = a[1];
219 } else if (a[0].equals("-todo")) {
220 todoFile = a[1];
221 } else if (a[0].equals("-public")) {
222 showLevel = SHOW_PUBLIC;
223 } else if (a[0].equals("-protected")) {
224 showLevel = SHOW_PROTECTED;
225 } else if (a[0].equals("-package")) {
226 showLevel = SHOW_PACKAGE;
227 } else if (a[0].equals("-private")) {
228 showLevel = SHOW_PRIVATE;
229 } else if (a[0].equals("-hidden")) {
230 showLevel = SHOW_HIDDEN;
231 } else if (a[0].equals("-stubs")) {
232 stubsDir = a[1];
233 } else if (a[0].equals("-stubpackages")) {
234 stubPackages = new HashSet<String>();
235 for (String pkg : a[1].split(":")) {
236 stubPackages.add(pkg);
237 }
238 } else if (a[0].equals("-sdkvalues")) {
239 sdkValuePath = a[1];
Joe Onorato04099252011-03-09 13:34:18 -0800240 } else if (a[0].equals("-api")) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700241 apiFile = a[1];
Hui Shu5118ffe2014-02-18 14:06:42 -0800242 } else if (a[0].equals("-removedApi")) {
243 removedApiFile = a[1];
244 }
245 else if (a[0].equals("-nodocs")) {
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700246 generateDocs = false;
Dirk Doughertye65daec2013-04-29 18:44:48 -0700247 } else if (a[0].equals("-nodefaultassets")) {
248 includeDefaultAssets = false;
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700249 } else if (a[0].equals("-parsecomments")) {
250 parseComments = true;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700251 } else if (a[0].equals("-since")) {
252 sinceTagger.addVersion(a[1], a[2]);
253 } else if (a[0].equals("-offlinemode")) {
254 offlineMode = true;
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800255 } else if (a[0].equals("-metadataDebug")) {
256 META_DBG = true;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700257 } else if (a[0].equals("-federate")) {
258 try {
259 String name = a[1];
260 URL federationURL = new URL(a[2]);
Jeff Hamilton1e0d3702012-06-21 04:21:54 -0500261 federationTagger.addSiteUrl(name, federationURL);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700262 } catch (MalformedURLException e) {
263 System.err.println("Could not parse URL for federation: " + a[1]);
264 return false;
265 }
Jeff Hamilton1e0d3702012-06-21 04:21:54 -0500266 } else if (a[0].equals("-federationapi")) {
267 String name = a[1];
268 String file = a[2];
269 federationTagger.addSiteApi(name, file);
Chirag Shah18b25552012-08-09 13:57:54 -0700270 } else if (a[0].equals("-yaml")) {
271 yamlNavFile = a[1];
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800272 } else if (a[0].equals("-devsite")) {
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800273 // Don't copy the doclava assets to devsite output (ie use proj assets only)
274 includeDefaultAssets = false;
275 outputPathHtmlDirs = outputPathHtmlDirs + "/" + devsiteRoot;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700276 }
277 }
278
Joe Onorato7a6456c2010-09-16 12:36:36 -0400279 if (!readKnownTagsFiles(knownTags, knownTagsFiles)) {
280 return false;
281 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700282
283 // Set up the data structures
284 Converter.makeInfo(r);
285
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700286 if (generateDocs) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700287 ClearPage.addBundledTemplateDir("assets/customizations");
288 ClearPage.addBundledTemplateDir("assets/templates");
289
290 List<ResourceLoader> resourceLoaders = new ArrayList<ResourceLoader>();
291 List<String> templates = ClearPage.getTemplateDirs();
292 for (String tmpl : templates) {
293 resourceLoaders.add(new FileSystemResourceLoader(tmpl));
294 }
295
296 templates = ClearPage.getBundledTemplateDirs();
297 for (String tmpl : templates) {
Andrew Sappersteinf959ed12011-06-23 10:24:40 -0700298 // TODO - remove commented line - it's here for debugging purposes
299 // resourceLoaders.add(new FileSystemResourceLoader("/Volumes/Android/master/external/doclava/res/" + tmpl));
Ben Dodson920dbbb2010-08-04 15:21:06 -0700300 resourceLoaders.add(new ClassResourceLoader(Doclava.class, '/'+tmpl));
301 }
302
303 ResourceLoader compositeResourceLoader = new CompositeResourceLoader(resourceLoaders);
304 jSilver = new JSilver(compositeResourceLoader);
305
306 if (!Doclava.readTemplateSettings()) {
307 return false;
308 }
309
Andrew Sappersteinf959ed12011-06-23 10:24:40 -0700310 //startTime = System.nanoTime();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700311
312 // Apply @since tags from the XML file
313 sinceTagger.tagAll(Converter.rootClasses());
Scott Main3c1a6b22010-10-15 17:34:04 -0700314
Ben Dodson920dbbb2010-08-04 15:21:06 -0700315 // Apply details of federated documentation
316 federationTagger.tagAll(Converter.rootClasses());
317
318 // Files for proofreading
319 if (proofreadFile != null) {
320 Proofread.initProofread(proofreadFile);
321 }
322 if (todoFile != null) {
323 TodoFile.writeTodoFile(todoFile);
324 }
325
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800326 if (samplesRef) {
Dirk Dougherty75372292013-11-26 18:45:31 -0800327 // always write samples without offlineMode behaviors
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800328 writeSamples(false, sampleCodes, SORT_BY_NAV_GROUPS);
329 }
Dirk Dougherty75372292013-11-26 18:45:31 -0800330
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800331 // HTML2 Pages -- Generate Pages from optional secondary dir
332 if (!inputPathHtmlDir2.isEmpty()) {
333 if (!outputPathHtmlDir2.isEmpty()) {
334 ClearPage.outputDir = outputPathBase + "/" + outputPathHtmlDir2;
335 }
336 ClearPage.htmlDirs = inputPathHtmlDir2;
337 writeHTMLPages();
338 ClearPage.htmlDirs = inputPathHtmlDirs;
339 }
340
Ben Dodson920dbbb2010-08-04 15:21:06 -0700341 // HTML Pages
Bill Napier4bac50a2010-08-25 18:17:16 -0700342 if (!ClearPage.htmlDirs.isEmpty()) {
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800343 ClearPage.htmlDirs = inputPathHtmlDirs;
344 ClearPage.outputDir = outputPathHtmlDirs;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700345 writeHTMLPages();
346 }
347
348 writeAssets();
Hui Shu5118ffe2014-02-18 14:06:42 -0800349
Ben Dodson920dbbb2010-08-04 15:21:06 -0700350 // Navigation tree
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700351 String refPrefix = new String();
Robert Ly5ab9cb52012-11-30 02:57:14 -0800352 if(gmsRef){
353 refPrefix = "gms-";
354 } else if(gcmRef){
355 refPrefix = "gcm-";
356 }
357 NavTree.writeNavTree(javadocDir, refPrefix);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700358
Chirag Shah18b25552012-08-09 13:57:54 -0700359 // Write yaml tree.
360 if (yamlNavFile != null){
361 NavTree.writeYamlTree(javadocDir, yamlNavFile);
362 }
363
Ben Dodson920dbbb2010-08-04 15:21:06 -0700364 // Packages Pages
Robert Ly5ab9cb52012-11-30 02:57:14 -0800365 writePackages(javadocDir + refPrefix + "packages" + htmlExtension);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700366
367 // Classes
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800368 writeClassLists();
369 writeClasses();
370 writeHierarchy();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700371 // writeKeywords();
372
373 // Lists for JavaScript
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800374 writeLists();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700375 if (keepListFile != null) {
376 writeKeepList(keepListFile);
377 }
378
Ben Dodson920dbbb2010-08-04 15:21:06 -0700379 // Index page
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800380 writeIndex();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700381
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800382 Proofread.finishProofread(proofreadFile);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700383
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800384 if (sdkValuePath != null) {
385 writeSdkValues(sdkValuePath);
386 }
Dirk Dougherty42303cc2013-09-13 19:45:17 -0700387 // Write metadata for all processed files to jd_lists_unified.js in out dir
388 if (!sTaglist.isEmpty()) {
389 PageMetadata.WriteList(sTaglist);
390 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700391 }
392
393 // Stubs
Hui Shu5118ffe2014-02-18 14:06:42 -0800394 if (stubsDir != null || apiFile != null || proguardFile != null || removedApiFile != null) {
395 Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, removedApiFile, stubPackages);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700396 }
397
398 Errors.printErrors();
Scott Main3c1a6b22010-10-15 17:34:04 -0700399
Andrew Sappersteinf959ed12011-06-23 10:24:40 -0700400 long time = System.nanoTime() - startTime;
401 System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to "
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800402 + outputPathBase );
Andrew Sappersteinf959ed12011-06-23 10:24:40 -0700403
Ben Dodson920dbbb2010-08-04 15:21:06 -0700404 return !Errors.hadError;
405 }
406
407 private static void writeIndex() {
408 Data data = makeHDF();
409 ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension);
410 }
411
412 private static boolean readTemplateSettings() {
413 Data data = makeHDF();
414
415 // The .html extension is hard-coded in several .cs files,
416 // and so you cannot currently set it as a property.
417 htmlExtension = ".html";
418 // htmlExtension = data.getValue("template.extension", ".html");
419 int i = 0;
420 while (true) {
421 String k = data.getValue("template.escape." + i + ".key", "");
422 String v = data.getValue("template.escape." + i + ".value", "");
423 if ("".equals(k)) {
424 break;
425 }
426 if (k.length() != 1) {
427 System.err.println("template.escape." + i + ".key must have a length of 1: " + k);
428 return false;
429 }
430 escapeChars.put(k.charAt(0), v);
431 i++;
432 }
433 return true;
434 }
435
Joe Onorato7a6456c2010-09-16 12:36:36 -0400436 private static boolean readKnownTagsFiles(HashSet<String> knownTags,
437 ArrayList<String> knownTagsFiles) {
438 for (String fn: knownTagsFiles) {
439 BufferedReader in = null;
440 try {
441 in = new BufferedReader(new FileReader(fn));
442 int lineno = 0;
443 boolean fail = false;
444 while (true) {
445 lineno++;
446 String line = in.readLine();
447 if (line == null) {
448 break;
449 }
450 line = line.trim();
451 if (line.length() == 0) {
452 continue;
453 } else if (line.charAt(0) == '#') {
454 continue;
455 }
456 String[] words = line.split("\\s+", 2);
457 if (words.length == 2) {
458 if (words[1].charAt(0) != '#') {
459 System.err.println(fn + ":" + lineno
460 + ": Only one tag allowed per line: " + line);
461 fail = true;
462 continue;
463 }
464 }
465 knownTags.add(words[0]);
466 }
467 if (fail) {
468 return false;
469 }
470 } catch (IOException ex) {
471 System.err.println("Error reading file: " + fn + " (" + ex.getMessage() + ")");
472 return false;
473 } finally {
474 if (in != null) {
475 try {
476 in.close();
477 } catch (IOException e) {
478 }
479 }
480 }
481 }
482 return true;
483 }
484
Ben Dodson920dbbb2010-08-04 15:21:06 -0700485 public static String escape(String s) {
486 if (escapeChars.size() == 0) {
487 return s;
488 }
489 StringBuffer b = null;
490 int begin = 0;
491 final int N = s.length();
492 for (int i = 0; i < N; i++) {
493 char c = s.charAt(i);
494 String mapped = escapeChars.get(c);
495 if (mapped != null) {
496 if (b == null) {
497 b = new StringBuffer(s.length() + mapped.length());
498 }
499 if (begin != i) {
500 b.append(s.substring(begin, i));
501 }
502 b.append(mapped);
503 begin = i + 1;
504 }
505 }
506 if (b != null) {
507 if (begin != N) {
508 b.append(s.substring(begin, N));
509 }
510 return b.toString();
511 }
512 return s;
513 }
514
515 public static void setPageTitle(Data data, String title) {
516 String s = title;
517 if (Doclava.title.length() > 0) {
518 s += " - " + Doclava.title;
519 }
520 data.setValue("page.title", s);
521 }
522
523
524 public static LanguageVersion languageVersion() {
525 return LanguageVersion.JAVA_1_5;
526 }
527
528
529 public static int optionLength(String option) {
530 if (option.equals("-d")) {
531 return 2;
532 }
533 if (option.equals("-templatedir")) {
534 return 2;
535 }
536 if (option.equals("-hdf")) {
537 return 3;
538 }
Joe Onorato7a6456c2010-09-16 12:36:36 -0400539 if (option.equals("-knowntags")) {
540 return 2;
541 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700542 if (option.equals("-toroot")) {
543 return 2;
544 }
545 if (option.equals("-samplecode")) {
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700546 samplesRef = true;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700547 return 4;
548 }
Dirk Dougherty815f9342013-09-12 00:30:28 -0700549 if (option.equals("-samplegroup")) {
550 return 2;
551 }
Dirk Dougherty5ce827a2013-11-21 18:08:58 -0800552 if (option.equals("-samplesdir")) {
553 samplesRef = true;
554 return 2;
555 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700556 if (option.equals("-devsite")) {
557 return 1;
558 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700559 if (option.equals("-htmldir")) {
560 return 2;
561 }
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800562 if (option.equals("-htmldir2")) {
563 return 3;
564 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700565 if (option.equals("-title")) {
566 return 2;
567 }
568 if (option.equals("-werror")) {
569 return 1;
570 }
571 if (option.equals("-hide")) {
572 return 2;
573 }
574 if (option.equals("-warning")) {
575 return 2;
576 }
577 if (option.equals("-error")) {
578 return 2;
579 }
580 if (option.equals("-keeplist")) {
581 return 2;
582 }
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800583 if (option.equals("-showAnnotation")) {
584 return 2;
585 }
Alex Klyubincbc60de2014-06-03 16:53:17 -0700586 if (option.equals("-hidePackage")) {
587 return 2;
588 }
Jeff Hamilton970f13f2012-06-22 00:21:56 -0500589 if (option.equals("-proguard")) {
590 return 2;
591 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700592 if (option.equals("-proofread")) {
593 return 2;
594 }
595 if (option.equals("-todo")) {
596 return 2;
597 }
598 if (option.equals("-public")) {
599 return 1;
600 }
601 if (option.equals("-protected")) {
602 return 1;
603 }
604 if (option.equals("-package")) {
605 return 1;
606 }
607 if (option.equals("-private")) {
608 return 1;
609 }
610 if (option.equals("-hidden")) {
611 return 1;
612 }
613 if (option.equals("-stubs")) {
614 return 2;
615 }
616 if (option.equals("-stubpackages")) {
617 return 2;
618 }
619 if (option.equals("-sdkvalues")) {
620 return 2;
621 }
Joe Onorato04099252011-03-09 13:34:18 -0800622 if (option.equals("-api")) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700623 return 2;
624 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800625 if (option.equals("-removedApi")) {
626 return 2;
627 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700628 if (option.equals("-nodocs")) {
629 return 1;
630 }
Dirk Doughertye65daec2013-04-29 18:44:48 -0700631 if (option.equals("-nodefaultassets")) {
632 return 1;
633 }
Ben Dodson9ccd9e32010-08-06 17:18:52 -0700634 if (option.equals("-parsecomments")) {
635 return 1;
636 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700637 if (option.equals("-since")) {
638 return 3;
639 }
640 if (option.equals("-offlinemode")) {
641 return 1;
642 }
643 if (option.equals("-federate")) {
644 return 3;
645 }
Jeff Hamilton1e0d3702012-06-21 04:21:54 -0500646 if (option.equals("-federationapi")) {
647 return 3;
648 }
Chirag Shah18b25552012-08-09 13:57:54 -0700649 if (option.equals("-yaml")) {
650 return 2;
651 }
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800652 if (option.equals("-devsite")) {
653 return 1;
654 }
Robert Ly5ab9cb52012-11-30 02:57:14 -0800655 if (option.equals("-gmsref")) {
656 gmsRef = true;
657 return 1;
658 }
659 if (option.equals("-gcmref")) {
660 gcmRef = true;
661 return 1;
662 }
Dirk Doughertyc770a6e2014-02-07 20:04:27 -0800663 if (option.equals("-metadataDebug")) {
664 return 1;
665 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700666 return 0;
667 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700668 public static boolean validOptions(String[][] options, DocErrorReporter r) {
669 for (String[] a : options) {
670 if (a[0].equals("-error") || a[0].equals("-warning") || a[0].equals("-hide")) {
671 try {
672 Integer.parseInt(a[1]);
673 } catch (NumberFormatException e) {
674 r.printError("bad -" + a[0] + " value must be a number: " + a[1]);
675 return false;
676 }
677 }
678 }
679
680 return true;
681 }
682
683 public static Data makeHDF() {
684 Data data = jSilver.createData();
685
686 for (String[] p : mHDFData) {
687 data.setValue(p[0], p[1]);
688 }
689
690 return data;
691 }
692
693
694
695 public static Data makePackageHDF() {
696 Data data = makeHDF();
697 ClassInfo[] classes = Converter.rootClasses();
698
699 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
700 for (ClassInfo cl : classes) {
701 PackageInfo pkg = cl.containingPackage();
702 String name;
703 if (pkg == null) {
704 name = "";
705 } else {
706 name = pkg.name();
707 }
708 sorted.put(name, pkg);
709 }
710
711 int i = 0;
712 for (String s : sorted.keySet()) {
713 PackageInfo pkg = sorted.get(s);
714
Hui Shu5118ffe2014-02-18 14:06:42 -0800715 if (pkg.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700716 continue;
717 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800718 boolean allHiddenOrRemoved = true;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700719 int pass = 0;
720 ClassInfo[] classesToCheck = null;
Brett Chabot700b9f22013-10-04 16:57:39 -0700721 while (pass < 6) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700722 switch (pass) {
723 case 0:
724 classesToCheck = pkg.ordinaryClasses();
725 break;
726 case 1:
727 classesToCheck = pkg.enums();
728 break;
729 case 2:
730 classesToCheck = pkg.errors();
731 break;
732 case 3:
733 classesToCheck = pkg.exceptions();
734 break;
735 case 4:
736 classesToCheck = pkg.interfaces();
737 break;
Brett Chabot700b9f22013-10-04 16:57:39 -0700738 case 5:
739 classesToCheck = pkg.annotations();
740 break;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700741 default:
742 System.err.println("Error reading package: " + pkg.name());
743 break;
744 }
745 for (ClassInfo cl : classesToCheck) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800746 if (!cl.isHiddenOrRemoved()) {
747 allHiddenOrRemoved = false;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700748 break;
749 }
750 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800751 if (!allHiddenOrRemoved) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700752 break;
753 }
754 pass++;
755 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800756 if (allHiddenOrRemoved) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700757 continue;
758 }
Robert Ly5ab9cb52012-11-30 02:57:14 -0800759 if(gmsRef){
760 data.setValue("reference.gms", "true");
761 } else if(gcmRef){
762 data.setValue("reference.gcm", "true");
763 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700764 data.setValue("reference", "1");
765 data.setValue("reference.apilevels", sinceTagger.hasVersions() ? "1" : "0");
766 data.setValue("docs.packages." + i + ".name", s);
767 data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
768 data.setValue("docs.packages." + i + ".since", pkg.getSince());
769 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr", pkg.firstSentenceTags());
770 i++;
771 }
772
773 sinceTagger.writeVersionNames(data);
774 return data;
775 }
776
777 private static void writeDirectory(File dir, String relative, JSilver js) {
778 File[] files = dir.listFiles();
779 int i, count = files.length;
780 for (i = 0; i < count; i++) {
781 File f = files[i];
782 if (f.isFile()) {
783 String templ = relative + f.getName();
784 int len = templ.length();
785 if (len > 3 && ".cs".equals(templ.substring(len - 3))) {
786 Data data = makeHDF();
787 String filename = templ.substring(0, len - 3) + htmlExtension;
788 ClearPage.write(data, templ, filename, js);
789 } else if (len > 3 && ".jd".equals(templ.substring(len - 3))) {
Dirk Dougherty42303cc2013-09-13 19:45:17 -0700790 Data data = makeHDF();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700791 String filename = templ.substring(0, len - 3) + htmlExtension;
Dirk Dougherty42303cc2013-09-13 19:45:17 -0700792 DocFile.writePage(f.getAbsolutePath(), relative, filename, data);
Robert Ly88c435b2013-02-01 11:55:04 -0800793 } else if(!f.getName().equals(".DS_Store")){
794 Data data = makeHDF();
795 String hdfValue = data.getValue("sac") == null ? "" : data.getValue("sac");
796 boolean allowExcepted = hdfValue.equals("true") ? true : false;
797 ClearPage.copyFile(allowExcepted, f, templ);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700798 }
799 } else if (f.isDirectory()) {
800 writeDirectory(f, relative + f.getName() + "/", js);
801 }
802 }
803 }
804
805 public static void writeHTMLPages() {
Bill Napier4bac50a2010-08-25 18:17:16 -0700806 for (String htmlDir : ClearPage.htmlDirs) {
807 File f = new File(htmlDir);
808 if (!f.isDirectory()) {
809 System.err.println("htmlDir not a directory: " + htmlDir);
810 continue;
811 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700812
Bill Napier4bac50a2010-08-25 18:17:16 -0700813 ResourceLoader loader = new FileSystemResourceLoader(f);
814 JSilver js = new JSilver(loader);
815 writeDirectory(f, "", js);
816 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700817 }
818
819 public static void writeAssets() {
820 JarFile thisJar = JarUtils.jarForClass(Doclava.class, null);
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800821 if ((thisJar != null) && (includeDefaultAssets)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700822 try {
823 List<String> templateDirs = ClearPage.getBundledTemplateDirs();
824 for (String templateDir : templateDirs) {
825 String assetsDir = templateDir + "/assets";
826 JarUtils.copyResourcesToDirectory(thisJar, assetsDir, ClearPage.outputDir + "/assets");
827 }
828 } catch (IOException e) {
829 System.err.println("Error copying assets directory.");
830 e.printStackTrace();
831 return;
832 }
833 }
834
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800835 //write the project-specific assets
Ben Dodson920dbbb2010-08-04 15:21:06 -0700836 List<String> templateDirs = ClearPage.getTemplateDirs();
837 for (String templateDir : templateDirs) {
838 File assets = new File(templateDir + "/assets");
839 if (assets.isDirectory()) {
840 writeDirectory(assets, "assets/", null);
841 }
842 }
Dirk Dougherty9b316c82013-01-28 10:53:33 -0800843
844 // Create the timestamp.js file based on .cs file
Scott Main460dd2a2013-01-29 14:18:45 -0800845 Data timedata = Doclava.makeHDF();
846 ClearPage.write(timedata, "timestamp.cs", "timestamp.js");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700847 }
848
Scott Main71bf8d22013-03-08 18:59:15 -0800849 /** Go through the docs and generate meta-data about each
850 page to use in search suggestions */
Ben Dodson920dbbb2010-08-04 15:21:06 -0700851 public static void writeLists() {
Scott Main71bf8d22013-03-08 18:59:15 -0800852
853 // Write the lists for API references
Ben Dodson920dbbb2010-08-04 15:21:06 -0700854 Data data = makeHDF();
855
856 ClassInfo[] classes = Converter.rootClasses();
857
858 SortedMap<String, Object> sorted = new TreeMap<String, Object>();
859 for (ClassInfo cl : classes) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800860 if (cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700861 continue;
862 }
863 sorted.put(cl.qualifiedName(), cl);
864 PackageInfo pkg = cl.containingPackage();
865 String name;
866 if (pkg == null) {
867 name = "";
868 } else {
869 name = pkg.name();
870 }
871 sorted.put(name, pkg);
872 }
873
874 int i = 0;
875 for (String s : sorted.keySet()) {
876 data.setValue("docs.pages." + i + ".id", "" + i);
877 data.setValue("docs.pages." + i + ".label", s);
878
879 Object o = sorted.get(s);
880 if (o instanceof PackageInfo) {
881 PackageInfo pkg = (PackageInfo) o;
882 data.setValue("docs.pages." + i + ".link", pkg.htmlPage());
883 data.setValue("docs.pages." + i + ".type", "package");
Scott Main68a238a2013-04-30 12:34:19 -0700884 data.setValue("docs.pages." + i + ".deprecated", pkg.isDeprecated() ? "true" : "false");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700885 } else if (o instanceof ClassInfo) {
886 ClassInfo cl = (ClassInfo) o;
887 data.setValue("docs.pages." + i + ".link", cl.htmlPage());
888 data.setValue("docs.pages." + i + ".type", "class");
Scott Main68a238a2013-04-30 12:34:19 -0700889 data.setValue("docs.pages." + i + ".deprecated", cl.isDeprecated() ? "true" : "false");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700890 }
891 i++;
892 }
Robert Ly57657b92013-02-21 02:00:37 +0000893 ClearPage.write(data, "lists.cs", javadocDir + "lists.js");
Scott Main71bf8d22013-03-08 18:59:15 -0800894
895
Scott Main77918022013-05-21 11:46:35 -0700896 // Write the lists for JD documents (if there are HTML directories to process)
897 if (inputPathHtmlDirs.size() > 0) {
898 Data jddata = makeHDF();
899 Iterator counter = new Iterator();
900 for (String htmlDir : inputPathHtmlDirs) {
901 File dir = new File(htmlDir);
902 if (!dir.isDirectory()) {
903 continue;
904 }
905 writeJdDirList(dir, jddata, counter);
Scott Main71bf8d22013-03-08 18:59:15 -0800906 }
Scott Main77918022013-05-21 11:46:35 -0700907 ClearPage.write(jddata, "jd_lists.cs", javadocDir + "jd_lists.js");
Scott Main71bf8d22013-03-08 18:59:15 -0800908 }
Scott Main71bf8d22013-03-08 18:59:15 -0800909 }
910
911 private static class Iterator {
912 int i = 0;
913 }
914
915 /** Write meta-data for a JD file, used for search suggestions */
916 private static void writeJdDirList(File dir, Data data, Iterator counter) {
917 File[] files = dir.listFiles();
918 int i, count = files.length;
919 // Loop all files in given directory
920 for (i = 0; i < count; i++) {
921 File f = files[i];
922 if (f.isFile()) {
923 String filePath = f.getAbsolutePath();
924 String templ = f.getName();
925 int len = templ.length();
926 // If it's a .jd file we want to process
927 if (len > 3 && ".jd".equals(templ.substring(len - 3))) {
928 // remove the directories below the site root
Hui Shu5118ffe2014-02-18 14:06:42 -0800929 String webPath = filePath.substring(filePath.indexOf("docs/html/") + 10,
930 filePath.length());
Scott Main71bf8d22013-03-08 18:59:15 -0800931 // replace .jd with .html
932 webPath = webPath.substring(0, webPath.length() - 3) + htmlExtension;
933 // Parse the .jd file for properties data at top of page
934 Data hdf = Doclava.makeHDF();
935 String filedata = DocFile.readFile(filePath);
936 Matcher lines = DocFile.LINE.matcher(filedata);
937 String line = null;
938 // Get each line to add the key-value to hdf
939 while (lines.find()) {
940 line = lines.group(1);
941 if (line.length() > 0) {
942 // Stop when we hit the body
943 if (line.equals("@jd:body")) {
944 break;
945 }
946 Matcher prop = DocFile.PROP.matcher(line);
947 if (prop.matches()) {
948 String key = prop.group(1);
949 String value = prop.group(2);
950 hdf.setValue(key, value);
951 } else {
952 break;
953 }
954 }
955 } // done gathering page properties
956
957 // Insert the goods into HDF data (title, link, tags, type)
958 String title = hdf.getValue("page.title", "");
Scott Main9950dd02013-04-11 19:03:58 -0700959 title = title.replaceAll("\"", "'");
960 // if there's a <span> in the title, get rid of it
961 if (title.indexOf("<span") != -1) {
962 String[] splitTitle = title.split("<span(.*?)</span>");
963 title = splitTitle[0];
964 for (int j = 1; j < splitTitle.length; j++) {
965 title.concat(splitTitle[j]);
966 }
967 }
Dirk Doughertyb4d562b2013-09-06 13:55:20 -0700968
969 StringBuilder tags = new StringBuilder();
Dirk Dougherty42303cc2013-09-13 19:45:17 -0700970 String tagsList = hdf.getValue("page.tags", "");
971 if (!tagsList.equals("")) {
972 tagsList = tagsList.replaceAll("\"", "");
973 String[] tagParts = tagsList.split(",");
Dirk Doughertyb4d562b2013-09-06 13:55:20 -0700974 for (int iter = 0; iter < tagParts.length; iter++) {
975 tags.append("\"");
976 tags.append(tagParts[iter].trim());
977 tags.append("\"");
978 if (iter < tagParts.length - 1) {
979 tags.append(",");
980 }
981 }
982 }
983
Scott Main71bf8d22013-03-08 18:59:15 -0800984 String dirName = (webPath.indexOf("/") != -1)
985 ? webPath.substring(0, webPath.indexOf("/")) : "";
986
Scott Main489864e2013-04-12 11:20:51 -0700987 if (!"".equals(title) &&
988 !"intl".equals(dirName) &&
989 !hdf.getBooleanValue("excludeFromSuggestions")) {
Scott Main9950dd02013-04-11 19:03:58 -0700990 data.setValue("docs.pages." + counter.i + ".label", title);
Scott Main71bf8d22013-03-08 18:59:15 -0800991 data.setValue("docs.pages." + counter.i + ".link", webPath);
Dirk Doughertyb4d562b2013-09-06 13:55:20 -0700992 data.setValue("docs.pages." + counter.i + ".tags", tags.toString());
Scott Main71bf8d22013-03-08 18:59:15 -0800993 data.setValue("docs.pages." + counter.i + ".type", dirName);
994 counter.i++;
995 }
996 }
997 } else if (f.isDirectory()) {
998 writeJdDirList(f, data, counter);
999 }
1000 }
Ben Dodson920dbbb2010-08-04 15:21:06 -07001001 }
1002
1003 public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) {
1004 if (!notStrippable.add(cl)) {
1005 // slight optimization: if it already contains cl, it already contains
1006 // all of cl's parents
1007 return;
1008 }
1009 ClassInfo supr = cl.superclass();
1010 if (supr != null) {
1011 cantStripThis(supr, notStrippable);
1012 }
1013 for (ClassInfo iface : cl.interfaces()) {
1014 cantStripThis(iface, notStrippable);
1015 }
1016 }
1017
1018 private static String getPrintableName(ClassInfo cl) {
1019 ClassInfo containingClass = cl.containingClass();
1020 if (containingClass != null) {
1021 // This is an inner class.
1022 String baseName = cl.name();
1023 baseName = baseName.substring(baseName.lastIndexOf('.') + 1);
1024 return getPrintableName(containingClass) + '$' + baseName;
1025 }
1026 return cl.qualifiedName();
1027 }
1028
1029 /**
1030 * Writes the list of classes that must be present in order to provide the non-hidden APIs known
1031 * to javadoc.
Scott Main3c1a6b22010-10-15 17:34:04 -07001032 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001033 * @param filename the path to the file to write the list to
1034 */
1035 public static void writeKeepList(String filename) {
1036 HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
1037 ClassInfo[] all = Converter.allClasses();
1038 Arrays.sort(all); // just to make the file a little more readable
1039
1040 // If a class is public and not hidden, then it and everything it derives
1041 // from cannot be stripped. Otherwise we can strip it.
1042 for (ClassInfo cl : all) {
Hui Shu5118ffe2014-02-18 14:06:42 -08001043 if (cl.isPublic() && !cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001044 cantStripThis(cl, notStrippable);
1045 }
1046 }
1047 PrintStream stream = null;
1048 try {
Brian Carlstromed8f7972011-06-10 11:10:48 -07001049 stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(filename)));
Ben Dodson920dbbb2010-08-04 15:21:06 -07001050 for (ClassInfo cl : notStrippable) {
1051 stream.println(getPrintableName(cl));
1052 }
1053 } catch (FileNotFoundException e) {
1054 System.err.println("error writing file: " + filename);
1055 } finally {
1056 if (stream != null) {
1057 stream.close();
1058 }
1059 }
1060 }
1061
1062 private static PackageInfo[] sVisiblePackages = null;
1063
1064 public static PackageInfo[] choosePackages() {
1065 if (sVisiblePackages != null) {
1066 return sVisiblePackages;
1067 }
1068
1069 ClassInfo[] classes = Converter.rootClasses();
1070 SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
1071 for (ClassInfo cl : classes) {
1072 PackageInfo pkg = cl.containingPackage();
1073 String name;
1074 if (pkg == null) {
1075 name = "";
1076 } else {
1077 name = pkg.name();
1078 }
1079 sorted.put(name, pkg);
1080 }
1081
1082 ArrayList<PackageInfo> result = new ArrayList<PackageInfo>();
1083
1084 for (String s : sorted.keySet()) {
1085 PackageInfo pkg = sorted.get(s);
1086
Hui Shu5118ffe2014-02-18 14:06:42 -08001087 if (pkg.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001088 continue;
1089 }
Hui Shu5118ffe2014-02-18 14:06:42 -08001090
1091 boolean allHiddenOrRemoved = true;
Ben Dodson920dbbb2010-08-04 15:21:06 -07001092 int pass = 0;
1093 ClassInfo[] classesToCheck = null;
Brett Chabot700b9f22013-10-04 16:57:39 -07001094 while (pass < 6) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001095 switch (pass) {
1096 case 0:
1097 classesToCheck = pkg.ordinaryClasses();
1098 break;
1099 case 1:
1100 classesToCheck = pkg.enums();
1101 break;
1102 case 2:
1103 classesToCheck = pkg.errors();
1104 break;
1105 case 3:
1106 classesToCheck = pkg.exceptions();
1107 break;
1108 case 4:
1109 classesToCheck = pkg.interfaces();
1110 break;
Brett Chabot700b9f22013-10-04 16:57:39 -07001111 case 5:
1112 classesToCheck = pkg.annotations();
1113 break;
Ben Dodson920dbbb2010-08-04 15:21:06 -07001114 default:
1115 System.err.println("Error reading package: " + pkg.name());
1116 break;
1117 }
1118 for (ClassInfo cl : classesToCheck) {
Hui Shu5118ffe2014-02-18 14:06:42 -08001119 if (!cl.isHiddenOrRemoved()) {
1120 allHiddenOrRemoved = false;
Ben Dodson920dbbb2010-08-04 15:21:06 -07001121 break;
1122 }
1123 }
Hui Shu5118ffe2014-02-18 14:06:42 -08001124 if (!allHiddenOrRemoved) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001125 break;
1126 }
1127 pass++;
1128 }
Hui Shu5118ffe2014-02-18 14:06:42 -08001129 if (allHiddenOrRemoved) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001130 continue;
1131 }
1132
1133 result.add(pkg);
1134 }
1135
1136 sVisiblePackages = result.toArray(new PackageInfo[result.size()]);
1137 return sVisiblePackages;
1138 }
1139
1140 public static void writePackages(String filename) {
1141 Data data = makePackageHDF();
1142
1143 int i = 0;
1144 for (PackageInfo pkg : choosePackages()) {
1145 writePackage(pkg);
1146
1147 data.setValue("docs.packages." + i + ".name", pkg.name());
1148 data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
1149 TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr", pkg.firstSentenceTags());
1150
1151 i++;
1152 }
1153
1154 setPageTitle(data, "Package Index");
1155
1156 TagInfo.makeHDF(data, "root.descr", Converter.convertTags(root.inlineTags(), null));
1157
1158 ClearPage.write(data, "packages.cs", filename);
1159 ClearPage.write(data, "package-list.cs", javadocDir + "package-list");
1160
1161 Proofread.writePackages(filename, Converter.convertTags(root.inlineTags(), null));
1162 }
1163
1164 public static void writePackage(PackageInfo pkg) {
1165 // these this and the description are in the same directory,
1166 // so it's okay
1167 Data data = makePackageHDF();
1168
1169 String name = pkg.name();
1170
1171 data.setValue("package.name", name);
1172 data.setValue("package.since", pkg.getSince());
1173 data.setValue("package.descr", "...description...");
1174 pkg.setFederatedReferences(data, "package");
1175
Brett Chabot700b9f22013-10-04 16:57:39 -07001176 makeClassListHDF(data, "package.annotations", ClassInfo.sortByName(pkg.annotations()));
Ben Dodson920dbbb2010-08-04 15:21:06 -07001177 makeClassListHDF(data, "package.interfaces", ClassInfo.sortByName(pkg.interfaces()));
1178 makeClassListHDF(data, "package.classes", ClassInfo.sortByName(pkg.ordinaryClasses()));
1179 makeClassListHDF(data, "package.enums", ClassInfo.sortByName(pkg.enums()));
1180 makeClassListHDF(data, "package.exceptions", ClassInfo.sortByName(pkg.exceptions()));
1181 makeClassListHDF(data, "package.errors", ClassInfo.sortByName(pkg.errors()));
1182 TagInfo.makeHDF(data, "package.shortDescr", pkg.firstSentenceTags());
1183 TagInfo.makeHDF(data, "package.descr", pkg.inlineTags());
1184
1185 String filename = pkg.htmlPage();
1186 setPageTitle(data, name);
1187 ClearPage.write(data, "package.cs", filename);
1188
Ben Dodson920dbbb2010-08-04 15:21:06 -07001189 Proofread.writePackage(filename, pkg.inlineTags());
1190 }
1191
1192 public static void writeClassLists() {
1193 int i;
1194 Data data = makePackageHDF();
1195
Hui Shu5118ffe2014-02-18 14:06:42 -08001196 ClassInfo[] classes = PackageInfo.filterHiddenAndRemoved(
1197 Converter.convertClasses(root.classes()));
Ben Dodson920dbbb2010-08-04 15:21:06 -07001198 if (classes.length == 0) {
1199 return;
1200 }
1201
1202 Sorter[] sorted = new Sorter[classes.length];
1203 for (i = 0; i < sorted.length; i++) {
1204 ClassInfo cl = classes[i];
1205 String name = cl.name();
1206 sorted[i] = new Sorter(name, cl);
1207 }
1208
1209 Arrays.sort(sorted);
1210
1211 // make a pass and resolve ones that have the same name
1212 int firstMatch = 0;
1213 String lastName = sorted[0].label;
1214 for (i = 1; i < sorted.length; i++) {
1215 String s = sorted[i].label;
1216 if (!lastName.equals(s)) {
1217 if (firstMatch != i - 1) {
1218 // there were duplicates
1219 for (int j = firstMatch; j < i; j++) {
1220 PackageInfo pkg = ((ClassInfo) sorted[j].data).containingPackage();
1221 if (pkg != null) {
1222 sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
1223 }
1224 }
1225 }
1226 firstMatch = i;
1227 lastName = s;
1228 }
1229 }
1230
1231 // and sort again
1232 Arrays.sort(sorted);
1233
1234 for (i = 0; i < sorted.length; i++) {
1235 String s = sorted[i].label;
1236 ClassInfo cl = (ClassInfo) sorted[i].data;
1237 char first = Character.toUpperCase(s.charAt(0));
1238 cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i);
1239 }
1240
1241 setPageTitle(data, "Class Index");
1242 ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension);
1243 }
1244
1245 // we use the word keywords because "index" means something else in html land
1246 // the user only ever sees the word index
1247 /*
1248 * public static void writeKeywords() { ArrayList<KeywordEntry> keywords = new
1249 * ArrayList<KeywordEntry>();
Scott Main3c1a6b22010-10-15 17:34:04 -07001250 *
Hui Shu5118ffe2014-02-18 14:06:42 -08001251 * ClassInfo[] classes = PackageInfo.filterHiddenAndRemoved(Converter.convertClasses(root.classes()));
Scott Main3c1a6b22010-10-15 17:34:04 -07001252 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001253 * for (ClassInfo cl: classes) { cl.makeKeywordEntries(keywords); }
Scott Main3c1a6b22010-10-15 17:34:04 -07001254 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001255 * HDF data = makeHDF();
Scott Main3c1a6b22010-10-15 17:34:04 -07001256 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001257 * Collections.sort(keywords);
Scott Main3c1a6b22010-10-15 17:34:04 -07001258 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001259 * int i=0; for (KeywordEntry entry: keywords) { String base = "keywords." + entry.firstChar() +
1260 * "." + i; entry.makeHDF(data, base); i++; }
Scott Main3c1a6b22010-10-15 17:34:04 -07001261 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001262 * setPageTitle(data, "Index"); ClearPage.write(data, "keywords.cs", javadocDir + "keywords" +
1263 * htmlExtension); }
1264 */
1265
1266 public static void writeHierarchy() {
1267 ClassInfo[] classes = Converter.rootClasses();
1268 ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
1269 for (ClassInfo cl : classes) {
Hui Shu5118ffe2014-02-18 14:06:42 -08001270 if (!cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001271 info.add(cl);
1272 }
1273 }
1274 Data data = makePackageHDF();
1275 Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()]));
1276 setPageTitle(data, "Class Hierarchy");
1277 ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension);
1278 }
1279
1280 public static void writeClasses() {
1281 ClassInfo[] classes = Converter.rootClasses();
1282
1283 for (ClassInfo cl : classes) {
1284 Data data = makePackageHDF();
Hui Shu5118ffe2014-02-18 14:06:42 -08001285 if (!cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001286 writeClass(cl, data);
1287 }
1288 }
1289 }
1290
1291 public static void writeClass(ClassInfo cl, Data data) {
1292 cl.makeHDF(data);
Ben Dodson920dbbb2010-08-04 15:21:06 -07001293 setPageTitle(data, cl.name());
Dirk Dougherty9b316c82013-01-28 10:53:33 -08001294 String outfile = cl.htmlPage();
1295 ClearPage.write(data, "class.cs", outfile);
Ben Dodson920dbbb2010-08-04 15:21:06 -07001296 Proofread.writeClass(cl.htmlPage(), cl);
1297 }
1298
1299 public static void makeClassListHDF(Data data, String base, ClassInfo[] classes) {
1300 for (int i = 0; i < classes.length; i++) {
1301 ClassInfo cl = classes[i];
Hui Shu5118ffe2014-02-18 14:06:42 -08001302 if (!cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001303 cl.makeShortDescrHDF(data, base + "." + i);
1304 }
1305 }
1306 }
1307
1308 public static String linkTarget(String source, String target) {
1309 String[] src = source.split("/");
1310 String[] tgt = target.split("/");
1311
1312 int srclen = src.length;
1313 int tgtlen = tgt.length;
1314
1315 int same = 0;
1316 while (same < (srclen - 1) && same < (tgtlen - 1) && (src[same].equals(tgt[same]))) {
1317 same++;
1318 }
1319
1320 String s = "";
1321
1322 int up = srclen - same - 1;
1323 for (int i = 0; i < up; i++) {
1324 s += "../";
1325 }
1326
1327
1328 int N = tgtlen - 1;
1329 for (int i = same; i < N; i++) {
1330 s += tgt[i] + '/';
1331 }
1332 s += tgt[tgtlen - 1];
1333
1334 return s;
1335 }
1336
1337 /**
Hui Shu5118ffe2014-02-18 14:06:42 -08001338 * Returns true if the given element has an @hide, @removed or @pending annotation.
Ben Dodson920dbbb2010-08-04 15:21:06 -07001339 */
Hui Shu5118ffe2014-02-18 14:06:42 -08001340 private static boolean hasHideOrRemovedAnnotation(Doc doc) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001341 String comment = doc.getRawCommentText();
Hui Shu5118ffe2014-02-18 14:06:42 -08001342 return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1 ||
1343 comment.indexOf("@removed") != -1;
Ben Dodson920dbbb2010-08-04 15:21:06 -07001344 }
1345
1346 /**
1347 * Returns true if the given element is hidden.
1348 */
Hui Shu5118ffe2014-02-18 14:06:42 -08001349 private static boolean isHiddenOrRemoved(Doc doc) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001350 // Methods, fields, constructors.
1351 if (doc instanceof MemberDoc) {
Hui Shu5118ffe2014-02-18 14:06:42 -08001352 return hasHideOrRemovedAnnotation(doc);
Ben Dodson920dbbb2010-08-04 15:21:06 -07001353 }
1354
1355 // Classes, interfaces, enums, annotation types.
1356 if (doc instanceof ClassDoc) {
1357 ClassDoc classDoc = (ClassDoc) doc;
1358
1359 // Check the containing package.
Hui Shu5118ffe2014-02-18 14:06:42 -08001360 if (hasHideOrRemovedAnnotation(classDoc.containingPackage())) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001361 return true;
1362 }
1363
1364 // Check the class doc and containing class docs if this is a
1365 // nested class.
1366 ClassDoc current = classDoc;
1367 do {
Hui Shu5118ffe2014-02-18 14:06:42 -08001368 if (hasHideOrRemovedAnnotation(current)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001369 return true;
1370 }
1371
1372 current = current.containingClass();
1373 } while (current != null);
1374 }
1375
1376 return false;
1377 }
1378
1379 /**
Hui Shu5118ffe2014-02-18 14:06:42 -08001380 * Filters out hidden and removed elements.
Ben Dodson920dbbb2010-08-04 15:21:06 -07001381 */
Hui Shu5118ffe2014-02-18 14:06:42 -08001382 private static Object filterHiddenAndRemoved(Object o, Class<?> expected) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001383 if (o == null) {
1384 return null;
1385 }
1386
1387 Class type = o.getClass();
1388 if (type.getName().startsWith("com.sun.")) {
1389 // TODO: Implement interfaces from superclasses, too.
1390 return Proxy
1391 .newProxyInstance(type.getClassLoader(), type.getInterfaces(), new HideHandler(o));
1392 } else if (o instanceof Object[]) {
1393 Class<?> componentType = expected.getComponentType();
1394 Object[] array = (Object[]) o;
1395 List<Object> list = new ArrayList<Object>(array.length);
1396 for (Object entry : array) {
Hui Shu5118ffe2014-02-18 14:06:42 -08001397 if ((entry instanceof Doc) && isHiddenOrRemoved((Doc) entry)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001398 continue;
1399 }
Hui Shu5118ffe2014-02-18 14:06:42 -08001400 list.add(filterHiddenAndRemoved(entry, componentType));
Ben Dodson920dbbb2010-08-04 15:21:06 -07001401 }
1402 return list.toArray((Object[]) Array.newInstance(componentType, list.size()));
1403 } else {
1404 return o;
1405 }
1406 }
1407
1408 /**
1409 * Filters hidden elements out of method return values.
1410 */
1411 private static class HideHandler implements InvocationHandler {
1412
1413 private final Object target;
1414
1415 public HideHandler(Object target) {
1416 this.target = target;
1417 }
1418
1419 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
1420 String methodName = method.getName();
1421 if (args != null) {
1422 if (methodName.equals("compareTo") || methodName.equals("equals")
1423 || methodName.equals("overrides") || methodName.equals("subclassOf")) {
1424 args[0] = unwrap(args[0]);
1425 }
1426 }
1427
1428 if (methodName.equals("getRawCommentText")) {
1429 return filterComment((String) method.invoke(target, args));
1430 }
1431
1432 // escape "&" in disjunctive types.
1433 if (proxy instanceof Type && methodName.equals("toString")) {
1434 return ((String) method.invoke(target, args)).replace("&", "&amp;");
1435 }
1436
1437 try {
Hui Shu5118ffe2014-02-18 14:06:42 -08001438 return filterHiddenAndRemoved(method.invoke(target, args), method.getReturnType());
Ben Dodson920dbbb2010-08-04 15:21:06 -07001439 } catch (InvocationTargetException e) {
1440 throw e.getTargetException();
1441 }
1442 }
1443
1444 private String filterComment(String s) {
1445 if (s == null) {
1446 return null;
1447 }
1448
1449 s = s.trim();
1450
1451 // Work around off by one error
1452 while (s.length() >= 5 && s.charAt(s.length() - 5) == '{') {
1453 s += "&nbsp;";
1454 }
1455
1456 return s;
1457 }
1458
1459 private static Object unwrap(Object proxy) {
1460 if (proxy instanceof Proxy) return ((HideHandler) Proxy.getInvocationHandler(proxy)).target;
1461 return proxy;
1462 }
1463 }
1464
1465 /**
1466 * Collect the values used by the Dev tools and write them in files packaged with the SDK
Scott Main3c1a6b22010-10-15 17:34:04 -07001467 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001468 * @param output the ouput directory for the files.
1469 */
1470 private static void writeSdkValues(String output) {
1471 ArrayList<String> activityActions = new ArrayList<String>();
1472 ArrayList<String> broadcastActions = new ArrayList<String>();
1473 ArrayList<String> serviceActions = new ArrayList<String>();
1474 ArrayList<String> categories = new ArrayList<String>();
1475 ArrayList<String> features = new ArrayList<String>();
1476
1477 ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
1478 ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
1479 ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
1480
1481 ClassInfo[] classes = Converter.allClasses();
1482
Deepanshu Guptadf8d48b2014-06-10 15:04:58 -07001483 // The topmost LayoutParams class - android.view.ViewGroup.LayoutParams
1484 ClassInfo topLayoutParams = null;
1485
Ben Dodson920dbbb2010-08-04 15:21:06 -07001486 // Go through all the fields of all the classes, looking SDK stuff.
1487 for (ClassInfo clazz : classes) {
1488
1489 // first check constant fields for the SdkConstant annotation.
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001490 ArrayList<FieldInfo> fields = clazz.allSelfFields();
Ben Dodson920dbbb2010-08-04 15:21:06 -07001491 for (FieldInfo field : fields) {
1492 Object cValue = field.constantValue();
1493 if (cValue != null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001494 ArrayList<AnnotationInstanceInfo> annotations = field.annotations();
1495 if (!annotations.isEmpty()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001496 for (AnnotationInstanceInfo annotation : annotations) {
1497 if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001498 if (!annotation.elementValues().isEmpty()) {
1499 String type = annotation.elementValues().get(0).valueString();
Ben Dodson920dbbb2010-08-04 15:21:06 -07001500 if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) {
1501 activityActions.add(cValue.toString());
1502 } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) {
1503 broadcastActions.add(cValue.toString());
1504 } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) {
1505 serviceActions.add(cValue.toString());
1506 } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) {
1507 categories.add(cValue.toString());
1508 } else if (SDK_CONSTANT_TYPE_FEATURE.equals(type)) {
1509 features.add(cValue.toString());
1510 }
1511 }
1512 break;
1513 }
1514 }
1515 }
1516 }
1517 }
1518
1519 // Now check the class for @Widget or if its in the android.widget package
1520 // (unless the class is hidden or abstract, or non public)
Hui Shu5118ffe2014-02-18 14:06:42 -08001521 if (clazz.isHiddenOrRemoved() == false && clazz.isPublic() && clazz.isAbstract() == false) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001522 boolean annotated = false;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001523 ArrayList<AnnotationInstanceInfo> annotations = clazz.annotations();
1524 if (!annotations.isEmpty()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001525 for (AnnotationInstanceInfo annotation : annotations) {
1526 if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) {
1527 widgets.add(clazz);
1528 annotated = true;
1529 break;
1530 } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) {
1531 layouts.add(clazz);
1532 annotated = true;
1533 break;
1534 }
1535 }
1536 }
1537
1538 if (annotated == false) {
Deepanshu Guptadf8d48b2014-06-10 15:04:58 -07001539 if (topLayoutParams == null
1540 && "android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
1541 topLayoutParams = clazz;
1542 }
1543 // let's check if this is inside android.widget or android.view
1544 if (isIncludedPackage(clazz)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001545 // now we check what this class inherits either from android.view.ViewGroup
1546 // or android.view.View, or android.view.ViewGroup.LayoutParams
1547 int type = checkInheritance(clazz);
1548 switch (type) {
1549 case TYPE_WIDGET:
1550 widgets.add(clazz);
1551 break;
1552 case TYPE_LAYOUT:
1553 layouts.add(clazz);
1554 break;
1555 case TYPE_LAYOUT_PARAM:
1556 layoutParams.add(clazz);
1557 break;
1558 }
1559 }
1560 }
1561 }
1562 }
1563
1564 // now write the files, whether or not the list are empty.
1565 // the SDK built requires those files to be present.
1566
1567 Collections.sort(activityActions);
1568 writeValues(output + "/activity_actions.txt", activityActions);
1569
1570 Collections.sort(broadcastActions);
1571 writeValues(output + "/broadcast_actions.txt", broadcastActions);
1572
1573 Collections.sort(serviceActions);
1574 writeValues(output + "/service_actions.txt", serviceActions);
1575
1576 Collections.sort(categories);
1577 writeValues(output + "/categories.txt", categories);
1578
1579 Collections.sort(features);
1580 writeValues(output + "/features.txt", features);
1581
1582 // before writing the list of classes, we do some checks, to make sure the layout params
1583 // are enclosed by a layout class (and not one that has been declared as a widget)
1584 for (int i = 0; i < layoutParams.size();) {
Deepanshu Guptadf8d48b2014-06-10 15:04:58 -07001585 ClassInfo clazz = layoutParams.get(i);
1586 ClassInfo containingClass = clazz.containingClass();
1587 boolean remove = containingClass == null || layouts.indexOf(containingClass) == -1;
1588 // Also ensure that super classes of the layout params are in android.widget or android.view.
1589 while (!remove && (clazz = clazz.superclass()) != null && !clazz.equals(topLayoutParams)) {
1590 remove = !isIncludedPackage(clazz);
1591 }
1592 if (remove) {
Ben Dodson920dbbb2010-08-04 15:21:06 -07001593 layoutParams.remove(i);
1594 } else {
1595 i++;
1596 }
1597 }
1598
1599 writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
1600 }
1601
1602 /**
Deepanshu Guptadf8d48b2014-06-10 15:04:58 -07001603 * Check if the clazz is in package android.view or android.widget
1604 */
1605 private static boolean isIncludedPackage(ClassInfo clazz) {
1606 String pckg = clazz.containingPackage().name();
1607 return "android.widget".equals(pckg) || "android.view".equals(pckg);
1608 }
1609
1610 /**
Ben Dodson920dbbb2010-08-04 15:21:06 -07001611 * Writes a list of values into a text files.
Scott Main3c1a6b22010-10-15 17:34:04 -07001612 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001613 * @param pathname the absolute os path of the output file.
1614 * @param values the list of values to write.
1615 */
1616 private static void writeValues(String pathname, ArrayList<String> values) {
1617 FileWriter fw = null;
1618 BufferedWriter bw = null;
1619 try {
1620 fw = new FileWriter(pathname, false);
1621 bw = new BufferedWriter(fw);
1622
1623 for (String value : values) {
1624 bw.append(value).append('\n');
1625 }
1626 } catch (IOException e) {
1627 // pass for now
1628 } finally {
1629 try {
1630 if (bw != null) bw.close();
1631 } catch (IOException e) {
1632 // pass for now
1633 }
1634 try {
1635 if (fw != null) fw.close();
1636 } catch (IOException e) {
1637 // pass for now
1638 }
1639 }
1640 }
1641
1642 /**
1643 * Writes the widget/layout/layout param classes into a text files.
Scott Main3c1a6b22010-10-15 17:34:04 -07001644 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001645 * @param pathname the absolute os path of the output file.
1646 * @param widgets the list of widget classes to write.
1647 * @param layouts the list of layout classes to write.
1648 * @param layoutParams the list of layout param classes to write.
1649 */
1650 private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets,
1651 ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) {
1652 FileWriter fw = null;
1653 BufferedWriter bw = null;
1654 try {
1655 fw = new FileWriter(pathname, false);
1656 bw = new BufferedWriter(fw);
1657
1658 // write the 3 types of classes.
1659 for (ClassInfo clazz : widgets) {
1660 writeClass(bw, clazz, 'W');
1661 }
1662 for (ClassInfo clazz : layoutParams) {
1663 writeClass(bw, clazz, 'P');
1664 }
1665 for (ClassInfo clazz : layouts) {
1666 writeClass(bw, clazz, 'L');
1667 }
1668 } catch (IOException e) {
1669 // pass for now
1670 } finally {
1671 try {
1672 if (bw != null) bw.close();
1673 } catch (IOException e) {
1674 // pass for now
1675 }
1676 try {
1677 if (fw != null) fw.close();
1678 } catch (IOException e) {
1679 // pass for now
1680 }
1681 }
1682 }
1683
1684 /**
1685 * Writes a class name and its super class names into a {@link BufferedWriter}.
Scott Main3c1a6b22010-10-15 17:34:04 -07001686 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001687 * @param writer the BufferedWriter to write into
1688 * @param clazz the class to write
1689 * @param prefix the prefix to put at the beginning of the line.
1690 * @throws IOException
1691 */
1692 private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)
1693 throws IOException {
1694 writer.append(prefix).append(clazz.qualifiedName());
1695 ClassInfo superClass = clazz;
1696 while ((superClass = superClass.superclass()) != null) {
1697 writer.append(' ').append(superClass.qualifiedName());
1698 }
1699 writer.append('\n');
1700 }
1701
1702 /**
1703 * Checks the inheritance of {@link ClassInfo} objects. This method return
1704 * <ul>
1705 * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li>
1706 * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
1707 * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends
1708 * <code>android.view.ViewGroup$LayoutParams</code></li>
1709 * <li>{@link #TYPE_NONE}: in all other cases</li>
1710 * </ul>
Scott Main3c1a6b22010-10-15 17:34:04 -07001711 *
Ben Dodson920dbbb2010-08-04 15:21:06 -07001712 * @param clazz the {@link ClassInfo} to check.
1713 */
1714 private static int checkInheritance(ClassInfo clazz) {
1715 if ("android.view.ViewGroup".equals(clazz.qualifiedName())) {
1716 return TYPE_LAYOUT;
1717 } else if ("android.view.View".equals(clazz.qualifiedName())) {
1718 return TYPE_WIDGET;
1719 } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
1720 return TYPE_LAYOUT_PARAM;
1721 }
1722
1723 ClassInfo parent = clazz.superclass();
1724 if (parent != null) {
1725 return checkInheritance(parent);
1726 }
1727
1728 return TYPE_NONE;
1729 }
Chirag Shah18b25552012-08-09 13:57:54 -07001730
1731 /**
1732 * Ensures a trailing '/' at the end of a string.
1733 */
1734 static String ensureSlash(String path) {
1735 return path.endsWith("/") ? path : path + "/";
1736 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -07001737
1738 /**
Dirk Dougherty75372292013-11-26 18:45:31 -08001739 * Process sample projects. Generate the TOC for the samples groups and project
1740 * and write it to a cs var, which is then written to files during templating to
1741 * html output. Collect metadata from sample project _index.jd files. Copy html
1742 * and specific source file types to the output directory.
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -07001743 */
Dirk Dougherty815f9342013-09-12 00:30:28 -07001744 public static void writeSamples(boolean offlineMode, ArrayList<SampleCode> sampleCodes,
1745 boolean sortNavByGroups) {
Dirk Dougherty75372292013-11-26 18:45:31 -08001746 samplesNavTree = makeHDF();
1747
1748 // Go through samples processing files. Create a root list for SC nodes,
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -07001749 // pass it to SCs for their NavTree children and append them.
1750 List<SampleCode.Node> samplesList = new ArrayList<SampleCode.Node>();
Dirk Dougherty815f9342013-09-12 00:30:28 -07001751 List<SampleCode.Node> sampleGroupsRootNodes = null;
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -07001752 for (SampleCode sc : sampleCodes) {
Dirk Dougherty75372292013-11-26 18:45:31 -08001753 samplesList.add(sc.setSamplesTOC(offlineMode));
1754 }
Dirk Dougherty815f9342013-09-12 00:30:28 -07001755 if (sortNavByGroups) {
1756 sampleGroupsRootNodes = new ArrayList<SampleCode.Node>();
1757 for (SampleCode gsc : sampleCodeGroups) {
Dirk Dougherty75372292013-11-26 18:45:31 -08001758 String link = ClearPage.toroot + "samples/" + gsc.mTitle.replaceAll(" ", "").trim().toLowerCase() + ".html";
Dirk Dougherty42303cc2013-09-13 19:45:17 -07001759 sampleGroupsRootNodes.add(new SampleCode.Node.Builder().setLabel(gsc.mTitle).setLink(link).setType("groupholder").build());
Dirk Dougherty815f9342013-09-12 00:30:28 -07001760 }
1761 }
Dirk Dougherty75372292013-11-26 18:45:31 -08001762 // Pass full samplesList to SC to render the samples TOC to sampleNavTree hdf
Dirk Doughertyda7497f2013-08-22 15:22:36 -07001763 if (!offlineMode) {
Dirk Dougherty815f9342013-09-12 00:30:28 -07001764 SampleCode.writeSamplesNavTree(samplesList, sampleGroupsRootNodes);
Dirk Doughertyda7497f2013-08-22 15:22:36 -07001765 }
Dirk Dougherty75372292013-11-26 18:45:31 -08001766 // Iterate the samplecode projects writing the files to out
1767 for (SampleCode sc : sampleCodes) {
1768 sc.writeSamplesFiles(offlineMode);
1769 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -07001770 }
1771
Dirk Dougherty5ce827a2013-11-21 18:08:58 -08001772 /**
1773 * Given an initial samples directory root, walk through the directory collecting
1774 * sample code project roots and adding them to an array of SampleCodes.
1775 * @param rootDir Root directory holding all browseable sample code projects,
1776 * defined in frameworks/base/Android.mk as "-sampleDir path".
1777 */
1778 public static void getSampleProjects(File rootDir) {
1779 for (File f : rootDir.listFiles()) {
1780 String name = f.getName();
1781 if (f.isDirectory()) {
1782 if (isValidSampleProjectRoot(f)) {
1783 sampleCodes.add(new SampleCode(f.getAbsolutePath(), "samples/" + name, name));
1784 } else {
1785 getSampleProjects(f);
1786 }
1787 }
1788 }
1789 }
1790
1791 /**
1792 * Test whether a given directory is the root directory for a sample code project.
Renato Manginie11680d2014-08-15 13:17:12 -07001793 * Root directories must contain a valid _index.jd file and a src/ directory
1794 * or a module directory that contains a src/ directory.
Dirk Dougherty5ce827a2013-11-21 18:08:58 -08001795 */
1796 public static boolean isValidSampleProjectRoot(File dir) {
Renato Manginie11680d2014-08-15 13:17:12 -07001797 File indexJd = new File(dir, "_index.jd");
1798 if (!indexJd.exists()) {
1799 return false;
1800 }
1801 File srcDir = new File(dir, "src");
1802 if (srcDir.exists()) {
Dirk Dougherty5ce827a2013-11-21 18:08:58 -08001803 return true;
1804 } else {
Renato Manginie11680d2014-08-15 13:17:12 -07001805 // Look for a src/ directory one level below the root directory, so
1806 // modules are supported.
1807 for (File childDir : dir.listFiles()) {
1808 if (childDir.isDirectory()) {
1809 srcDir = new File(childDir, "src");
1810 if (srcDir.exists()) {
1811 return true;
1812 }
1813 }
1814 }
Dirk Dougherty5ce827a2013-11-21 18:08:58 -08001815 return false;
1816 }
1817 }
Hui Shu5118ffe2014-02-18 14:06:42 -08001818
Ben Dodson920dbbb2010-08-04 15:21:06 -07001819}