1package jdiff;
2
3import java.io.*;
4import java.util.*;
5
6/**
7 * The internal representation of an API.
8 *
9 * RootDoc could have been used for representing this, but
10 * you cannot serialize a RootDoc object - see
11 *  http://developer.java.sun.com/developer/bugParade/bugs/4125581.html
12 * You might be able use Javadoc.Main() to create another RootDoc, but the
13 * methods are package private. You can run javadoc in J2SE1.4, see:
14 *  http://java.sun.com/j2se/1.4/docs/tooldocs/javadoc/standard-doclet.html#runningprogrammatically
15 * but you still can't get the RootDoc object.
16 *
17 * The advantage of writing out an XML representation of each API is that
18 * later runs of JDiff don't have to have Javadoc scan all the files again,
19 * a possibly lengthy process. XML also permits other source code in
20 * languages other than Java to be scanned to produce XML, and then versions
21 * of JDiff can be used to create documents describing the difference in those
22 * APIs.
23 *
24 * See the file LICENSE.txt for copyright details.
25 * @author Matthew Doar, mdoar@pobox.com
26 */
27public class API {
28
29    /**
30     * The list of all the top-level packages.
31     * Each package contains classes, each class contains members, and so on.
32     */
33    public List packages_; // PackageAPI[]
34
35    /**
36     * The list of all the classes.
37     * This is used to generate the methods and fields which are inherited,
38     * rather than storing them in the XML file.
39     */
40    public Hashtable classes_;
41
42    /**
43     * The String which identifies this API, e.g. &quotSuperProduct 1.3".
44     */
45    public String name_ = null;
46
47    /** The current package being added to during parsing. */
48    public PackageAPI currPkg_ = null;
49    /** The current class being added to during parsing. */
50    public ClassAPI currClass_ = null;
51    /** The current constructor being added to during parsing. */
52    public ConstructorAPI currCtor_ = null;
53    /** The current method being added to during parsing. */
54    public MethodAPI currMethod_ = null;
55    /** The current field being added to during parsing. */
56    public FieldAPI currField_ = null;
57
58    /** Default constructor. */
59    public API() {
60        packages_ = new ArrayList(); //PackageAPI[]
61        classes_ = new Hashtable(); //ClassAPI
62    }
63
64//
65// Methods to display the contents of an API object.
66//
67
68    /** Amount by which to increment each indentation. */
69    public static final int indentInc = 2;
70
71    /** Display the contents of the API object. */
72    public void dump() {
73        int indent = 0;
74        Iterator iter = packages_.iterator();
75        while (iter.hasNext()) {
76            dumpPackage((PackageAPI)(iter.next()), indent);
77        }
78    }
79
80    /**
81     * Display the contents of a PackageAPI object.
82     *
83     * @param pkg The given PackageAPI object.
84     * @param indent The number of spaces to indent the output.
85     */
86    public void dumpPackage(PackageAPI pkg, int indent) {
87        for (int i = 0; i < indent; i++) System.out.print(" ");
88        System.out.println("Package Name: " + pkg.name_);
89        Iterator iter = pkg.classes_.iterator();
90        while (iter.hasNext()) {
91            dumpClass((ClassAPI)(iter.next()), indent + indentInc);
92        }
93        // Display documentation
94        if (pkg.doc_ != null) {
95            System.out.print("Package doc block:");
96            System.out.println("\"" + pkg.doc_ + "\"");
97        }
98    }
99
100    /**
101     * Display the contents of a ClassAPI object.
102     *
103     * @param c The given ClassAPI object.
104     * @param indent The number of spaces to indent the output.
105     */
106    public static void dumpClass(ClassAPI c, int indent) {
107        for (int i = 0; i < indent; i++) System.out.print(" ");
108        if (c.isInterface_)
109            System.out.println("Interface name: " + c.name_);
110        else
111            System.out.println("Class Name: " + c.name_);
112        if (c.extends_ != null) {
113            for (int i = 0; i < indent; i++) System.out.print(" ");
114            System.out.println("Extends: " + c.extends_);
115        }
116        if (c.implements_.size() != 0) {
117            for (int i = 0; i < indent; i++) System.out.print(" ");
118            System.out.println("Implements: ");
119            Iterator iter = c.implements_.iterator();
120            while (iter.hasNext()) {
121                String interfaceImpl = (String)(iter.next());
122                for (int i = 0; i < indent + 2; i++) System.out.print(" ");
123                System.out.println("  " + interfaceImpl);
124            }
125        }
126        // Dump modifiers specific to a class
127        if (c.isAbstract_)
128            System.out.print("abstract ");
129        // Dump modifiers common to all
130        dumpModifiers(c.modifiers_, indent);
131        // Dump ctors
132        Iterator iter = c.ctors_.iterator();
133        while (iter.hasNext()) {
134            dumpCtor((ConstructorAPI)(iter.next()), indent + indentInc);
135        }
136        // Dump methods
137        iter = c.methods_.iterator();
138        while (iter.hasNext()) {
139            dumpMethod((MethodAPI)(iter.next()), indent + indentInc);
140        }
141        // Dump fields
142        iter = c.fields_.iterator();
143        while (iter.hasNext()) {
144            dumpField((FieldAPI)(iter.next()), indent + indentInc);
145        }
146        // Display documentation
147        if (c.doc_ != null) {
148            System.out.print("Class doc block:");
149            System.out.println("\"" + c.doc_ + "\"");
150        } else
151            System.out.println();
152    }
153
154    /**
155     * Display the contents of the Modifiers object.
156     *
157     * @param c The given Modifiers object.
158     * @param indent The number of spaces to indent the output.
159     */
160    public static void dumpModifiers(Modifiers m, int indent) {
161        for (int i = 0; i < indent; i++) System.out.print(" ");
162        if (m.isStatic)
163            System.out.print("static ");
164        if (m.isFinal)
165            System.out.print("final ");
166        if (m.visibility != null)
167            System.out.print("visibility = " + m.visibility + " ");
168        // Flush the line
169        System.out.println();
170    }
171
172    /**
173     * Display the contents of a constructor.
174     *
175     * @param c The given constructor object.
176     * @param indent The number of spaces to indent the output.
177     */
178    public static void dumpCtor(ConstructorAPI c, int indent) {
179        for (int i = 0; i < indent; i++) System.out.print(" ");
180        System.out.println("Ctor type: " + c.type_);
181        // Display exceptions
182        System.out.print("exceptions: " + c.exceptions_ + " ");
183        // Dump modifiers common to all
184        dumpModifiers(c.modifiers_, indent);
185        // Display documentation
186        if (c.doc_ != null) {
187            System.out.print("Ctor doc block:");
188            System.out.println("\"" + c.doc_ + "\"");
189        }
190    }
191
192    /**
193     * Display the contents of a MethodAPI object.
194     *
195     * @param m The given MethodAPI object.
196     * @param indent The number of spaces to indent the output.
197     */
198    public static void dumpMethod(MethodAPI m, int indent) {
199        if (m.inheritedFrom_ != null)
200            return;
201        for (int i = 0; i < indent; i++) System.out.print(" ");
202        System.out.print("Method Name: " + m.name_);
203        if (m.inheritedFrom_ != null)
204            System.out.println(", inherited from: " + m.inheritedFrom_);
205        if (m.returnType_ != null)
206            System.out.println(", return type: " + m.returnType_);
207        else
208            System.out.println();
209        // Dump modifiers specific to a method
210        if (m.isAbstract_)
211            System.out.print("abstract ");
212        if (m.isNative_)
213            System.out.print("native ");
214        if (m.isSynchronized_)
215            System.out.print("synchronized ");
216        // Display exceptions
217        System.out.print("exceptions: " + m.exceptions_ + " ");
218        // Dump modifiers common to all
219        dumpModifiers(m.modifiers_, indent);
220
221        Iterator iter = m.params_.iterator();
222        while (iter.hasNext()) {
223            dumpParam((ParamAPI)(iter.next()), indent + indentInc);
224        }
225        // Display documentation
226        if (m.doc_ != null) {
227            System.out.print("Method doc block:");
228            System.out.println("\"" + m.doc_ + "\"");
229        }
230    }
231
232    /**
233     * Display the contents of a field.
234     * Does not show inherited fields.
235     *
236     * @param f The given field object.
237     * @param indent The number of spaces to indent the output.
238     */
239    public static void dumpField(FieldAPI f, int indent) {
240        if (f.inheritedFrom_ != null)
241            return;
242        for (int i = 0; i < indent; i++) System.out.print(" ");
243        System.out.println("Field Name: " + f.name_ + ", type: " + f.type_);
244        if (f.inheritedFrom_ != null)
245            System.out.println(", inherited from: " + f.inheritedFrom_);
246        if (f.isTransient_)
247            System.out.print("transient ");
248        if (f.isVolatile_)
249            System.out.print("volatile ");
250        // Dump modifiers common to all
251        dumpModifiers(f.modifiers_, indent);
252        // Display documentation
253        if (f.doc_ != null)
254            System.out.print("Field doc block:");
255            System.out.println("\"" + f.doc_ + "\"");
256    }
257
258    /**
259     * Display the contents of a parameter.
260     *
261     * @param p The given parameter object.
262     * @param indent The number of spaces to indent the output.
263     */
264    public static void dumpParam(ParamAPI p, int indent) {
265        for (int i = 0; i < indent; i++) System.out.print(" ");
266        System.out.println("Param Name: " + p.name_ + ", type: " + p.type_);
267    }
268
269    /**
270     * Convert all HTML tags to text by placing them inside a CDATA element.
271     * Characters still have to be valid Unicode characters as defined by the
272     * parser.
273     */
274    public static String stuffHTMLTags(String htmlText) {
275        if (htmlText.indexOf("]]>") != -1) {
276            System.out.println("Warning: illegal string ]]> found in text. Ignoring the comment.");
277            return "";
278        }
279        return "<![CDATA[" + htmlText + "]]>";
280    }
281
282    /**
283     * Convert all HTML tags to text by stuffing text into the HTML tag
284     * to stop it being an HTML or XML tag. E.g. &quot;<code>foo</code>&quot;
285     * becomes &quot;lEsS_tHaNcode>foolEsS_tHaN/code>&quot;. Replace all &lt;
286     * characters
287     * with the string "lEsS_tHaN". Also replace &amp; character with the
288     * string "aNd_cHaR" to avoid text entities. Also replace &quot;
289     * character with the
290     * string "qUoTe_cHaR".
291     */
292    public static String hideHTMLTags(String htmlText) {
293        StringBuffer sb = new StringBuffer(htmlText);
294        int i = 0;
295        while (i < sb.length()) {
296            if (sb.charAt(i) == '<') {
297                sb.setCharAt(i ,'l');
298                sb.insert(i+1, "EsS_tHaN");
299            } else if (sb.charAt(i) == '&') {
300                sb.setCharAt(i ,'a');
301                sb.insert(i+1, "Nd_cHaR");
302            } else if (sb.charAt(i) == '"') {
303                sb.setCharAt(i ,'q');
304                sb.insert(i+1, "uote_cHaR");
305            }
306            i++;
307        }
308        return sb.toString();
309    }
310
311    /**
312     * Convert text with stuffed HTML tags ("lEsS_tHaN", etc) into HTML text.
313     */
314    public static String showHTMLTags(String text) {
315        StringBuffer sb = new StringBuffer(text);
316        StringBuffer res = new StringBuffer();
317        int len = sb.length();
318        res.setLength(len);
319        int i = 0;
320        int resIdx = 0;
321        while (i < len) {
322            char c = sb.charAt(i);
323            if (len - i > 8 && c == 'l' &&
324                sb.charAt(i+1) == 'E' &&
325                sb.charAt(i+2) == 's' &&
326                sb.charAt(i+3) == 'S' &&
327                sb.charAt(i+4) == '_' &&
328                sb.charAt(i+5) == 't' &&
329                sb.charAt(i+6) == 'H' &&
330                sb.charAt(i+7) == 'a' &&
331                sb.charAt(i+8) == 'N') {
332                res.setCharAt(resIdx ,'<');
333                i += 8;
334            } else if (len - i > 9 && c == 'q' &&
335                sb.charAt(i+1) == 'U' &&
336                sb.charAt(i+2) == 'o' &&
337                sb.charAt(i+3) == 'T' &&
338                sb.charAt(i+4) == 'e' &&
339                sb.charAt(i+5) == '_' &&
340                sb.charAt(i+6) == 'c' &&
341                sb.charAt(i+7) == 'H' &&
342                sb.charAt(i+8) == 'a' &&
343                sb.charAt(i+9) == 'R') {
344                res.setCharAt(resIdx ,'"');
345                i += 9;
346            } else if (len - i > 7 && c == 'a' &&
347                sb.charAt(i+1) == 'N' &&
348                sb.charAt(i+2) == 'd' &&
349                sb.charAt(i+3) == '_' &&
350                sb.charAt(i+4) == 'c' &&
351                sb.charAt(i+5) == 'H' &&
352                sb.charAt(i+6) == 'a' &&
353                sb.charAt(i+7) == 'R') {
354                res.setCharAt(resIdx ,'&');
355                i += 7;
356            } else {
357                res.setCharAt(resIdx, c);
358            }
359            i++;
360            resIdx++;
361        }
362        res.setLength(resIdx);
363        return res.toString();
364    }
365
366    /**
367     * <b>NOT USED</b>.
368     *
369     * Replace all instances of <p> with <p/>. Just for the small number
370     * of HMTL tags which don't require a matching end tag.
371     * Also make HTML conform to the simple HTML requirements such as
372     * no double hyphens. Double hyphens are replaced by - and the character
373     * entity for a hyphen.
374     *
375     * Cases where this fails and has to be corrected in the XML by hand:
376     *  Attributes' values missing their double quotes , e.g. size=-2
377     *  Mangled HTML tags e.g. &lt;ttt>
378     *
379     * <p><b>NOT USED</b>. There is often too much bad HTML in
380     * doc blocks to try to handle every case correctly. Better just to
381     * stuff the *lt; and &amp: characters with stuffHTMLTags(). Though
382     * the resulting XML is not as elegant, it does the job with less
383     * intervention by the user.
384     */
385    public static String convertHTMLTagsToXHTML(String htmlText) {
386        StringBuffer sb = new StringBuffer(htmlText);
387        int i = 0;
388        boolean inTag = false;
389        String tag = null;
390        // Needs to re-evaluate this length at each loop
391        while (i < sb.length()) {
392            char c = sb.charAt(i);
393            if (inTag) {
394                if (c == '>') {
395                    // OPTION Could fail at or fix some errorneous tags here
396                    // Make the best guess as to whether this tag is terminated
397                    if (Comments.isMinimizedTag(tag) &&
398                        htmlText.indexOf("</" + tag + ">", i) == -1)
399                        sb.insert(i, "/");
400                    inTag = false;
401                } else {
402                    // OPTION could also make sure that attribute values are
403                    // surrounded by quotes.
404                    tag += c;
405                }
406            }
407            if (c == '<') {
408                inTag = true;
409                tag = "";
410            }
411            // -- is not allowed in XML, but !-- is part of an comment,
412            // and --> is also part of a comment
413            if (c == '-' && i > 0 && sb.charAt(i-1) == '-') {
414                if (!(i > 1 && sb.charAt(i-2) == '!')) {
415                    sb.setCharAt(i, '&');
416                    sb.insert(i+1, "#045;");
417                    i += 5;
418                }
419            }
420            i++;
421        }
422        if (inTag) {
423            // Oops. Someone forgot to close their HTML tag, e.g. "<code."
424            // Close it for them.
425            sb.insert(i, ">");
426        }
427        return sb.toString();
428    }
429}
430