ThrowsTaglet.java revision 3233:b5d08bc0d224
1/*
2 * Copyright (c) 2001, 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
26package jdk.javadoc.internal.doclets.toolkit.taglets;
27
28import java.util.*;
29import java.util.Map.Entry;
30
31import javax.lang.model.element.Element;
32import javax.lang.model.element.ExecutableElement;
33import javax.lang.model.element.TypeElement;
34import javax.lang.model.type.TypeMirror;
35
36import com.sun.source.doctree.DocTree;
37import jdk.javadoc.internal.doclets.toolkit.Content;
38import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
39import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
40import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Input;
41import jdk.javadoc.internal.doclets.toolkit.util.Utils;
42
43import static com.sun.source.doctree.DocTree.Kind.*;
44
45/**
46 * A taglet that represents the @throws tag.
47 *
48 *  <p><b>This is NOT part of any supported API.
49 *  If you write code that depends on this, you do so at your own risk.
50 *  This code and its internal interfaces are subject to change or
51 *  deletion without notice.</b>
52 *
53 * @author Jamie Ho
54 */
55public class ThrowsTaglet extends BaseExecutableMemberTaglet
56    implements InheritableTaglet {
57
58    public ThrowsTaglet() {
59        name = THROWS.tagName;
60    }
61
62    /**
63     * {@inheritDoc}
64     */
65    @Override
66    public void inherit(DocFinder.Input input, DocFinder.Output output) {
67        Utils utils = input.utils;
68        Element exception;
69        CommentHelper ch = utils.getCommentHelper(input.element);
70        if (input.tagId == null) {
71            exception = ch.getException(utils.configuration, input.docTreeInfo.docTree);
72            input.tagId = exception == null
73                    ? ch.getExceptionName(input.docTreeInfo.docTree).getSignature()
74                    : utils.getFullyQualifiedName(exception);
75        } else {
76            TypeElement element = input.utils.findClass(input.element, input.tagId);
77            exception = (element == null) ? null : element;
78        }
79
80        for (DocTree dt : input.utils.getThrowsTrees(input.element)) {
81            Element texception = ch.getException(utils.configuration, dt);
82            if (texception != null && (input.tagId.equals(utils.getSimpleName(texception)) ||
83                 (input.tagId.equals(utils.getFullyQualifiedName(texception))))) {
84                output.holder = input.element;
85                output.holderTag = dt;
86                output.inlineTags = ch.getBody(input.utils.configuration, output.holderTag);
87                output.tagList.add(dt);
88            } else if (exception != null && texception != null &&
89                    utils.isTypeElement(texception) && utils.isTypeElement(exception) &&
90                    utils.isSubclassOf((TypeElement)texception, (TypeElement)exception)) {
91                output.tagList.add(dt);
92            }
93        }
94    }
95
96    /**
97     * Add links for exceptions that are declared but not documented.
98     */
99    private Content linkToUndocumentedDeclaredExceptions(List<? extends TypeMirror> declaredExceptionTypes,
100            Set<String> alreadyDocumented, TagletWriter writer) {
101        Utils utils = writer.configuration().utils;
102        Content result = writer.getOutputInstance();
103        //Add links to the exceptions declared but not documented.
104        for (TypeMirror declaredExceptionType : declaredExceptionTypes) {
105            TypeElement klass = utils.asTypeElement(declaredExceptionType);
106            if (klass != null &&
107                !alreadyDocumented.contains(utils.getSimpleName(klass)) &&
108                !alreadyDocumented.contains(utils.getFullyQualifiedName(klass))) {
109                if (alreadyDocumented.isEmpty()) {
110                    result.addContent(writer.getThrowsHeader());
111                }
112                result.addContent(writer.throwsTagOutput(declaredExceptionType));
113                alreadyDocumented.add(utils.getSimpleName(klass));
114            }
115        }
116        return result;
117    }
118
119    /**
120     * Inherit throws documentation for exceptions that were declared but not
121     * documented.
122     */
123    private Content inheritThrowsDocumentation(Element holder,
124            List<? extends TypeMirror> declaredExceptionTypes, Set<String> alreadyDocumented,
125            TagletWriter writer) {
126        Utils utils = writer.configuration().utils;
127        Content result = writer.getOutputInstance();
128        if (utils.isExecutableElement(holder)) {
129            Map<List<? extends DocTree>, ExecutableElement> declaredExceptionTags = new LinkedHashMap<>();
130            for (TypeMirror declaredExceptionType : declaredExceptionTypes) {
131                Input input = new DocFinder.Input(utils, holder, this,
132                        utils.getTypeName(declaredExceptionType, false));
133                DocFinder.Output inheritedDoc = DocFinder.search(writer.configuration(), input);
134                if (inheritedDoc.tagList.isEmpty()) {
135                    String typeName = utils.getTypeName(declaredExceptionType, true);
136                    input = new DocFinder.Input(utils, holder, this, typeName);
137                    inheritedDoc = DocFinder.search(writer.configuration(), input);
138                }
139                if (!inheritedDoc.tagList.isEmpty()) {
140                    if (inheritedDoc.holder == null) {
141                        inheritedDoc.holder = holder;
142                    }
143                    declaredExceptionTags.put(inheritedDoc.tagList, (ExecutableElement)inheritedDoc.holder);
144                }
145            }
146            result.addContent(throwsTagsOutput(declaredExceptionTags, writer, alreadyDocumented, false));
147        }
148        return result;
149    }
150
151    /**
152     * {@inheritDoc}
153     */
154    public Content getTagletOutput(Element holder, TagletWriter writer) {
155        Utils utils = writer.configuration().utils;
156        ExecutableElement execHolder = (ExecutableElement) holder;
157        Map<List<? extends DocTree>, ExecutableElement> tagsMap = new LinkedHashMap<>();
158        tagsMap.put(utils.getThrowsTrees(execHolder), execHolder);
159        Content result = writer.getOutputInstance();
160        HashSet<String> alreadyDocumented = new HashSet<>();
161        if (!tagsMap.isEmpty()) {
162            result.addContent(throwsTagsOutput(tagsMap, writer, alreadyDocumented, true));
163        }
164        result.addContent(inheritThrowsDocumentation(holder,
165            execHolder.getThrownTypes(), alreadyDocumented, writer));
166        result.addContent(linkToUndocumentedDeclaredExceptions(
167            execHolder.getThrownTypes(), alreadyDocumented, writer));
168        return result;
169    }
170
171    /**
172     * Given an array of <code>Tag</code>s representing this custom
173     * tag, return its string representation.
174     * @param throwTags the array of <code>ThrowsTag</code>s to convert.
175     * @param writer the TagletWriter that will write this tag.
176     * @param alreadyDocumented the set of exceptions that have already
177     *        been documented.
178     * @param allowDups True if we allow duplicate throws tags to be documented.
179     * @return the Content representation of this <code>Tag</code>.
180     */
181    protected Content throwsTagsOutput(Map<List<? extends DocTree>, ExecutableElement> throwTags,
182        TagletWriter writer, Set<String> alreadyDocumented, boolean allowDups) {
183        Utils utils = writer.configuration().utils;
184        Content result = writer.getOutputInstance();
185        if (!throwTags.isEmpty()) {
186            for (Entry<List<? extends DocTree>, ExecutableElement> entry : throwTags.entrySet()) {
187                CommentHelper ch = utils.getCommentHelper(entry.getValue());
188                Element e = entry.getValue();
189                for (DocTree dt : entry.getKey()) {
190                    Element te = ch.getException(utils.configuration, dt);
191                    String excName = ch.getExceptionName(dt).toString();
192                    if ((!allowDups) &&
193                        (alreadyDocumented.contains(excName) ||
194                        (te != null && alreadyDocumented.contains(utils.getFullyQualifiedName(te))))) {
195                        continue;
196                    }
197                    if (alreadyDocumented.isEmpty()) {
198                        result.addContent(writer.getThrowsHeader());
199                    }
200                    result.addContent(writer.throwsTagOutput(e, dt));
201                    alreadyDocumented.add(te != null
202                            ? utils.getFullyQualifiedName(te)
203                            : excName);
204                }
205            }
206        }
207        return result;
208    }
209}
210