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