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