1 /*
2  * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.tools.javac.tree;
27 
28 import java.io.Writer;
29 
30 import com.sun.source.doctree.*;
31 import com.sun.source.doctree.AttributeTree.ValueKind;
32 import com.sun.tools.javac.util.Convert;
33 import java.io.IOException;
34 import java.util.List;
35 
36 /**
37  * Prints out a doc comment tree.
38  *
39  *  <p><b>This is NOT part of any supported API.
40  *  If you write code that depends on this, you do so at your own risk.
41  *  This code and its internal interfaces are subject to change or
42  *  deletion without notice.</b>
43  */
44 public class DocPretty implements DocTreeVisitor<Void,Void> {
45 
46     /**
47      * The output stream on which trees are printed.
48      */
49     final Writer out;
50 
51     /**
52      * The left margin.
53      */
54     int lmargin = 0;
55 
DocPretty(Writer out)56     public DocPretty(Writer out) {
57         this.out = out;
58     }
59 
60     /** Visitor method: print expression tree.
61      */
print(DocTree tree)62     public void print(DocTree tree) throws IOException {
63         try {
64             if (tree == null)
65                 print("/*missing*/");
66             else {
67                 tree.accept(this, null);
68             }
69         } catch (UncheckedIOException ex) {
70             throw new IOException(ex.getMessage(), ex);
71         }
72     }
73 
74     /**
75      * Print string, replacing all non-ascii character with unicode escapes.
76      */
print(Object s)77     protected void print(Object s) throws IOException {
78         out.write(Convert.escapeUnicode(s.toString()));
79     }
80 
81     /**
82      * Print list.
83      */
print(List<? extends DocTree> list)84     public void print(List<? extends DocTree> list) throws IOException {
85         for (DocTree t: list) {
86             print(t);
87         }
88     }
89 
90     /**
91      * Print list., with separators
92      */
print(List<? extends DocTree> list, String sep)93     protected void print(List<? extends DocTree> list, String sep) throws IOException {
94         if (list.isEmpty())
95             return;
96         boolean first = true;
97         for (DocTree t: list) {
98             if (!first)
99                 print(sep);
100             print(t);
101             first = false;
102         }
103     }
104 
105     /** Print new line.
106      */
println()107     protected void println() throws IOException {
108         out.write(lineSep);
109     }
110 
printTagName(DocTree node)111     protected void printTagName(DocTree node) throws IOException {
112         out.write("@");
113         out.write(node.getKind().tagName);
114     }
115 
116     final String lineSep = System.getProperty("line.separator");
117 
118     /**************************************************************************
119      * Traversal methods
120      *************************************************************************/
121 
122     /** Exception to propagate IOException through visitXXX methods */
123     private static class UncheckedIOException extends Error {
124         static final long serialVersionUID = -4032692679158424751L;
UncheckedIOException(IOException e)125         UncheckedIOException(IOException e) {
126             super(e.getMessage(), e);
127         }
128     }
129 
130 
visitAttribute(AttributeTree node, Void p)131     public Void visitAttribute(AttributeTree node, Void p) {
132         try {
133             print(node.getName());
134             String quote;
135             switch (node.getValueKind()) {
136                 case EMPTY:
137                     quote = null;
138                     break;
139                 case UNQUOTED:
140                     quote = "";
141                     break;
142                 case SINGLE:
143                     quote = "'";
144                     break;
145                 case DOUBLE:
146                     quote = "\"";
147                     break;
148                 default:
149                     throw new AssertionError();
150             }
151             if (quote != null) {
152                 print("=" + quote);
153                 print(node.getValue());
154                 print(quote);
155             }
156         } catch (IOException e) {
157             throw new UncheckedIOException(e);
158         }
159         return null;
160     }
161 
visitAuthor(AuthorTree node, Void p)162     public Void visitAuthor(AuthorTree node, Void p) {
163         try {
164             printTagName(node);
165             print(" ");
166             print(node.getName());
167         } catch (IOException e) {
168             throw new UncheckedIOException(e);
169         }
170         return null;
171     }
172 
visitComment(CommentTree node, Void p)173     public Void visitComment(CommentTree node, Void p) {
174         try {
175             print(node.getBody());
176         } catch (IOException e) {
177             throw new UncheckedIOException(e);
178         }
179         return null;
180     }
181 
visitDeprecated(DeprecatedTree node, Void p)182     public Void visitDeprecated(DeprecatedTree node, Void p) {
183         try {
184             printTagName(node);
185             if (!node.getBody().isEmpty()) {
186                 print(" ");
187                 print(node.getBody());
188             }
189         } catch (IOException e) {
190             throw new UncheckedIOException(e);
191         }
192         return null;
193     }
194 
visitDocComment(DocCommentTree node, Void p)195     public Void visitDocComment(DocCommentTree node, Void p) {
196         try {
197             List<? extends DocTree> fs = node.getFirstSentence();
198             List<? extends DocTree> b = node.getBody();
199             List<? extends DocTree> t = node.getBlockTags();
200             print(fs);
201             if (!fs.isEmpty() && !b.isEmpty())
202                 print(" ");
203             print(b);
204             if ((!fs.isEmpty() || !b.isEmpty()) && !t.isEmpty())
205                 print("\n");
206             print(t, "\n");
207         } catch (IOException e) {
208             throw new UncheckedIOException(e);
209         }
210         return null;
211     }
212 
visitDocRoot(DocRootTree node, Void p)213     public Void visitDocRoot(DocRootTree node, Void p) {
214         try {
215             print("{");
216             printTagName(node);
217             print("}");
218         } catch (IOException e) {
219             throw new UncheckedIOException(e);
220         }
221         return null;
222     }
223 
visitEndElement(EndElementTree node, Void p)224     public Void visitEndElement(EndElementTree node, Void p) {
225         try {
226             print("</");
227             print(node.getName());
228             print(">");
229         } catch (IOException e) {
230             throw new UncheckedIOException(e);
231         }
232         return null;
233     }
234 
visitEntity(EntityTree node, Void p)235     public Void visitEntity(EntityTree node, Void p) {
236         try {
237             print("&");
238             print(node.getName());
239             print(";");
240         } catch (IOException e) {
241             throw new UncheckedIOException(e);
242         }
243         return null;
244     }
245 
visitErroneous(ErroneousTree node, Void p)246     public Void visitErroneous(ErroneousTree node, Void p) {
247         try {
248             print(node.getBody());
249         } catch (IOException e) {
250             throw new UncheckedIOException(e);
251         }
252         return null;
253     }
254 
visitIdentifier(IdentifierTree node, Void p)255     public Void visitIdentifier(IdentifierTree node, Void p) {
256         try {
257             print(node.getName());
258         } catch (IOException e) {
259             throw new UncheckedIOException(e);
260         }
261         return null;
262     }
263 
visitInheritDoc(InheritDocTree node, Void p)264     public Void visitInheritDoc(InheritDocTree node, Void p) {
265         try {
266             print("{");
267             printTagName(node);
268             print("}");
269         } catch (IOException e) {
270             throw new UncheckedIOException(e);
271         }
272         return null;
273     }
274 
visitLink(LinkTree node, Void p)275     public Void visitLink(LinkTree node, Void p) {
276         try {
277             print("{");
278             printTagName(node);
279             print(" ");
280             print(node.getReference());
281             if (!node.getLabel().isEmpty()) {
282                 print(" ");
283                 print(node.getLabel());
284             }
285             print("}");
286         } catch (IOException e) {
287             throw new UncheckedIOException(e);
288         }
289         return null;
290     }
291 
visitLiteral(LiteralTree node, Void p)292     public Void visitLiteral(LiteralTree node, Void p) {
293         try {
294             print("{");
295             printTagName(node);
296             print(" ");
297             print(node.getBody());
298             print("}");
299         } catch (IOException e) {
300             throw new UncheckedIOException(e);
301         }
302         return null;
303     }
304 
visitParam(ParamTree node, Void p)305     public Void visitParam(ParamTree node, Void p) {
306         try {
307             printTagName(node);
308             print(" ");
309             if (node.isTypeParameter()) print("<");
310             print(node.getName());
311             if (node.isTypeParameter()) print(">");
312             if (!node.getDescription().isEmpty()) {
313                 print(" ");
314                 print(node.getDescription());
315             }
316         } catch (IOException e) {
317             throw new UncheckedIOException(e);
318         }
319         return null;
320     }
321 
visitReference(ReferenceTree node, Void p)322     public Void visitReference(ReferenceTree node, Void p) {
323         try {
324             print(node.getSignature());
325         } catch (IOException e) {
326             throw new UncheckedIOException(e);
327         }
328         return null;
329     }
330 
visitReturn(ReturnTree node, Void p)331     public Void visitReturn(ReturnTree node, Void p) {
332         try {
333             printTagName(node);
334             print(" ");
335             print(node.getDescription());
336         } catch (IOException e) {
337             throw new UncheckedIOException(e);
338         }
339         return null;
340     }
341 
visitSee(SeeTree node, Void p)342     public Void visitSee(SeeTree node, Void p) {
343         try {
344             printTagName(node);
345             boolean first = true;
346             boolean needSep = true;
347             for (DocTree t: node.getReference()) {
348                 if (needSep) print(" ");
349                 needSep = (first && (t instanceof ReferenceTree));
350                 first = false;
351                 print(t);
352             }
353         } catch (IOException e) {
354             throw new UncheckedIOException(e);
355         }
356         return null;
357     }
358 
visitSerial(SerialTree node, Void p)359     public Void visitSerial(SerialTree node, Void p) {
360         try {
361             printTagName(node);
362             if (!node.getDescription().isEmpty()) {
363                 print(" ");
364                 print(node.getDescription());
365             }
366         } catch (IOException e) {
367             throw new UncheckedIOException(e);
368         }
369         return null;
370     }
371 
visitSerialData(SerialDataTree node, Void p)372     public Void visitSerialData(SerialDataTree node, Void p) {
373         try {
374             printTagName(node);
375             if (!node.getDescription().isEmpty()) {
376                 print(" ");
377                 print(node.getDescription());
378             }
379         } catch (IOException e) {
380             throw new UncheckedIOException(e);
381         }
382         return null;
383     }
384 
visitSerialField(SerialFieldTree node, Void p)385     public Void visitSerialField(SerialFieldTree node, Void p) {
386         try {
387             printTagName(node);
388             print(" ");
389             print(node.getName());
390             print(" ");
391             print(node.getType());
392             if (!node.getDescription().isEmpty()) {
393                 print(" ");
394                 print(node.getDescription());
395             }
396         } catch (IOException e) {
397             throw new UncheckedIOException(e);
398         }
399         return null;
400     }
401 
visitSince(SinceTree node, Void p)402     public Void visitSince(SinceTree node, Void p) {
403         try {
404             printTagName(node);
405             print(" ");
406             print(node.getBody());
407         } catch (IOException e) {
408             throw new UncheckedIOException(e);
409         }
410         return null;
411     }
412 
visitStartElement(StartElementTree node, Void p)413     public Void visitStartElement(StartElementTree node, Void p) {
414         try {
415             print("<");
416             print(node.getName());
417             List<? extends DocTree> attrs = node.getAttributes();
418             if (!attrs.isEmpty()) {
419                 print(" ");
420                 print(attrs);
421                 DocTree last = node.getAttributes().get(attrs.size() - 1);
422                 if (node.isSelfClosing() && last instanceof AttributeTree
423                         && ((AttributeTree) last).getValueKind() == ValueKind.UNQUOTED)
424                     print(" ");
425             }
426             if (node.isSelfClosing())
427                 print("/");
428             print(">");
429         } catch (IOException e) {
430             throw new UncheckedIOException(e);
431         }
432         return null;
433     }
434 
visitText(TextTree node, Void p)435     public Void visitText(TextTree node, Void p) {
436         try {
437             print(node.getBody());
438         } catch (IOException e) {
439             throw new UncheckedIOException(e);
440         }
441         return null;
442     }
443 
visitThrows(ThrowsTree node, Void p)444     public Void visitThrows(ThrowsTree node, Void p) {
445         try {
446             printTagName(node);
447             print(" ");
448             print(node.getExceptionName());
449             if (!node.getDescription().isEmpty()) {
450                 print(" ");
451                 print(node.getDescription());
452             }
453         } catch (IOException e) {
454             throw new UncheckedIOException(e);
455         }
456         return null;
457     }
458 
visitUnknownBlockTag(UnknownBlockTagTree node, Void p)459     public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
460         try {
461             print("@");
462             print(node.getTagName());
463             print(" ");
464             print(node.getContent());
465         } catch (IOException e) {
466             throw new UncheckedIOException(e);
467         }
468         return null;
469     }
470 
visitUnknownInlineTag(UnknownInlineTagTree node, Void p)471     public Void visitUnknownInlineTag(UnknownInlineTagTree node, Void p) {
472         try {
473             print("{");
474             print("@");
475             print(node.getTagName());
476             print(" ");
477             print(node.getContent());
478             print("}");
479         } catch (IOException e) {
480             throw new UncheckedIOException(e);
481         }
482         return null;
483     }
484 
visitValue(ValueTree node, Void p)485     public Void visitValue(ValueTree node, Void p) {
486         try {
487             print("{");
488             printTagName(node);
489             if (node.getReference() != null) {
490                 print(" ");
491                 print(node.getReference());
492             }
493             print("}");
494         } catch (IOException e) {
495             throw new UncheckedIOException(e);
496         }
497         return null;
498     }
499 
visitVersion(VersionTree node, Void p)500     public Void visitVersion(VersionTree node, Void p) {
501         try {
502             printTagName(node);
503             print(" ");
504             print(node.getBody());
505         } catch (IOException e) {
506             throw new UncheckedIOException(e);
507         }
508         return null;
509     }
510 
visitOther(DocTree node, Void p)511     public Void visitOther(DocTree node, Void p) {
512         try {
513             print("(UNKNOWN: " + node + ")");
514             println();
515         } catch (IOException e) {
516             throw new UncheckedIOException(e);
517         }
518         return null;
519     }
520 }
521