1 /** 2 ******************************************************************************* 3 * Copyright (C) 2004-2012, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8 /** 9 * Generate a list of ICU's public APIs, sorted by qualified name and signature 10 * public APIs are all non-internal, non-package apis in com.ibm.icu.[lang|math|text|util]. 11 * For each API, list 12 * - public, package, protected, or private (PB PK PT PR) 13 * - static or non-static (STK NST) 14 * - final or non-final (FN NF) 15 * - synchronized or non-synchronized (SYN NSY) 16 * - stable, draft, deprecated, obsolete (ST DR DP OB) 17 * - abstract or non-abstract (AB NA) 18 * - constructor, member, field (C M F) 19 * 20 * Requires JDK 1.4.2 or later 21 * 22 * Sample compilation: 23 * c:/doug/java/jdk1.4.2/build/windows-i586/bin/javac *.java 24 * 25 * Sample execution 26 * c:/j2sdk1.4.2/bin/javadoc 27 * -classpath c:/jd2sk1.4.2/lib/tools.jar 28 * -doclet com.ibm.icu.dev.tool.docs.GatherAPIData 29 * -docletpath c:/doug/cvsproj/icu4j/src 30 * -sourcepath c:/doug/cvsproj/icu4j/src 31 * -name "ICU4J 3.0" 32 * -output icu4j30.api 33 * -gzip 34 * -source 1.4 35 * com.ibm.icu.lang com.ibm.icu.math com.ibm.icu.text com.ibm.icu.util 36 * 37 * todo: provide command-line control of filters of which subclasses/packages to process 38 * todo: record full inheritance heirarchy, not just immediate inheritance 39 * todo: allow for aliasing comparisons (force (pkg.)*class to be treated as though it 40 * were in a different pkg/class heirarchy (facilitates comparison of icu4j and java) 41 */ 42 43 package com.ibm.icu.dev.tool.docs; 44 45 // standard release sdk won't work, need internal build to get access to javadoc 46 import java.io.BufferedWriter; 47 import java.io.FileOutputStream; 48 import java.io.IOException; 49 import java.io.OutputStream; 50 import java.io.OutputStreamWriter; 51 import java.util.Collection; 52 import java.util.Iterator; 53 import java.util.TreeSet; 54 import java.util.regex.Pattern; 55 import java.util.zip.GZIPOutputStream; 56 import java.util.zip.ZipEntry; 57 import java.util.zip.ZipOutputStream; 58 59 import com.sun.javadoc.ClassDoc; 60 import com.sun.javadoc.ConstructorDoc; 61 import com.sun.javadoc.Doc; 62 import com.sun.javadoc.ExecutableMemberDoc; 63 import com.sun.javadoc.FieldDoc; 64 import com.sun.javadoc.MethodDoc; 65 import com.sun.javadoc.ProgramElementDoc; 66 import com.sun.javadoc.RootDoc; 67 import com.sun.javadoc.Tag; 68 69 public class GatherAPIDataOld { 70 RootDoc root; 71 TreeSet results; 72 String srcName = "Current"; // default source name 73 String output; // name of output file to write 74 String base; // strip this prefix 75 Pattern pat; 76 boolean zip; 77 boolean gzip; 78 boolean internal; 79 boolean version; 80 optionLength(String option)81 public static int optionLength(String option) { 82 if (option.equals("-name")) { 83 return 2; 84 } else if (option.equals("-output")) { 85 return 2; 86 } else if (option.equals("-base")) { 87 return 2; 88 } else if (option.equals("-filter")) { 89 return 2; 90 } else if (option.equals("-zip")) { 91 return 1; 92 } else if (option.equals("-gzip")) { 93 return 1; 94 } else if (option.equals("-internal")) { 95 return 1; 96 } else if (option.equals("-version")) { 97 return 1; 98 } 99 return 0; 100 } 101 start(RootDoc root)102 public static boolean start(RootDoc root) { 103 return new GatherAPIDataOld(root).run(); 104 } 105 GatherAPIDataOld(RootDoc root)106 GatherAPIDataOld(RootDoc root) { 107 this.root = root; 108 109 String[][] options = root.options(); 110 for (int i = 0; i < options.length; ++i) { 111 String opt = options[i][0]; 112 if (opt.equals("-name")) { 113 this.srcName = options[i][1]; 114 } else if (opt.equals("-output")) { 115 this.output = options[i][1]; 116 } else if (opt.equals("-base")) { 117 this.base = options[i][1]; // should not include '.' 118 } else if (opt.equals("-filter")) { 119 this.pat = Pattern.compile(options[i][1], Pattern.CASE_INSENSITIVE); 120 } else if (opt.equals("-zip")) { 121 this.zip = true; 122 } else if (opt.equals("-gzip")) { 123 this.gzip = true; 124 } else if (opt.equals("-internal")) { 125 this.internal = true; 126 } else if (opt.equals("-version")) { 127 this.version = true; 128 } 129 } 130 131 results = new TreeSet(APIInfo.defaultComparator()); 132 } 133 run()134 private boolean run() { 135 doDocs(root.classes()); 136 137 OutputStream os = System.out; 138 if (output != null) { 139 ZipOutputStream zos = null; 140 try { 141 if (zip) { 142 zos = new ZipOutputStream(new FileOutputStream(output + ".zip")); 143 zos.putNextEntry(new ZipEntry(output)); 144 os = zos; 145 } else if (gzip) { 146 os = new GZIPOutputStream(new FileOutputStream(output + ".gz")); 147 } else { 148 os = new FileOutputStream(output); 149 } 150 } 151 catch (IOException e) { 152 RuntimeException re = new RuntimeException(e.getMessage()); 153 re.initCause(e); 154 throw re; 155 } 156 finally { 157 if (zos != null) { 158 try { 159 zos.close(); 160 } catch (Exception e) { 161 // ignore 162 } 163 } 164 } 165 } 166 167 BufferedWriter bw = null; 168 try { 169 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8"); 170 bw = new BufferedWriter(osw); 171 172 // writing data file 173 bw.write(String.valueOf(APIInfo.VERSION) + APIInfo.SEP); // header version 174 bw.write(srcName + APIInfo.SEP); // source name 175 bw.write((base == null ? "" : base) + APIInfo.SEP); // base 176 bw.newLine(); 177 writeResults(results, bw); 178 bw.close(); // should flush, close all, etc 179 } catch (IOException e) { 180 try { bw.close(); } catch (IOException e2) {} 181 RuntimeException re = new RuntimeException("write error: " + e.getMessage()); 182 re.initCause(e); 183 throw re; 184 } 185 186 return false; 187 } 188 doDocs(ProgramElementDoc[] docs)189 private void doDocs(ProgramElementDoc[] docs) { 190 if (docs != null && docs.length > 0) { 191 for (int i = 0; i < docs.length; ++i) { 192 doDoc(docs[i]); 193 } 194 } 195 } 196 doDoc(ProgramElementDoc doc)197 private void doDoc(ProgramElementDoc doc) { 198 if (ignore(doc)) return; 199 200 if (doc.isClass() || doc.isInterface()) { 201 ClassDoc cdoc = (ClassDoc)doc; 202 doDocs(cdoc.fields()); 203 doDocs(cdoc.constructors()); 204 doDocs(cdoc.methods()); 205 doDocs(cdoc.innerClasses()); 206 } 207 208 APIInfo info = createInfo(doc); 209 if (info != null) { 210 results.add(info); 211 } 212 } 213 ignore(ProgramElementDoc doc)214 private boolean ignore(ProgramElementDoc doc) { 215 if (doc == null) return true; 216 if (doc.isPrivate() || doc.isPackagePrivate()) return true; 217 if (doc instanceof ConstructorDoc && ((ConstructorDoc)doc).isSynthetic()) return true; 218 if (doc.qualifiedName().indexOf(".misc") != -1) { 219 System.out.println("misc: " + doc.qualifiedName()); return true; 220 } 221 if (!internal) { // debug 222 Tag[] tags = doc.tags(); 223 for (int i = 0; i < tags.length; ++i) { 224 if (tagKindIndex(tags[i].kind()) == INTERNAL) { return true; } 225 } 226 } 227 if (pat != null && (doc.isClass() || doc.isInterface())) { 228 if (!pat.matcher(doc.name()).matches()) { 229 return true; 230 } 231 } 232 return false; 233 } 234 writeResults(Collection c, BufferedWriter w)235 private static void writeResults(Collection c, BufferedWriter w) { 236 Iterator iter = c.iterator(); 237 while (iter.hasNext()) { 238 APIInfo info = (APIInfo)iter.next(); 239 info.writeln(w); 240 } 241 } 242 trimBase(String arg)243 private String trimBase(String arg) { 244 if (base != null) { 245 for (int n = arg.indexOf(base); n != -1; n = arg.indexOf(base, n)) { 246 arg = arg.substring(0, n) + arg.substring(n+base.length()); 247 } 248 } 249 return arg; 250 } 251 createInfo(ProgramElementDoc doc)252 public APIInfo createInfo(ProgramElementDoc doc) { 253 254 // Doc. name 255 // Doc. isField, isMethod, isConstructor, isClass, isInterface 256 // ProgramElementDoc. containingClass, containingPackage 257 // ProgramElementDoc. isPublic, isProtected, isPrivate, isPackagePrivate 258 // ProgramElementDoc. isStatic, isFinal 259 // MemberDoc.isSynthetic 260 // ExecutableMemberDoc isSynchronized, signature 261 // Type.toString() // e.g. "String[][]" 262 // ClassDoc.isAbstract, superClass, interfaces, fields, methods, constructors, innerClasses 263 // FieldDoc type 264 // ConstructorDoc qualifiedName 265 // MethodDoc isAbstract, returnType 266 267 APIInfo info = new APIInfo(); 268 if (version) { 269 info.includeStatusVersion(true); 270 } 271 272 // status 273 String[] version = new String[1]; 274 info.setType(APIInfo.STA, tagStatus(doc, version)); 275 info.setStatusVersion(version[0]); 276 277 // visibility 278 if (doc.isPublic()) { 279 info.setPublic(); 280 } else if (doc.isProtected()) { 281 info.setProtected(); 282 } else if (doc.isPrivate()) { 283 info.setPrivate(); 284 } else { 285 // default is package 286 } 287 288 // static 289 if (doc.isStatic()) { 290 info.setStatic(); 291 } else { 292 // default is non-static 293 } 294 295 // final 296 if (doc.isFinal()) { 297 info.setFinal(); 298 } else { 299 // default is non-final 300 } 301 302 // type 303 if (doc.isField()) { 304 info.setField(); 305 } else if (doc.isMethod()) { 306 info.setMethod(); 307 } else if (doc.isConstructor()) { 308 info.setConstructor(); 309 } else if (doc.isClass() || doc.isInterface()) { 310 info.setClass(); 311 } 312 313 info.setPackage(trimBase(doc.containingPackage().name())); 314 info.setClassName((doc.isClass() || doc.isInterface() || (doc.containingClass() == null)) 315 ? "" 316 : trimBase(doc.containingClass().name())); 317 info.setName(trimBase(doc.name())); 318 319 if (doc instanceof FieldDoc) { 320 FieldDoc fdoc = (FieldDoc)doc; 321 info.setSignature(trimBase(fdoc.type().toString())); 322 } else if (doc instanceof ClassDoc) { 323 ClassDoc cdoc = (ClassDoc)doc; 324 325 if (cdoc.isClass() && cdoc.isAbstract()) { 326 // interfaces are abstract by default, don't mark them as abstract 327 info.setAbstract(); 328 } 329 330 StringBuffer buf = new StringBuffer(); 331 if (cdoc.isClass()) { 332 buf.append("extends "); 333 buf.append(cdoc.superclass().qualifiedName()); 334 } 335 ClassDoc[] imp = cdoc.interfaces(); 336 if (imp != null && imp.length > 0) { 337 if (buf.length() > 0) { 338 buf.append(" "); 339 } 340 buf.append("implements"); 341 for (int i = 0; i < imp.length; ++i) { 342 if (i != 0) { 343 buf.append(","); 344 } 345 buf.append(" "); 346 buf.append(imp[i].qualifiedName()); 347 } 348 } 349 info.setSignature(trimBase(buf.toString())); 350 } else { 351 ExecutableMemberDoc emdoc = (ExecutableMemberDoc)doc; 352 if (emdoc.isSynchronized()) { 353 info.setSynchronized(); 354 } 355 356 if (doc instanceof MethodDoc) { 357 MethodDoc mdoc = (MethodDoc)doc; 358 if (mdoc.isAbstract()) { 359 info.setAbstract(); 360 } 361 info.setSignature(trimBase(mdoc.returnType().toString() + emdoc.signature())); 362 } else { 363 // constructor 364 info.setSignature(trimBase(emdoc.signature())); 365 } 366 } 367 368 return info; 369 } 370 tagStatus(final Doc doc, String[] version)371 private int tagStatus(final Doc doc, String[] version) { 372 class Result { 373 int res = -1; 374 void set(int val) { 375 if (res != -1) { 376 if (val == APIInfo.STA_DEPRECATED) { 377 // ok to have both a 'standard' tag and deprecated 378 return; 379 } else if (res != APIInfo.STA_DEPRECATED) { 380 // if already not deprecated, this is an error 381 System.err.println("bad doc: " + doc + " both: " + APIInfo.getTypeValName(APIInfo.STA, res) + " and: " + APIInfo.getTypeValName(APIInfo.STA, val)); 382 return; 383 } 384 } 385 // ok to replace with new tag 386 res = val; 387 } 388 int get() { 389 if (res == -1) { 390 System.err.println("warning: no tag for " + doc); 391 return 0; 392 } 393 return res; 394 } 395 } 396 397 Tag[] tags = doc.tags(); 398 Result result = new Result(); 399 String statusVer = ""; 400 for (int i = 0; i < tags.length; ++i) { 401 Tag tag = tags[i]; 402 403 String kind = tag.kind(); 404 int ix = tagKindIndex(kind); 405 406 switch (ix) { 407 case INTERNAL: 408 result.set(internal ? APIInfo.STA_INTERNAL : -2); // -2 for legacy compatibility 409 statusVer = getStatusVersion(tag); 410 break; 411 412 case DRAFT: 413 result.set(APIInfo.STA_DRAFT); 414 statusVer = getStatusVersion(tag); 415 break; 416 417 case STABLE: 418 result.set(APIInfo.STA_STABLE); 419 statusVer = getStatusVersion(tag); 420 break; 421 422 case DEPRECATED: 423 result.set(APIInfo.STA_DEPRECATED); 424 statusVer = getStatusVersion(tag); 425 break; 426 427 case OBSOLETE: 428 result.set(APIInfo.STA_OBSOLETE); 429 statusVer = getStatusVersion(tag); 430 break; 431 432 case SINCE: 433 case EXCEPTION: 434 case VERSION: 435 case UNKNOWN: 436 case AUTHOR: 437 case SEE: 438 case PARAM: 439 case RETURN: 440 case THROWS: 441 case SERIAL: 442 break; 443 444 default: 445 throw new RuntimeException("unknown index " + ix + " for tag: " + kind); 446 } 447 } 448 449 if (version != null) { 450 version[0] = statusVer; 451 } 452 return result.get(); 453 } 454 getStatusVersion(Tag tag)455 private String getStatusVersion(Tag tag) { 456 String text = tag.text(); 457 if (text != null && text.length() > 0) { 458 // Extract version string 459 int start = -1; 460 int i = 0; 461 for (; i < text.length(); i++) { 462 char ch = text.charAt(i); 463 if (ch == '.' || (ch >= '0' && ch <= '9')) { 464 if (start == -1) { 465 start = i; 466 } 467 } else if (start != -1) { 468 break; 469 } 470 } 471 if (start != -1) { 472 return text.substring(start, i); 473 } 474 } 475 return ""; 476 } 477 478 private static final int UNKNOWN = -1; 479 private static final int INTERNAL = 0; 480 private static final int DRAFT = 1; 481 private static final int STABLE = 2; 482 private static final int SINCE = 3; 483 private static final int DEPRECATED = 4; 484 private static final int AUTHOR = 5; 485 private static final int SEE = 6; 486 private static final int VERSION = 7; 487 private static final int PARAM = 8; 488 private static final int RETURN = 9; 489 private static final int THROWS = 10; 490 private static final int OBSOLETE = 11; 491 private static final int EXCEPTION = 12; 492 private static final int SERIAL = 13; 493 tagKindIndex(String kind)494 private static int tagKindIndex(String kind) { 495 final String[] tagKinds = { 496 "@internal", "@draft", "@stable", "@since", "@deprecated", "@author", "@see", "@version", 497 "@param", "@return", "@throws", "@obsolete", "@exception", "@serial" 498 }; 499 500 for (int i = 0; i < tagKinds.length; ++i) { 501 if (kind.equals(tagKinds[i])) { 502 return i; 503 } 504 } 505 return UNKNOWN; 506 } 507 } 508