1 /*
   2  * Copyright (c) 2003, 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.util;
  27 
  28 import java.util.*;
  29 
  30 import com.sun.javadoc.*;
  31 import com.sun.tools.doclets.internal.toolkit.Configuration;
  32 import com.sun.tools.doclets.internal.toolkit.taglets.*;
  33 
  34 /**
  35  * Search for the requested documentation.  Inherit documentation if necessary.
  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.5
  44  */
  45 public class DocFinder {
  46 
  47     /**
  48      * The class that encapsulates the input.
  49      */
  50     public static class Input {
  51         /**
  52          * The element to search documentation from.
  53          */
  54         public ProgramElementDoc element;
  55         /**
  56          * The taglet to search for documentation on behalf of. Null if we want
  57          * to search for overall documentation.
  58          */
  59         public InheritableTaglet taglet = null;
  60 
  61         /**
  62          * The id of the tag to retrieve documentation for.
  63          */
  64         public String tagId = null;
  65 
  66         /**
  67          * The tag to retrieve documentation for.  This is only used for the
  68          * inheritDoc tag.
  69          */
  70         public Tag tag = null;
  71 
  72         /**
  73          * True if we only want to search for the first sentence.
  74          */
  75         public boolean isFirstSentence = false;
  76 
  77         /**
  78          * True if we are looking for documentation to replace the inheritDocTag.
  79          */
  80         public boolean isInheritDocTag = false;
  81 
  82         /**
  83          * Used to distinguish between type variable param tags and regular
  84          * param tags.
  85          */
  86         public boolean isTypeVariableParamTag = false;
  87 
  88         public Input(ProgramElementDoc element, InheritableTaglet taglet, Tag tag,
  89                 boolean isFirstSentence, boolean isInheritDocTag) {
  90             this(element);
  91             this.taglet = taglet;
  92             this.tag = tag;
  93             this.isFirstSentence = isFirstSentence;
  94             this.isInheritDocTag = isInheritDocTag;
  95         }
  96 
  97         public Input(ProgramElementDoc element, InheritableTaglet taglet, String tagId) {
  98             this(element);
  99             this.taglet = taglet;
 100             this.tagId = tagId;
 101         }
 102 
 103         public Input(ProgramElementDoc element, InheritableTaglet taglet, String tagId,
 104             boolean isTypeVariableParamTag) {
 105             this(element);
 106             this.taglet = taglet;
 107             this.tagId = tagId;
 108             this.isTypeVariableParamTag = isTypeVariableParamTag;
 109         }
 110 
 111         public Input(ProgramElementDoc element, InheritableTaglet taglet) {
 112             this(element);
 113             this.taglet = taglet;
 114         }
 115 
 116         public Input(ProgramElementDoc element) {
 117             if (element == null)
 118                 throw new NullPointerException();
 119             this.element = element;
 120         }
 121 
 122         public Input(ProgramElementDoc element, boolean isFirstSentence) {
 123             this(element);
 124             this.isFirstSentence = isFirstSentence;
 125         }
 126 
 127         public Input copy() {
 128             Input clone = new Input(this.element);
 129             clone.taglet = this.taglet;
 130             clone.tagId = this.tagId;
 131             clone.tag = this.tag;
 132             clone.isFirstSentence = this.isFirstSentence;
 133             clone.isInheritDocTag = this.isInheritDocTag;
 134             clone.isTypeVariableParamTag = this.isTypeVariableParamTag;
 135             if (clone.element == null)
 136                 throw new NullPointerException();
 137             return clone;
 138 
 139         }
 140     }
 141 
 142     /**
 143      * The class that encapsulates the output.
 144      */
 145     public static class Output {
 146         /**
 147          * The tag that holds the documentation.  Null if documentation
 148          * is not held by a tag.
 149          */
 150         public Tag holderTag;
 151 
 152         /**
 153          * The Doc object that holds the documentation.
 154          */
 155         public Doc holder;
 156 
 157         /**
 158          * The inherited documentation.
 159          */
 160         public Tag[] inlineTags = new Tag[] {};
 161 
 162         /**
 163          * False if documentation could not be inherited.
 164          */
 165         public boolean isValidInheritDocTag = true;
 166 
 167         /**
 168          * When automatically inheriting throws tags, you sometime must inherit
 169          * more than one tag.  For example if the element declares that it throws
 170          * IOException and the overridden element has throws tags for IOException and
 171          * ZipException, both tags would be inherited because ZipException is a
 172          * subclass of IOException.  This subclass of DocFinder.Output allows
 173          * multiple tag inheritence.
 174          */
 175         public List<Tag> tagList  = new ArrayList<>();
 176     }
 177 
 178     /**
 179      * Search for the requested comments in the given element.  If it does not
 180      * have comments, return documentation from the overriden element if possible.
 181      * If the overriden element does not exist or does not have documentation to
 182      * inherit, search for documentation to inherit from implemented methods.
 183      *
 184      * @param input the input object used to perform the search.
 185      *
 186      * @return an Output object representing the documentation that was found.
 187      */
 188     public static Output search(Configuration configuration, Input input) {
 189         Output output = new Output();
 190         if (input.isInheritDocTag) {
 191             //Do nothing because "element" does not have any documentation.
 192             //All it has it {@inheritDoc}.
 193         } else if (input.taglet == null) {
 194             //We want overall documentation.
 195             output.inlineTags = input.isFirstSentence ?
 196                 input.element.firstSentenceTags() :
 197                 input.element.inlineTags();
 198             output.holder = input.element;
 199         } else {
 200             input.taglet.inherit(input, output);
 201         }
 202 
 203         if (output.inlineTags != null && output.inlineTags.length > 0) {
 204             return output;
 205         }
 206         output.isValidInheritDocTag = false;
 207         Input inheritedSearchInput = input.copy();
 208         inheritedSearchInput.isInheritDocTag = false;
 209         if (input.element instanceof MethodDoc) {
 210             MethodDoc overriddenMethod = ((MethodDoc) input.element).overriddenMethod();
 211             if (overriddenMethod != null) {
 212                 inheritedSearchInput.element = overriddenMethod;
 213                 output = search(configuration, inheritedSearchInput);
 214                 output.isValidInheritDocTag = true;
 215                 if (output.inlineTags.length > 0) {
 216                     return output;
 217                 }
 218             }
 219             //NOTE:  When we fix the bug where ClassDoc.interfaceTypes() does
 220             //       not pass all implemented interfaces, we will use the
 221             //       appropriate element here.
 222             MethodDoc[] implementedMethods =
 223                 (new ImplementedMethods((MethodDoc) input.element, configuration)).build(false);
 224             for (MethodDoc implementedMethod : implementedMethods) {
 225                 inheritedSearchInput.element = implementedMethod;
 226                 output = search(configuration, inheritedSearchInput);
 227                 output.isValidInheritDocTag = true;
 228                 if (output.inlineTags.length > 0) {
 229                     return output;
 230                 }
 231             }
 232         } else if (input.element instanceof ClassDoc) {
 233             ProgramElementDoc superclass = ((ClassDoc) input.element).superclass();
 234             if (superclass != null) {
 235                 inheritedSearchInput.element = superclass;
 236                 output = search(configuration, inheritedSearchInput);
 237                 output.isValidInheritDocTag = true;
 238                 if (output.inlineTags.length > 0) {
 239                     return output;
 240                 }
 241             }
 242         }
 243         return output;
 244     }
 245 }