1 /*
   2  * Copyright (c) 2001, 2015, 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 jdk.javadoc.internal.doclets.toolkit.taglets;
  27 
  28 
  29 import javax.lang.model.element.Element;
  30 import javax.lang.model.element.TypeElement;
  31 import javax.lang.model.element.VariableElement;
  32 import javax.lang.model.util.Elements;
  33 
  34 import com.sun.source.doctree.DocTree;
  35 import jdk.javadoc.internal.doclets.toolkit.Configuration;
  36 import jdk.javadoc.internal.doclets.toolkit.Content;
  37 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
  38 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
  39 
  40 import static com.sun.source.doctree.DocTree.Kind.*;
  41 
  42 /**
  43  * An inline Taglet representing the value tag. This tag should only be used with
  44  * constant fields that have a value.  It is used to access the value of constant
  45  * fields.  This inline tag has an optional field name parameter.  If the name is
  46  * specified, the constant value is retrieved from the specified field.  A link
  47  * is also created to the specified field.  If a name is not specified, the value
  48  * is retrieved for the field that the inline tag appears on.  The name is specifed
  49  * in the following format:  [fully qualified class name]#[constant field name].
  50  *
  51  *  <p><b>This is NOT part of any supported API.
  52  *  If you write code that depends on this, you do so at your own risk.
  53  *  This code and its internal interfaces are subject to change or
  54  *  deletion without notice.</b>
  55  *
  56  * @author Jamie Ho
  57  * @since 1.4
  58  */
  59 
  60 public class ValueTaglet extends BaseInlineTaglet {
  61 
  62     /**
  63      * Construct a new ValueTaglet.
  64      */
  65     public ValueTaglet() {
  66         name = VALUE.tagName;
  67     }
  68 
  69     /**
  70      * Will return false because this inline tag may
  71      * only appear in Fields.
  72      * @return false since this is not a method.
  73      */
  74     public boolean inMethod() {
  75         return true;
  76     }
  77 
  78     /**
  79      * Will return false because this inline tag may
  80      * only appear in Fields.
  81      * @return false since this is not a method.
  82      */
  83     public boolean inConstructor() {
  84         return true;
  85     }
  86 
  87     /**
  88      * Will return false because this inline tag may
  89      * only appear in Fields.
  90      * @return false since this is not a method.
  91      */
  92     public boolean inOverview() {
  93         return true;
  94     }
  95 
  96     /**
  97      * Will return false because this inline tag may
  98      * only appear in Fields.
  99      * @return false since this is not a method.
 100      */
 101     public boolean inPackage() {
 102         return true;
 103     }
 104 
 105     /**
 106      * Will return false because this inline tag may
 107      * only appear in Fields.
 108      * @return false since this is not a method.
 109      */
 110     public boolean inType() {
 111         return true;
 112     }
 113 
 114     /**
 115      * Given the name of the field, return the corresponding VariableElement. Return null
 116      * due to invalid use of value tag if the name is null or empty string and if
 117      * the value tag is not used on a field.
 118      *
 119      * @param holder the element holding the tag
 120      * @param config the current configuration of the doclet.
 121      * @param tag the value tag.
 122      *
 123      * @return the corresponding VariableElement. If the name is null or empty string,
 124      * return field that the value tag was used in. Return null if the name is null
 125      * or empty string and if the value tag is not used on a field.
 126      */
 127     private VariableElement getVariableElement(Element holder, Configuration config, DocTree tag) {
 128         Utils utils = config.utils;
 129         CommentHelper ch = utils.getCommentHelper(holder);
 130         String signature = ch.getReferencedSignature(tag);
 131 
 132         if (signature == null) { // no reference
 133             //Base case: no label.
 134             if (utils.isVariableElement(holder)) {
 135                 return (VariableElement)(holder);
 136             } else {
 137                 // If the value tag does not specify a parameter which is a valid field and
 138                 // it is not used within the comments of a valid field, return null.
 139                  return null;
 140             }
 141         }
 142 
 143         String[] sigValues = signature.split("#");
 144         String memberName = null;
 145         TypeElement te = null;
 146         if (sigValues.length == 1) {
 147             //Case 2:  @value in same class.
 148             if (utils.isExecutableElement(holder) || utils.isVariableElement(holder)) {
 149                 te = utils.getEnclosingTypeElement(holder);
 150             } else if (utils.isTypeElement(holder)) {
 151                 te = utils.getTopMostContainingTypeElement(holder);
 152             }
 153             memberName = sigValues[0];
 154         } else {
 155             //Case 3: @value in different class.
 156             Elements elements = config.root.getElementUtils();
 157             te = elements.getTypeElement(sigValues[0]);
 158             memberName = sigValues[1];
 159         }
 160         if (te == null) {
 161             return null;
 162         }
 163         for (Element field : utils.getFields(te)) {
 164             if (utils.getSimpleName(field).equals(memberName)) {
 165                 return (VariableElement)field;
 166             }
 167         }
 168         return null;
 169     }
 170 
 171     /**
 172      * {@inheritDoc}
 173      */
 174     public Content getTagletOutput(Element holder, DocTree tag, TagletWriter writer) {
 175         Utils utils = writer.configuration().utils;
 176         VariableElement field = getVariableElement(holder, writer.configuration(), tag);
 177         if (field == null) {
 178             if (tag.toString().isEmpty()) {
 179                 //Invalid use of @value
 180                 writer.getMsgRetriever().warning(holder,
 181                         "doclet.value_tag_invalid_use");
 182             } else {
 183                 //Reference is unknown.
 184                 writer.getMsgRetriever().warning(holder,
 185                         "doclet.value_tag_invalid_reference", tag.toString());
 186             }
 187         } else if (field.getConstantValue() != null) {
 188             return writer.valueTagOutput(field,
 189                 utils.constantValueExpresion(field),
 190                 // TODO: investigate and cleanup
 191                 // in the j.l.m world, equals will not be accurate
 192                 // !field.equals(tag.holder())
 193                 !utils.elementsEqual(field, holder)
 194             );
 195         } else {
 196             //Referenced field is not a constant.
 197             writer.getMsgRetriever().warning(holder,
 198                 "doclet.value_tag_invalid_constant", utils.getSimpleName(field));
 199         }
 200         return writer.getOutputInstance();
 201     }
 202 }