2 *******************************************************************************
\r
3 * Copyright (C) 2005-2010, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
9 * Represents the API information on a doc element.
\r
12 package com.ibm.icu.dev.tool.docs;
\r
14 import java.io.BufferedReader;
\r
15 import java.io.BufferedWriter;
\r
16 import java.io.IOException;
\r
17 import java.io.PrintWriter;
\r
18 import java.util.Comparator;
\r
21 // version id for the format of the APIInfo data
\r
23 public static final int VERSION = 2;
\r
25 // public keys and values for queries on info
\r
27 public static final int STA = 0, STA_DRAFT = 0, STA_STABLE = 1, STA_DEPRECATED = 2,
\r
28 STA_OBSOLETE = 3, STA_INTERNAL = 4;
\r
29 public static final int VIS = 1, VIS_PACKAGE = 0, VIS_PUBLIC= 1, VIS_PROTECTED = 2,
\r
31 public static final int STK = 2, STK_STATIC = 1;
\r
32 public static final int FIN = 3, FIN_FINAL = 1;
\r
33 public static final int SYN = 4, SYN_SYNCHRONIZED = 1;
\r
34 public static final int ABS = 5, ABS_ABSTRACT = 1;
\r
35 public static final int CAT = 6, CAT_CLASS = 0, CAT_FIELD = 1, CAT_CONSTRUCTOR = 2,
\r
37 public static final int PAK = 7;
\r
38 public static final int CLS = 8;
\r
39 public static final int NAM = 9;
\r
40 public static final int SIG = 10;
\r
41 public static final int EXC = 11;
\r
42 public static final int NUM_TYPES = 11;
\r
44 // the separator between tokens in the data file
\r
45 public int[] masks = { 0x7, 0x3, 0x1, 0x1, 0x1, 0x1, 0x3 };
\r
46 public int[] shifts = { 0, 3, 5, 6, 7, 8, 9 };
\r
48 public static final char SEP = ';';
\r
52 private int info; // information about numeric values packed into an int
\r
53 // as variable-length nibbles
\r
54 private String pack = ""; // package
\r
55 private String cls = ""; // enclosing class
\r
56 private String name = ""; // name
\r
57 private String sig = ""; // signature, class: inheritance, method: signature,
\r
58 // field: type, const: signature
\r
59 private String exc = ""; // throws
\r
60 private String stver = ""; // status version
\r
62 private boolean includeStatusVer = false;
\r
64 public int hashCode() {
\r
65 return (((pack.hashCode() << 3) ^ cls.hashCode()) << 3) ^ name.hashCode();
\r
68 public boolean equals(Object rhs) {
\r
69 if (rhs == this) return true;
\r
70 if (rhs == null) return false;
\r
72 APIInfo that = (APIInfo)rhs;
\r
73 return this.info == that.info &&
\r
74 this.pack.equals(that.pack) &&
\r
75 this.cls.equals(that.cls) &&
\r
76 this.name.equals(that.name) &&
\r
77 this.sig.equals(that.sig) &&
\r
78 this.exc.equals(that.exc) &&
\r
79 this.stver.equals(this.stver);
\r
81 catch (ClassCastException e) {
\r
86 public void setDraft() { setType(STA, STA_DRAFT); }
\r
87 public void setStable() { setType(STA, STA_STABLE); }
\r
88 public void setDeprecated() { setType(STA, STA_DEPRECATED); }
\r
89 public void setObsolete() { setType(STA, STA_OBSOLETE); }
\r
90 public void setInternal() { setType(STA, STA_INTERNAL); }
\r
91 public void setPackage() { setType(VIS, VIS_PACKAGE); }
\r
92 public void setPublic() { setType(VIS, VIS_PUBLIC); }
\r
93 public void setProtected() { setType(VIS, VIS_PROTECTED); }
\r
94 public void setPrivate() { setType(VIS, VIS_PRIVATE); }
\r
95 public void setStatic() { setType(STK, STK_STATIC); }
\r
96 public void setFinal() { setType(FIN, FIN_FINAL); }
\r
97 public void setSynchronized() { setType(SYN, SYN_SYNCHRONIZED); }
\r
98 public void setAbstract() { setType(ABS, ABS_ABSTRACT); }
\r
99 public void setClass() { setType(CAT, CAT_CLASS); }
\r
100 public void setField() { setType(CAT, CAT_FIELD); }
\r
101 public void setConstructor() { setType(CAT, CAT_CONSTRUCTOR); }
\r
102 public void setMethod() { setType(CAT, CAT_METHOD); }
\r
104 public void setPackage(String val) { setType(PAK, val); }
\r
105 public void setClassName(String val) { setType(CLS, val); }
\r
106 public void setName(String val) { setType(NAM, val); }
\r
107 public void setSignature(String val) { setType(SIG, val); }
\r
108 public void setExceptions(String val) { setType(EXC, val); }
\r
110 public boolean isDraft() { return getVal(STA) == STA_DRAFT; }
\r
111 public boolean isStable() { return getVal(STA) == STA_STABLE; }
\r
112 public boolean isDeprecated() { return getVal(STA) == STA_DEPRECATED; }
\r
113 public boolean isObsolete() { return getVal(STA) == STA_OBSOLETE; }
\r
114 public boolean isInternal() { return getVal(STA) == STA_INTERNAL; }
\r
115 public boolean isPackage() { return getVal(VIS) == VIS_PACKAGE; }
\r
116 public boolean isPublic() { return getVal(VIS) == VIS_PUBLIC; }
\r
117 public boolean isProtected() { return getVal(VIS) == VIS_PROTECTED; }
\r
118 public boolean isPrivate() { return getVal(VIS) == VIS_PRIVATE; }
\r
119 public boolean isStatic() { return getVal(STK) == STK_STATIC; }
\r
120 public boolean isFinal() { return getVal(FIN) == FIN_FINAL; }
\r
121 public boolean isSynchronized() { return getVal(SYN) == SYN_SYNCHRONIZED; }
\r
122 public boolean isAbstract() { return getVal(ABS) == ABS_ABSTRACT; }
\r
123 public boolean isClass() { return getVal(CAT) == CAT_CLASS; }
\r
124 public boolean isField() { return getVal(CAT) == CAT_FIELD; }
\r
125 public boolean isConstructor() { return getVal(CAT) == CAT_CONSTRUCTOR; }
\r
126 public boolean isMethod() { return getVal(CAT) == CAT_METHOD; }
\r
128 public String getPackageName() { return get(PAK, true); }
\r
129 public String getClassName() { return get(CLS, true); }
\r
130 public String getName() { return get(NAM, true); }
\r
131 public String getSignature() { return get(SIG, true); }
\r
132 public String getExceptions() { return get(EXC, true); }
\r
134 public void setStatusVersion(String v) { stver = v; }
\r
135 public String getStatusVersion() { return stver; }
\r
138 * Return the integer value for the provided type. The type
\r
139 * must be one of the defined type names. The return value
\r
140 * will be one of corresponding values for that type.
\r
142 public int getVal(int typ) {
\r
144 if (typ >= shifts.length) {
\r
147 return (info >>> shifts[typ]) & masks[typ];
\r
151 * Return the string value for the provided type. The type
\r
152 * must be one of the defined type names. The return value
\r
153 * will be one of corresponding values for that type. Brief
\r
154 * should be true for writing data files, false for presenting
\r
155 * information to the user.
\r
157 public String get(int typ, boolean brief) {
\r
159 String[] vals = brief ? shortNames[typ] : names[typ];
\r
160 if (vals == null) {
\r
162 case PAK: return pack;
\r
163 case CLS: return cls;
\r
164 case NAM: return name;
\r
165 case SIG: return sig;
\r
166 case EXC: return exc;
\r
169 int val = (info >>> shifts[typ]) & masks[typ];
\r
174 * Set the numeric value for the type. The value should be a
\r
175 * value corresponding to the type. Only the lower two bits
\r
176 * of the value are used.
\r
178 public void setType(int typ, int val) {
\r
180 if (typ < masks.length) {
\r
181 info &= ~(masks[typ] << shifts[typ]);
\r
182 info |= (val&masks[typ]) << shifts[typ];
\r
187 * Set the string value for the type. For numeric types,
\r
188 * the value should be a string in 'brief' format. For
\r
189 * non-numeric types, the value can be any
\r
192 private void setType(int typ, String val) {
\r
194 String[] vals = shortNames[typ];
\r
195 if (vals == null) {
\r
200 case PAK: pack = val; break;
\r
201 case CLS: cls = val; break;
\r
202 case NAM: name = val; break;
\r
203 case SIG: sig = val; break;
\r
204 case EXC: exc = val; break;
\r
209 for (int i = 0; i < vals.length; ++i) {
\r
210 if (val.equalsIgnoreCase(vals[i])) {
\r
211 info &= ~(masks[typ] << shifts[typ]);
\r
212 info |= i << shifts[typ];
\r
217 throw new IllegalArgumentException(
\r
218 "unrecognized value '" + val + "' for type '" + typeNames[typ] + "'");
\r
222 * Enable status version included in input/output
\r
224 public void includeStatusVersion(boolean include) {
\r
225 includeStatusVer = include;
\r
229 * Write the information out as a single line in brief format.
\r
230 * If there are IO errors, throws a RuntimeException.
\r
232 public void writeln(BufferedWriter w) {
\r
234 for (int i = 0; i < NUM_TYPES; ++i) {
\r
235 String s = get(i, true);
\r
239 if (includeStatusVer && i == STA) {
\r
240 String ver = getStatusVersion();
\r
241 if (ver.length() > 0) {
\r
243 w.write(getStatusVersion());
\r
250 catch (IOException e) {
\r
251 RuntimeException re = new RuntimeException("IO Error");
\r
258 * Read a record from the input and initialize this APIInfo.
\r
259 * Return true if successful, false if EOF, otherwise throw
\r
260 * a RuntimeException.
\r
262 public boolean read(BufferedReader r) {
\r
265 for (; i < NUM_TYPES; ++i) {
\r
266 setType(i, readToken(r));
\r
268 r.readLine(); // swallow line end sequence
\r
270 catch (IOException e) {
\r
271 if (i == 0) { // assume if first read returns error, we have reached end of input
\r
274 RuntimeException re = new RuntimeException("IO Error");
\r
283 * Read one token from input, which should have been written by
\r
284 * APIInfo. Throws IOException if EOF is encountered before the
\r
285 * token is complete (i.e. before the separator character is
\r
286 * encountered) or if the token exceeds the maximum length of
\r
289 public static String readToken(BufferedReader r) throws IOException {
\r
290 char[] buf = new char[256];
\r
292 for (; i < buf.length; ++i) {
\r
295 throw new IOException("unexpected EOF");
\r
296 } else if (c == SEP) {
\r
301 if (i == buf.length) {
\r
302 throw new IOException("unterminated token" + new String(buf));
\r
305 return new String(buf, 0, i);
\r
309 * The default comparator for APIInfo. This compares packages, class/name
\r
310 * (as the info represents a class or other object), category, name,
\r
313 public static Comparator defaultComparator() {
\r
314 final Comparator c = new Comparator() {
\r
315 public int compare(Object lhs, Object rhs) {
\r
316 APIInfo lhi = (APIInfo)lhs;
\r
317 APIInfo rhi = (APIInfo)rhs;
\r
318 int result = lhi.pack.compareTo(rhi.pack);
\r
320 result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name : lhi.cls)
\r
321 .compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name : rhi.cls);
\r
323 result = lhi.getVal(CAT)- rhi.getVal(CAT);
\r
325 result = lhi.name.compareTo(rhi.name);
\r
327 result = lhi.sig.compareTo(rhi.sig);
\r
339 * This compares two APIInfos by package, class/name, category, name, and then if
\r
340 * the APIInfo does not represent a class, by signature. The difference between
\r
341 * this and the default comparator is that APIInfos representing classes are considered
\r
342 * equal regardless of their signatures (which represent inheritance for classes).
\r
344 public static Comparator changedComparator() {
\r
345 final Comparator c = new Comparator() {
\r
346 public int compare(Object lhs, Object rhs) {
\r
347 APIInfo lhi = (APIInfo)lhs;
\r
348 APIInfo rhi = (APIInfo)rhs;
\r
349 int result = lhi.pack.compareTo(rhi.pack);
\r
351 result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name : lhi.cls)
\r
352 .compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name : rhi.cls);
\r
354 result = lhi.getVal(CAT)- rhi.getVal(CAT);
\r
356 result = lhi.name.compareTo(rhi.name);
\r
357 if (result == 0 && lhi.getVal(CAT) != CAT_CLASS) {
\r
358 // signature change on fields ignored
\r
359 if (lhi.getVal(CAT) != CAT_FIELD) {
\r
360 result = lhi.sig.compareTo(rhi.sig);
\r
373 * This compares two APIInfos by package, then sorts classes before non-classes, then
\r
374 * by class/name, category, name, and signature.
\r
376 public static Comparator classFirstComparator() {
\r
377 final Comparator c = new Comparator() {
\r
378 public int compare(Object lhs, Object rhs) {
\r
379 APIInfo lhi = (APIInfo)lhs;
\r
380 APIInfo rhi = (APIInfo)rhs;
\r
381 int result = lhi.pack.compareTo(rhi.pack);
\r
383 boolean lcls = lhi.getVal(CAT) == CAT_CLASS;
\r
384 boolean rcls = rhi.getVal(CAT) == CAT_CLASS;
\r
385 result = lcls == rcls ? 0 : (lcls ? -1 : 1);
\r
387 result = (lcls ? lhi.name : lhi.cls).compareTo(
\r
388 rcls ? rhi.name : rhi.cls);
\r
390 result = lhi.getVal(CAT)- rhi.getVal(CAT);
\r
392 result = lhi.name.compareTo(rhi.name);
\r
393 if (result == 0 && !lcls) {
\r
394 result = lhi.sig.compareTo(rhi.sig);
\r
407 * Write the data in report format.
\r
409 public void print(PrintWriter pw, boolean detail, boolean html) {
\r
410 StringBuffer buf = new StringBuffer();
\r
412 // remove all occurrences of icu packages from the param string
\r
413 // fortunately, all the packages have 4 chars (lang, math, text, util).
\r
416 final String ICUPACK = "com.ibm.icu.";
\r
417 StringBuffer tbuf = new StringBuffer();
\r
418 for (int i = 0; i < sig.length();) {
\r
419 int n = sig.indexOf(ICUPACK, i);
\r
421 tbuf.append(sig.substring(i));
\r
424 tbuf.append(sig.substring(i, n));
\r
425 i = n + ICUPACK.length() + 5; // trailing 'xxxx.'
\r
427 xsig = tbuf.toString();
\r
430 // construct signature
\r
431 for (int i = STA; i < CAT; ++i) { // include status
\r
432 String s = get(i, false);
\r
433 if (s != null && s.length() > 0) {
\r
434 if (html && s.indexOf("internal") != -1) {
\r
435 buf.append("<span style='color:red'>");
\r
437 buf.append("</span>");
\r
445 int val = getVal(CAT);
\r
448 if (sig.indexOf("extends") == -1) {
\r
449 buf.append("interface ");
\r
451 buf.append("class ");
\r
456 if (cls.length() > 0) {
\r
462 buf.append("</i>");
\r
477 case CAT_CONSTRUCTOR:
\r
478 int n = xsig.indexOf('(');
\r
480 buf.append(xsig.substring(0, n));
\r
486 buf.append("<i>" + name + "</i>");
\r
490 buf.append(xsig.substring(n));
\r
494 pw.print(buf.toString());
\r
497 public void println(PrintWriter pw, boolean detail, boolean html) {
\r
498 print(pw, detail, html);
\r
502 private static final String[] typeNames = {
\r
503 "status", "visibility", "static", "final", "synchronized",
\r
504 "abstract", "category", "package", "class", "name", "signature"
\r
507 public static final String getTypeValName(int typ, int val) {
\r
509 return names[typ][val];
\r
511 catch (Exception e) {
\r
516 private static final String[][] names = {
\r
517 { "(draft) ", "(stable) ", "(deprecated)", "(obsolete) ", "*internal* " },
\r
518 { "package", "public", "protected", "private" },
\r
521 { "", "synchronized" },
\r
522 { "", "abstract" },
\r
523 { "class", "field", "constructor", "method" },
\r
531 private static final String[][] shortNames = {
\r
532 { "DR", "ST", "DP", "OB", "IN" },
\r
533 { "PK", "PB", "PT", "PR" },
\r
538 { "L", "F", "C", "M" },
\r
546 private static void validateType(int typ) {
\r
547 if (typ < 0 || typ > NUM_TYPES) {
\r
548 throw new IllegalArgumentException("bad type index: " + typ);
\r
552 public String toString() {
\r
553 return get(NAM, true);
\r