001: /**
002: *******************************************************************************
003: * Copyright (C) 2005-2006, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007:
008: /**
009: * Represents the API information on a doc element.
010: */package com.ibm.icu.dev.tool.docs;
011:
012: import java.io.*;
013: import java.util.*;
014:
015: class APIInfo {
016: // version id for the format of the APIInfo data
017:
018: public static final int VERSION = 2;
019:
020: // public keys and values for queries on info
021:
022: public static final int STA = 0, STA_DRAFT = 0, STA_STABLE = 1,
023: STA_DEPRECATED = 2, STA_OBSOLETE = 3, STA_INTERNAL = 4;
024: public static final int VIS = 1, VIS_PACKAGE = 0, VIS_PUBLIC = 1,
025: VIS_PROTECTED = 2, VIS_PRIVATE = 3;
026: public static final int STK = 2, STK_STATIC = 1;
027: public static final int FIN = 3, FIN_FINAL = 1;
028: public static final int SYN = 4, SYN_SYNCHRONIZED = 1;
029: public static final int ABS = 5, ABS_ABSTRACT = 1;
030: public static final int CAT = 6, CAT_CLASS = 0, CAT_FIELD = 1,
031: CAT_CONSTRUCTOR = 2, CAT_METHOD = 3;
032: public static final int PAK = 7;
033: public static final int CLS = 8;
034: public static final int NAM = 9;
035: public static final int SIG = 10;
036: public static final int EXC = 11;
037: public static final int NUM_TYPES = 11;
038:
039: // the separator between tokens in the data file
040: public int[] masks = { 0x7, 0x3, 0x1, 0x1, 0x1, 0x1, 0x3 };
041: public int[] shifts = { 0, 3, 5, 6, 7, 8, 9 };
042:
043: public static final char SEP = ';';
044:
045: // Internal State
046: private int info; // information about numeric values packed into an int as variable-length nibbles
047: private String pack = ""; // package
048: private String cls = ""; // enclosing class
049: private String name = ""; // name
050: private String sig = ""; // signature, class: inheritance, method: signature, field: type, const: signature
051: private String exc = ""; // throws
052:
053: public int hashCode() {
054: return (((pack.hashCode() << 3) ^ cls.hashCode()) << 3)
055: ^ name.hashCode();
056: }
057:
058: public boolean equals(Object rhs) {
059: if (rhs == this )
060: return true;
061: if (rhs == null)
062: return false;
063: try {
064: APIInfo that = (APIInfo) rhs;
065: return this .info == that.info
066: && this .pack.equals(that.pack)
067: && this .cls.equals(that.cls)
068: && this .name.equals(that.name)
069: && this .sig.equals(that.sig)
070: && this .exc.equals(that.exc);
071: } catch (ClassCastException e) {
072: return false;
073: }
074: }
075:
076: public void setDraft() {
077: setType(STA, STA_DRAFT);
078: }
079:
080: public void setStable() {
081: setType(STA, STA_STABLE);
082: }
083:
084: public void setDeprecated() {
085: setType(STA, STA_DEPRECATED);
086: }
087:
088: public void setObsolete() {
089: setType(STA, STA_OBSOLETE);
090: }
091:
092: public void setInternal() {
093: setType(STA, STA_INTERNAL);
094: }
095:
096: public void setPackage() {
097: setType(VIS, VIS_PACKAGE);
098: }
099:
100: public void setPublic() {
101: setType(VIS, VIS_PUBLIC);
102: }
103:
104: public void setProtected() {
105: setType(VIS, VIS_PROTECTED);
106: }
107:
108: public void setPrivate() {
109: setType(VIS, VIS_PRIVATE);
110: }
111:
112: public void setStatic() {
113: setType(STK, STK_STATIC);
114: }
115:
116: public void setFinal() {
117: setType(FIN, FIN_FINAL);
118: }
119:
120: public void setSynchronized() {
121: setType(SYN, SYN_SYNCHRONIZED);
122: }
123:
124: public void setAbstract() {
125: setType(ABS, ABS_ABSTRACT);
126: }
127:
128: public void setClass() {
129: setType(CAT, CAT_CLASS);
130: }
131:
132: public void setField() {
133: setType(CAT, CAT_FIELD);
134: }
135:
136: public void setConstructor() {
137: setType(CAT, CAT_CONSTRUCTOR);
138: }
139:
140: public void setMethod() {
141: setType(CAT, CAT_METHOD);
142: }
143:
144: public void setPackage(String val) {
145: setType(PAK, val);
146: }
147:
148: public void setClassName(String val) {
149: setType(CLS, val);
150: }
151:
152: public void setName(String val) {
153: setType(NAM, val);
154: }
155:
156: public void setSignature(String val) {
157: setType(SIG, val);
158: }
159:
160: public void setExceptions(String val) {
161: setType(EXC, val);
162: }
163:
164: public boolean isDraft() {
165: return getVal(STA) == STA_DRAFT;
166: }
167:
168: public boolean isStable() {
169: return getVal(STA) == STA_STABLE;
170: }
171:
172: public boolean isDeprecated() {
173: return getVal(STA) == STA_DEPRECATED;
174: }
175:
176: public boolean isObsolete() {
177: return getVal(STA) == STA_OBSOLETE;
178: }
179:
180: public boolean isInternal() {
181: return getVal(STA) == STA_INTERNAL;
182: }
183:
184: public boolean isPackage() {
185: return getVal(VIS) == VIS_PACKAGE;
186: }
187:
188: public boolean isPublic() {
189: return getVal(VIS) == VIS_PUBLIC;
190: }
191:
192: public boolean isProtected() {
193: return getVal(VIS) == VIS_PROTECTED;
194: }
195:
196: public boolean isPrivate() {
197: return getVal(VIS) == VIS_PRIVATE;
198: }
199:
200: public boolean isStatic() {
201: return getVal(STK) == STK_STATIC;
202: }
203:
204: public boolean isFinal() {
205: return getVal(FIN) == FIN_FINAL;
206: }
207:
208: public boolean isSynchronized() {
209: return getVal(SYN) == SYN_SYNCHRONIZED;
210: }
211:
212: public boolean isAbstract() {
213: return getVal(ABS) == ABS_ABSTRACT;
214: }
215:
216: public boolean isClass() {
217: return getVal(CAT) == CAT_CLASS;
218: }
219:
220: public boolean isField() {
221: return getVal(CAT) == CAT_FIELD;
222: }
223:
224: public boolean isConstructor() {
225: return getVal(CAT) == CAT_CONSTRUCTOR;
226: }
227:
228: public boolean isMethod() {
229: return getVal(CAT) == CAT_METHOD;
230: }
231:
232: public String getPackageName() {
233: return get(PAK, true);
234: }
235:
236: public String getClassName() {
237: return get(CLS, true);
238: }
239:
240: public String getName() {
241: return get(NAM, true);
242: }
243:
244: public String getSignature() {
245: return get(SIG, true);
246: }
247:
248: public String getExceptions() {
249: return get(EXC, true);
250: }
251:
252: /**
253: * Return the integer value for the provided type. The type
254: * must be one of the defined type names. The return value
255: * will be one of corresponding values for that type.
256: */
257: public int getVal(int typ) {
258: validateType(typ);
259: return (info >>> shifts[typ]) & masks[typ];
260: }
261:
262: /**
263: * Return the string value for the provided type. The type
264: * must be one of the defined type names. The return value
265: * will be one of corresponding values for that type. Brief
266: * should be true for writing data files, false for presenting
267: * information to the user.
268: */
269: public String get(int typ, boolean brief) {
270: validateType(typ);
271: String[] vals = brief ? shortNames[typ] : names[typ];
272: if (vals == null) {
273: switch (typ) {
274: case PAK:
275: return pack;
276: case CLS:
277: return cls;
278: case NAM:
279: return name;
280: case SIG:
281: return sig;
282: case EXC:
283: return exc;
284: }
285: }
286: int val = (info >>> shifts[typ]) & masks[typ];
287: return vals[val];
288: }
289:
290: /**
291: * Set the numeric value for the type. The value should be a
292: * value corresponding to the type. Only the lower two bits
293: * of the value are used.
294: */
295: public void setType(int typ, int val) {
296: validateType(typ);
297: info &= ~(masks[typ] << shifts[typ]);
298: info |= (val & masks[typ]) << shifts[typ];
299: }
300:
301: /**
302: * Set the string value for the type. For numeric types,
303: * the value should be a string in 'brief' format. For
304: * non-numeric types, the value can be any
305: * string.
306: */
307: private void setType(int typ, String val) {
308: validateType(typ);
309: String[] vals = shortNames[typ];
310: if (vals == null) {
311: if (val == null) {
312: val = "";
313: }
314: switch (typ) {
315: case PAK:
316: pack = val;
317: break;
318: case CLS:
319: cls = val;
320: break;
321: case NAM:
322: name = val;
323: break;
324: case SIG:
325: sig = val;
326: break;
327: case EXC:
328: exc = val;
329: break;
330: }
331: return;
332: }
333:
334: for (int i = 0; i < vals.length; ++i) {
335: if (val.equalsIgnoreCase(vals[i])) {
336: info &= ~(masks[typ] << shifts[typ]);
337: info |= i << shifts[typ];
338: return;
339: }
340: }
341:
342: throw new IllegalArgumentException("unrecognized value '" + val
343: + "' for type '" + typeNames[typ] + "'");
344: }
345:
346: /**
347: * Write the information out as a single line in brief format.
348: * If there are IO errors, throws a RuntimeException.
349: */
350: public void writeln(BufferedWriter w) {
351: try {
352: for (int i = 0; i < NUM_TYPES; ++i) {
353: String s = get(i, true);
354: if (s != null) {
355: w.write(s);
356: }
357: w.write(SEP);
358: }
359: w.newLine();
360: } catch (IOException e) {
361: RuntimeException re = new RuntimeException("IO Error");
362: re.initCause(e);
363: throw re;
364: }
365: }
366:
367: /**
368: * Read a record from the input and initialize this APIInfo.
369: * Return true if successfule, false if EOF, otherwise throw
370: * a RuntimeException.
371: */
372: public boolean read(BufferedReader r) {
373: int i = 0;
374: try {
375: for (; i < NUM_TYPES; ++i) {
376: setType(i, readToken(r));
377: }
378: r.readLine(); // swallow line end sequence
379: } catch (IOException e) {
380: if (i == 0) { // assume if first read returns error, we have reached end of input
381: return false;
382: }
383: RuntimeException re = new RuntimeException("IO Error");
384: re.initCause(e);
385: throw re;
386: }
387:
388: return true;
389: }
390:
391: /**
392: * Read one token from input, which should have been written by
393: * APIInfo. Throws IOException if EOF is encountered before the
394: * token is complete (i.e. before the separator character is
395: * encountered) or if the token exceeds the maximum length of
396: * 255 chars.
397: */
398: public static String readToken(BufferedReader r) throws IOException {
399: char[] buf = new char[256];
400: int i = 0;
401: for (; i < buf.length; ++i) {
402: int c = r.read();
403: if (c == -1) {
404: throw new IOException("unexpected EOF");
405: } else if (c == SEP) {
406: break;
407: }
408: buf[i] = (char) c;
409: }
410: if (i == buf.length) {
411: throw new IOException("unterminated token"
412: + new String(buf));
413: }
414:
415: return new String(buf, 0, i);
416: }
417:
418: /**
419: * The default comparator for APIInfo. This compares packages, class/name
420: * (as the info represents a class or other object), category, name,
421: * and signature.
422: */
423: public static Comparator defaultComparator() {
424: final Comparator c = new Comparator() {
425: public int compare(Object lhs, Object rhs) {
426: APIInfo lhi = (APIInfo) lhs;
427: APIInfo rhi = (APIInfo) rhs;
428: int result = lhi.pack.compareTo(rhi.pack);
429: if (result == 0) {
430: result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name
431: : lhi.cls)
432: .compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name
433: : rhi.cls);
434: if (result == 0) {
435: result = lhi.getVal(CAT) - rhi.getVal(CAT);
436: if (result == 0) {
437: result = lhi.name.compareTo(rhi.name);
438: if (result == 0) {
439: result = lhi.sig.compareTo(rhi.sig);
440: }
441: }
442: }
443: }
444: return result;
445: }
446: };
447: return c;
448: }
449:
450: /**
451: * This compares two APIInfos by package, class/name, category, name, and then if
452: * the APIInfo does not represent a class, by signature. The difference between
453: * this and the default comparator is that APIInfos representing classes are considered
454: * equal regardless of their signatures (which represent inheritance for classes).
455: */
456: public static Comparator changedComparator() {
457: final Comparator c = new Comparator() {
458: public int compare(Object lhs, Object rhs) {
459: APIInfo lhi = (APIInfo) lhs;
460: APIInfo rhi = (APIInfo) rhs;
461: int result = lhi.pack.compareTo(rhi.pack);
462: if (result == 0) {
463: result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name
464: : lhi.cls)
465: .compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name
466: : rhi.cls);
467: if (result == 0) {
468: result = lhi.getVal(CAT) - rhi.getVal(CAT);
469: if (result == 0) {
470: result = lhi.name.compareTo(rhi.name);
471: if (result == 0
472: && lhi.getVal(CAT) != CAT_CLASS) {
473: // signature change on fields ignored
474: if (lhi.getVal(CAT) != CAT_FIELD) {
475: result = lhi.sig.compareTo(rhi.sig);
476: }
477: }
478: }
479: }
480: }
481: return result;
482: }
483: };
484: return c;
485: }
486:
487: /**
488: * This compares two APIInfos by package, then sorts classes before non-classes, then
489: * by class/name, category, name, and signature.
490: */
491: public static Comparator classFirstComparator() {
492: final Comparator c = new Comparator() {
493: public int compare(Object lhs, Object rhs) {
494: APIInfo lhi = (APIInfo) lhs;
495: APIInfo rhi = (APIInfo) rhs;
496: int result = lhi.pack.compareTo(rhi.pack);
497: if (result == 0) {
498: boolean lcls = lhi.getVal(CAT) == CAT_CLASS;
499: boolean rcls = rhi.getVal(CAT) == CAT_CLASS;
500: result = lcls == rcls ? 0 : (lcls ? -1 : 1);
501: if (result == 0) {
502: result = (lcls ? lhi.name : lhi.cls)
503: .compareTo(rcls ? rhi.name : rhi.cls);
504: if (result == 0) {
505: result = lhi.getVal(CAT) - rhi.getVal(CAT);
506: if (result == 0) {
507: result = lhi.name.compareTo(rhi.name);
508: if (result == 0 && !lcls) {
509: result = lhi.sig.compareTo(rhi.sig);
510: }
511: }
512: }
513: }
514: }
515: return result;
516: }
517: };
518: return c;
519: }
520:
521: /**
522: * Write the data in report format.
523: */
524: public void print(PrintWriter pw, boolean detail, boolean html) {
525: StringBuffer buf = new StringBuffer();
526:
527: // remove all occurrences of icu packages from the param string
528: // fortunately, all the packages have 4 chars (lang, math, text, util).
529: String xsig = sig;
530: if (!detail) {
531: final String ICUPACK = "com.ibm.icu.";
532: StringBuffer tbuf = new StringBuffer();
533: for (int i = 0; i < sig.length();) {
534: int n = sig.indexOf(ICUPACK, i);
535: if (n == -1) {
536: tbuf.append(sig.substring(i));
537: break;
538: }
539: tbuf.append(sig.substring(i, n));
540: i = n + ICUPACK.length() + 5; // trailing 'xxxx.'
541: }
542: xsig = tbuf.toString();
543: }
544:
545: // construct signature
546: for (int i = STA; i < CAT; ++i) { // include status
547: String s = get(i, false);
548: if (s != null && s.length() > 0) {
549: if (html && s.indexOf("internal") != -1) {
550: buf.append("<span style='color:red'>");
551: buf.append(s);
552: buf.append("</span>");
553: } else {
554: buf.append(s);
555: buf.append(' ');
556: }
557: }
558: }
559:
560: int val = getVal(CAT);
561: switch (val) {
562: case CAT_CLASS:
563: if (sig.indexOf("extends") == -1) {
564: buf.append("interface ");
565: } else {
566: buf.append("class ");
567: }
568: if (html) {
569: buf.append("<i>");
570: }
571: if (cls.length() > 0) {
572: buf.append(cls);
573: buf.append('.');
574: }
575: buf.append(name);
576: if (html) {
577: buf.append("</i>");
578: }
579: if (detail) {
580: buf.append(' ');
581: buf.append(sig);
582: }
583: break;
584:
585: case CAT_FIELD:
586: buf.append(xsig);
587: buf.append(' ');
588: buf.append(name);
589: break;
590:
591: case CAT_METHOD:
592: case CAT_CONSTRUCTOR:
593: int n = xsig.indexOf('(');
594: if (n > 0) {
595: buf.append(xsig.substring(0, n));
596: buf.append(' ');
597: } else {
598: n = 0;
599: }
600: if (html) {
601: buf.append("<i>" + name + "</i>");
602: } else {
603: buf.append(name);
604: }
605: buf.append(xsig.substring(n));
606: break;
607: }
608:
609: pw.print(buf.toString());
610: }
611:
612: public void println(PrintWriter pw, boolean detail, boolean html) {
613: print(pw, detail, html);
614: pw.println();
615: }
616:
617: private static final String[] typeNames = { "status", "visibility",
618: "static", "final", "synchronized", "abstract", "category",
619: "package", "class", "name", "signature" };
620:
621: public static final String getTypeValName(int typ, int val) {
622: try {
623: return names[typ][val];
624: } catch (Exception e) {
625: return "";
626: }
627: }
628:
629: private static final String[][] names = {
630: { "(draft) ", "(stable) ", "(deprecated)",
631: "(obsolete) ", "*internal* " },
632: { "package", "public", "protected", "private" },
633: { "", "static" }, { "", "final" }, { "", "synchronized" },
634: { "", "abstract" },
635: { "class", "field", "constructor", "method" }, null, null,
636: null, null, null };
637:
638: private static final String[][] shortNames = {
639: { "DR", "ST", "DP", "OB", "IN" },
640: { "PK", "PB", "PT", "PR" }, { "NS", "ST" }, { "NF", "FN" },
641: { "NS", "SY" }, { "NA", "AB" }, { "L", "F", "C", "M" },
642: null, null, null, null, null };
643:
644: private static void validateType(int typ) {
645: if (typ < 0 || typ > NUM_TYPES) {
646: throw new IllegalArgumentException("bad type index: " + typ);
647: }
648: }
649:
650: public String toString() {
651: return get(NAM, true);
652: }
653: }
|