1 /*
   2  * Copyright (c) 2001, 2014, 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.doclets.internal.toolkit.taglets;
  27 
  28 import java.util.*;
  29 
  30 import com.sun.javadoc.*;
  31 import com.sun.tools.doclets.internal.toolkit.Content;
  32 import com.sun.tools.doclets.internal.toolkit.util.*;
  33 
  34 /**
  35  * A taglet that represents the @throws tag.
  36  *
  37  *  <p><b>This is NOT part of any supported API.
  38  *  If you write code that depends on this, you do so at your own risk.
  39  *  This code and its internal interfaces are subject to change or
  40  *  deletion without notice.</b>
  41  *
  42  * @author Jamie Ho
  43  * @since 1.4
  44  */
  45 public class ThrowsTaglet extends BaseExecutableMemberTaglet
  46     implements InheritableTaglet {
  47 
  48     public ThrowsTaglet() {
  49         name = "throws";
  50     }
  51 
  52     /**
  53      * {@inheritDoc}
  54      */
  55     public void inherit(DocFinder.Input input, DocFinder.Output output) {
  56         ClassDoc exception;
  57         if (input.tagId == null) {
  58             ThrowsTag throwsTag = (ThrowsTag) input.tag;
  59             exception = throwsTag.exception();
  60             input.tagId = exception == null ?
  61                 throwsTag.exceptionName() :
  62                 throwsTag.exception().qualifiedName();
  63         } else {
  64             exception = input.element.containingClass().findClass(input.tagId);
  65         }
  66 
  67         for (ThrowsTag tag : ((MethodDoc)input.element).throwsTags()) {
  68             if (input.tagId.equals(tag.exceptionName()) ||
  69                 (tag.exception() != null &&
  70                  (input.tagId.equals(tag.exception().qualifiedName())))) {
  71                 output.holder = input.element;
  72                 output.holderTag = tag;
  73                 output.inlineTags = input.isFirstSentence ?
  74                                     tag.firstSentenceTags() : tag.inlineTags();
  75                 output.tagList.add(tag);
  76             } else if (exception != null && tag.exception() != null &&
  77                      tag.exception().subclassOf(exception)) {
  78                 output.tagList.add(tag);
  79             }
  80         }
  81     }
  82 
  83     /**
  84      * Add links for exceptions that are declared but not documented.
  85      */
  86     private Content linkToUndocumentedDeclaredExceptions(
  87             Type[] declaredExceptionTypes, Set<String> alreadyDocumented,
  88             TagletWriter writer) {
  89         Content result = writer.getOutputInstance();
  90         //Add links to the exceptions declared but not documented.
  91         for (Type declaredExceptionType : declaredExceptionTypes) {
  92             if (declaredExceptionType.asClassDoc() != null &&
  93                 !alreadyDocumented.contains(
  94                         declaredExceptionType.asClassDoc().name()) &&
  95                 !alreadyDocumented.contains(
  96                         declaredExceptionType.asClassDoc().qualifiedName())) {
  97                 if (alreadyDocumented.size() == 0) {
  98                     result.addContent(writer.getThrowsHeader());
  99                 }
 100                 result.addContent(writer.throwsTagOutput(declaredExceptionType));
 101                 alreadyDocumented.add(declaredExceptionType.asClassDoc().name());
 102             }
 103         }
 104         return result;
 105     }
 106 
 107     /**
 108      * Inherit throws documentation for exceptions that were declared but not
 109      * documented.
 110      */
 111     private Content inheritThrowsDocumentation(Doc holder,
 112             Type[] declaredExceptionTypes, Set<String> alreadyDocumented,
 113             TagletWriter writer) {
 114         Content result = writer.getOutputInstance();
 115         if (holder instanceof MethodDoc) {
 116             Set<Tag> declaredExceptionTags = new LinkedHashSet<>();
 117             for (Type declaredExceptionType : declaredExceptionTypes) {
 118                 DocFinder.Output inheritedDoc =
 119                         DocFinder.search(writer.configuration(), new DocFinder.Input((MethodDoc) holder, this,
 120                                                              declaredExceptionType.typeName()));
 121                 if (inheritedDoc.tagList.size() == 0) {
 122                     inheritedDoc = DocFinder.search(writer.configuration(), new DocFinder.Input(
 123                             (MethodDoc) holder, this,
 124                             declaredExceptionType.qualifiedTypeName()));
 125                 }
 126                 declaredExceptionTags.addAll(inheritedDoc.tagList);
 127             }
 128             result.addContent(throwsTagsOutput(
 129                 declaredExceptionTags.toArray(new ThrowsTag[] {}),
 130                 writer, alreadyDocumented, false));
 131         }
 132         return result;
 133     }
 134 
 135     /**
 136      * {@inheritDoc}
 137      */
 138     public Content getTagletOutput(Doc holder, TagletWriter writer) {
 139         ExecutableMemberDoc execHolder = (ExecutableMemberDoc) holder;
 140         ThrowsTag[] tags = execHolder.throwsTags();
 141         Content result = writer.getOutputInstance();
 142         HashSet<String> alreadyDocumented = new HashSet<>();
 143         if (tags.length > 0) {
 144             result.addContent(throwsTagsOutput(
 145                 execHolder.throwsTags(), writer, alreadyDocumented, true));
 146         }
 147         result.addContent(inheritThrowsDocumentation(holder,
 148             execHolder.thrownExceptionTypes(), alreadyDocumented, writer));
 149         result.addContent(linkToUndocumentedDeclaredExceptions(
 150             execHolder.thrownExceptionTypes(), alreadyDocumented, writer));
 151         return result;
 152     }
 153 
 154 
 155     /**
 156      * Given an array of <code>Tag</code>s representing this custom
 157      * tag, return its string representation.
 158      * @param throwTags the array of <code>ThrowsTag</code>s to convert.
 159      * @param writer the TagletWriter that will write this tag.
 160      * @param alreadyDocumented the set of exceptions that have already
 161      *        been documented.
 162      * @param allowDups True if we allow duplicate throws tags to be documented.
 163      * @return the Content representation of this <code>Tag</code>.
 164      */
 165     protected Content throwsTagsOutput(ThrowsTag[] throwTags,
 166         TagletWriter writer, Set<String> alreadyDocumented, boolean allowDups) {
 167         Content result = writer.getOutputInstance();
 168         if (throwTags.length > 0) {
 169             for (ThrowsTag tt : throwTags) {
 170                 ClassDoc cd = tt.exception();
 171                 if ((!allowDups) && (alreadyDocumented.contains(tt.exceptionName()) ||
 172                                      (cd != null && alreadyDocumented.contains(cd.qualifiedName())))) {
 173                     continue;
 174                 }
 175                 if (alreadyDocumented.size() == 0) {
 176                     result.addContent(writer.getThrowsHeader());
 177                 }
 178                 result.addContent(writer.throwsTagOutput(tt));
 179                 alreadyDocumented.add(cd != null ?
 180                                       cd.qualifiedName() : tt.exceptionName());
 181             }
 182         }
 183         return result;
 184     }
 185 }