1 /*
2  * Copyright (c) 1997, 2016, 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.javadoc;
27 
28 import java.io.DataInputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.text.CollationKey;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34 
35 import javax.tools.FileObject;
36 
37 import com.sun.javadoc.*;
38 import com.sun.source.util.TreePath;
39 import com.sun.tools.doclets.internal.toolkit.util.FatalError;
40 import com.sun.tools.javac.tree.JCTree;
41 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
42 import com.sun.tools.javac.util.Position;
43 
44 /**
45  * abstract base class of all Doc classes.  Doc item's are representations
46  * of java language constructs (class, package, method,...) which have
47  * comments and have been processed by this run of javadoc.  All Doc items
48  * are unique, that is, they are == comparable.
49  *
50  *  <p><b>This is NOT part of any supported API.
51  *  If you write code that depends on this, you do so at your own risk.
52  *  This code and its internal interfaces are subject to change or
53  *  deletion without notice.</b>
54  *
55  * @since 1.2
56  * @author Robert Field
57  * @author Atul M Dambalkar
58  * @author Neal Gafter (rewrite)
59  */
60 public abstract class DocImpl implements Doc, Comparable<Object> {
61 
62     /**
63      * Doc environment
64      */
65     protected final DocEnv env;   //### Rename this everywhere to 'docenv' ?
66 
67     /**
68      * Back pointer to the tree node for this doc item.
69      * May be null if there is no associated tree.
70      */
71     protected TreePath treePath;
72 
73     /**
74      *  The complex comment object, lazily initialized.
75      */
76     private Comment comment;
77 
78     /**
79      * The cached sort key, to take care of Natural Language Text sorting.
80      */
81     private CollationKey collationkey = null;
82 
83     /**
84      *  Raw documentation string.
85      */
86     protected String documentation;  // Accessed in PackageDocImpl, RootDocImpl
87 
88     /**
89      * Cached first sentence.
90      */
91     private Tag[] firstSentence;
92 
93     /**
94      * Cached inline tags.
95      */
96     private Tag[] inlineTags;
97 
98     /**
99      * Constructor.
100      */
DocImpl(DocEnv env, TreePath treePath)101     DocImpl(DocEnv env, TreePath treePath) {
102         this.treePath = treePath;
103         this.documentation = getCommentText(treePath);
104         this.env = env;
105     }
106 
getCommentText(TreePath p)107     private static String getCommentText(TreePath p) {
108         if (p == null)
109             return null;
110 
111         JCCompilationUnit topLevel = (JCCompilationUnit) p.getCompilationUnit();
112         JCTree tree = (JCTree) p.getLeaf();
113         return topLevel.docComments.getCommentText(tree);
114     }
115 
116     /**
117      * So subclasses have the option to do lazy initialization of
118      * "documentation" string.
119      */
documentation()120     protected String documentation() {
121         if (documentation == null) documentation = "";
122         return documentation;
123     }
124 
125     /**
126      * For lazy initialization of comment.
127      */
comment()128     Comment comment() {
129         if (comment == null) {
130             String d = documentation();
131             if (env.javaScriptScanner != null) {
132                 env.javaScriptScanner.parse(d, new JavaScriptScanner.Reporter() {
133                     @Override
134                     public void report() {
135                         env.error(DocImpl.this, "javadoc.JavaScript_in_comment");
136                         throw new FatalError();
137                     }
138                 });
139             }
140             if (env.doclint != null
141                     && treePath != null
142                     && d.equals(getCommentText(treePath))) {
143                 env.doclint.scan(treePath);
144             }
145             comment = new Comment(this, d);
146         }
147         return comment;
148     }
149 
150     /**
151      * Return the text of the comment for this doc item.
152      * TagImpls have been removed.
153      */
commentText()154     public String commentText() {
155         return comment().commentText();
156     }
157 
158     /**
159      * Return all tags in this Doc item.
160      *
161      * @return an array of TagImpl containing all tags on this Doc item.
162      */
tags()163     public Tag[] tags() {
164         return comment().tags();
165     }
166 
167     /**
168      * Return tags of the specified kind in this Doc item.
169      *
170      * @param tagname name of the tag kind to search for.
171      * @return an array of TagImpl containing all tags whose 'kind()'
172      * matches 'tagname'.
173      */
tags(String tagname)174     public Tag[] tags(String tagname) {
175         return comment().tags(tagname);
176     }
177 
178     /**
179      * Return the see also tags in this Doc item.
180      *
181      * @return an array of SeeTag containing all &#64;see tags.
182      */
seeTags()183     public SeeTag[] seeTags() {
184         return comment().seeTags();
185     }
186 
inlineTags()187     public Tag[] inlineTags() {
188         if (inlineTags == null) {
189             inlineTags = Comment.getInlineTags(this, commentText());
190         }
191         return inlineTags;
192     }
193 
firstSentenceTags()194     public Tag[] firstSentenceTags() {
195         if (firstSentence == null) {
196             //Parse all sentences first to avoid duplicate warnings.
197             inlineTags();
198             try {
199                 env.setSilent(true);
200                 firstSentence = Comment.firstSentenceTags(this, commentText());
201             } finally {
202                 env.setSilent(false);
203             }
204         }
205         return firstSentence;
206     }
207 
208     /**
209      * Utility for subclasses which read HTML documentation files.
210      */
readHTMLDocumentation(InputStream input, FileObject filename)211     String readHTMLDocumentation(InputStream input, FileObject filename) throws IOException {
212         byte[] filecontents = new byte[input.available()];
213         try {
214             DataInputStream dataIn = new DataInputStream(input);
215             dataIn.readFully(filecontents);
216         } finally {
217             input.close();
218         }
219         String encoding = env.getEncoding();
220         String rawDoc = (encoding!=null)
221             ? new String(filecontents, encoding)
222             : new String(filecontents);
223         Pattern bodyPat = Pattern.compile("(?is).*<body\\b[^>]*>(.*)</body\\b.*");
224         Matcher m = bodyPat.matcher(rawDoc);
225         if (m.matches()) {
226             return m.group(1);
227         } else {
228             String key = rawDoc.matches("(?is).*<body\\b.*")
229                     ? "javadoc.End_body_missing_from_html_file"
230                     : "javadoc.Body_missing_from_html_file";
231             env.error(SourcePositionImpl.make(filename, Position.NOPOS, null), key);
232             return "";
233         }
234     }
235 
236     /**
237      * Return the full unprocessed text of the comment.  Tags
238      * are included as text.  Used mainly for store and retrieve
239      * operations like internalization.
240      */
getRawCommentText()241     public String getRawCommentText() {
242         return documentation();
243     }
244 
245     /**
246      * Set the full unprocessed text of the comment.  Tags
247      * are included as text.  Used mainly for store and retrieve
248      * operations like internalization.
249      */
setRawCommentText(String rawDocumentation)250     public void setRawCommentText(String rawDocumentation) {
251         treePath = null;
252         documentation = rawDocumentation;
253         comment = null;
254     }
255 
256     /**
257      * Set the full unprocessed text of the comment and tree path.
258      */
setTreePath(TreePath treePath)259     void setTreePath(TreePath treePath) {
260         this.treePath = treePath;
261         documentation = getCommentText(treePath);
262         comment = null;
263     }
264 
265     /**
266      * return a key for sorting.
267      */
key()268     CollationKey key() {
269         if (collationkey == null) {
270             collationkey = generateKey();
271         }
272         return collationkey;
273     }
274 
275     /**
276      * Generate a key for sorting.
277      * <p>
278      * Default is name().
279      */
generateKey()280     CollationKey generateKey() {
281         String k = name();
282         // System.out.println("COLLATION KEY FOR " + this + " is \"" + k + "\"");
283         return env.doclocale.collator.getCollationKey(k);
284     }
285 
286     /**
287      * Returns a string representation of this Doc item.
288      */
289     @Override
toString()290     public String toString() {
291         return qualifiedName();
292     }
293 
294     /**
295      * Returns the name of this Doc item.
296      *
297      * @return  the name
298      */
name()299     public abstract String name();
300 
301     /**
302      * Returns the qualified name of this Doc item.
303      *
304      * @return  the name
305      */
qualifiedName()306     public abstract String qualifiedName();
307 
308     /**
309      * Compares this Object with the specified Object for order.  Returns a
310      * negative integer, zero, or a positive integer as this Object is less
311      * than, equal to, or greater than the given Object.
312      * <p>
313      * Included so that Doc item are java.lang.Comparable.
314      *
315      * @param   obj the {@code Object} to be compared.
316      * @return  a negative integer, zero, or a positive integer as this Object
317      *          is less than, equal to, or greater than the given Object.
318      * @exception ClassCastException the specified Object's type prevents it
319      *            from being compared to this Object.
320      */
compareTo(Object obj)321     public int compareTo(Object obj) {
322         // System.out.println("COMPARE \"" + this + "\" to \"" + obj + "\" = " + key().compareTo(((DocImpl)obj).key()));
323         return key().compareTo(((DocImpl)obj).key());
324     }
325 
326     /**
327      * Is this Doc item a field?  False until overridden.
328      *
329      * @return true if it represents a field
330      */
isField()331     public boolean isField() {
332         return false;
333     }
334 
335     /**
336      * Is this Doc item an enum constant?  False until overridden.
337      *
338      * @return true if it represents an enum constant
339      */
isEnumConstant()340     public boolean isEnumConstant() {
341         return false;
342     }
343 
344     /**
345      * Is this Doc item a constructor?  False until overridden.
346      *
347      * @return true if it represents a constructor
348      */
isConstructor()349     public boolean isConstructor() {
350         return false;
351     }
352 
353     /**
354      * Is this Doc item a method (but not a constructor or annotation
355      * type element)?
356      * False until overridden.
357      *
358      * @return true if it represents a method
359      */
isMethod()360     public boolean isMethod() {
361         return false;
362     }
363 
364     /**
365      * Is this Doc item an annotation type element?
366      * False until overridden.
367      *
368      * @return true if it represents an annotation type element
369      */
isAnnotationTypeElement()370     public boolean isAnnotationTypeElement() {
371         return false;
372     }
373 
374     /**
375      * Is this Doc item a interface (but not an annotation type)?
376      * False until overridden.
377      *
378      * @return true if it represents a interface
379      */
isInterface()380     public boolean isInterface() {
381         return false;
382     }
383 
384     /**
385      * Is this Doc item a exception class?  False until overridden.
386      *
387      * @return true if it represents a exception
388      */
isException()389     public boolean isException() {
390         return false;
391     }
392 
393     /**
394      * Is this Doc item a error class?  False until overridden.
395      *
396      * @return true if it represents a error
397      */
isError()398     public boolean isError() {
399         return false;
400     }
401 
402     /**
403      * Is this Doc item an enum type?  False until overridden.
404      *
405      * @return true if it represents an enum type
406      */
isEnum()407     public boolean isEnum() {
408         return false;
409     }
410 
411     /**
412      * Is this Doc item an annotation type?  False until overridden.
413      *
414      * @return true if it represents an annotation type
415      */
isAnnotationType()416     public boolean isAnnotationType() {
417         return false;
418     }
419 
420     /**
421      * Is this Doc item an ordinary class (i.e. not an interface,
422      * annotation type, enumeration, exception, or error)?
423      * False until overridden.
424      *
425      * @return true if it represents an ordinary class
426      */
isOrdinaryClass()427     public boolean isOrdinaryClass() {
428         return false;
429     }
430 
431     /**
432      * Is this Doc item a class
433      * (and not an interface or annotation type)?
434      * This includes ordinary classes, enums, errors and exceptions.
435      * False until overridden.
436      *
437      * @return true if it represents a class
438      */
isClass()439     public boolean isClass() {
440         return false;
441     }
442 
443     /**
444      * return true if this Doc is include in the active set.
445      */
isIncluded()446     public abstract boolean isIncluded();
447 
448     /**
449      * Return the source position of the entity, or null if
450      * no position is available.
451      */
position()452     public SourcePosition position() { return null; }
453 }
454