1 /*
   2  * Copyright (c) 2003, 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 
  26 package jdk.javadoc.internal.doclets.formats.html;
  27 
  28 import java.util.List;
  29 
  30 import javax.lang.model.element.AnnotationMirror;
  31 import javax.lang.model.element.Element;
  32 import javax.lang.model.element.TypeElement;
  33 import javax.lang.model.type.TypeMirror;
  34 
  35 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
  36 import jdk.javadoc.internal.doclets.toolkit.Configuration;
  37 import jdk.javadoc.internal.doclets.toolkit.Content;
  38 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
  39 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
  40 import jdk.javadoc.internal.doclets.toolkit.util.links.LinkFactory;
  41 import jdk.javadoc.internal.doclets.toolkit.util.links.LinkInfo;
  42 
  43 import static jdk.javadoc.internal.doclets.formats.html.LinkInfoImpl.Kind.MEMBER_TYPE_PARAMS;
  44 
  45 /**
  46  * A factory that returns a link given the information about it.
  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  * @since 1.5
  55  */
  56 public class LinkFactoryImpl extends LinkFactory {
  57 
  58     private final HtmlDocletWriter m_writer;
  59 
  60     public LinkFactoryImpl(HtmlDocletWriter writer) {
  61         m_writer = writer;
  62     }
  63 
  64     /**
  65      * {@inheritDoc}
  66      */
  67     @Override
  68     protected Content newContent() {
  69         return new ContentBuilder();
  70     }
  71 
  72     /**
  73      * {@inheritDoc}
  74      */
  75     @Override
  76     protected Content getClassLink(LinkInfo linkInfo) {
  77         Configuration configuration = m_writer.configuration;
  78         Utils utils = configuration.utils;
  79         LinkInfoImpl classLinkInfo = (LinkInfoImpl) linkInfo;
  80         boolean noLabel = linkInfo.label == null || linkInfo.label.isEmpty();
  81         TypeElement typeElement = classLinkInfo.typeElement;
  82         // Create a tool tip if we are linking to a class or interface.  Don't
  83         // create one if we are linking to a member.
  84         String title = "";
  85         if (classLinkInfo.where == null || classLinkInfo.where.length() == 0) {
  86             boolean isTypeLink = classLinkInfo.type != null &&
  87                      utils.isTypeVariable(utils.getComponentType(classLinkInfo.type));
  88             title = getClassToolTip(typeElement, isTypeLink);
  89         }
  90         Content label = classLinkInfo.getClassLinkLabel(m_writer.configuration);
  91 
  92         Content link = new ContentBuilder();
  93         if (utils.isIncluded(typeElement)) {
  94             if (configuration.isGeneratedDoc(typeElement)) {
  95                 DocPath filename = getPath(classLinkInfo);
  96                 if (linkInfo.linkToSelf ||
  97                                 !(DocPath.forName(utils, typeElement)).equals(m_writer.filename)) {
  98                         link.addContent(m_writer.getHyperLink(
  99                                 filename.fragment(classLinkInfo.where),
 100                             label,
 101                             classLinkInfo.isStrong, classLinkInfo.styleName,
 102                             title, classLinkInfo.target));
 103                         if (noLabel && !classLinkInfo.excludeTypeParameterLinks) {
 104                             link.addContent(getTypeParameterLinks(linkInfo));
 105                         }
 106                         return link;
 107                 }
 108             }
 109         } else {
 110             Content crossLink = m_writer.getCrossClassLink(
 111                 typeElement.getQualifiedName().toString(), classLinkInfo.where,
 112                 label, classLinkInfo.isStrong, classLinkInfo.styleName,
 113                 true);
 114             if (crossLink != null) {
 115                 link.addContent(crossLink);
 116                 if (noLabel && !classLinkInfo.excludeTypeParameterLinks) {
 117                     link.addContent(getTypeParameterLinks(linkInfo));
 118                 }
 119                 return link;
 120             }
 121         }
 122         // Can't link so just write label.
 123         link.addContent(label);
 124         if (noLabel && !classLinkInfo.excludeTypeParameterLinks) {
 125             link.addContent(getTypeParameterLinks(linkInfo));
 126         }
 127         return link;
 128     }
 129 
 130     /**
 131      * {@inheritDoc}
 132      */
 133     @Override
 134     protected Content getTypeParameterLink(LinkInfo linkInfo, TypeMirror typeParam) {
 135         LinkInfoImpl typeLinkInfo = new LinkInfoImpl(m_writer.configuration,
 136                 ((LinkInfoImpl) linkInfo).getContext(), typeParam);
 137         typeLinkInfo.excludeTypeBounds = linkInfo.excludeTypeBounds;
 138         typeLinkInfo.excludeTypeParameterLinks = linkInfo.excludeTypeParameterLinks;
 139         typeLinkInfo.linkToSelf = linkInfo.linkToSelf;
 140         typeLinkInfo.isJava5DeclarationLocation = false;
 141         return getLink(typeLinkInfo);
 142     }
 143 
 144     @Override
 145     protected Content getTypeAnnotationLink(LinkInfo linkInfo, AnnotationMirror annotation) {
 146         throw new RuntimeException("Not implemented yet!");
 147     }
 148 
 149     @Override
 150     public Content getTypeAnnotationLinks(LinkInfo linkInfo) {
 151         Utils utils = ((LinkInfoImpl)linkInfo).utils;
 152         ContentBuilder links = new ContentBuilder();
 153         List<? extends AnnotationMirror> annotations;
 154         if (utils.isAnnotated(linkInfo.type)) {
 155             annotations = linkInfo.type.getAnnotationMirrors();
 156         } else if (utils.isTypeVariable(linkInfo.type)) {
 157             // TODO: use the context for now, and special case for Receiver_Types,
 158             // which takes the default case.
 159             switch (((LinkInfoImpl)linkInfo).context) {
 160                 case MEMBER_TYPE_PARAMS:
 161                 case EXECUTABLE_MEMBER_PARAM:
 162                 case CLASS_SIGNATURE:
 163                     Element element = utils.typeUtils.asElement(linkInfo.type);
 164                     annotations = element.getAnnotationMirrors();
 165                     break;
 166                 default:
 167                     annotations = linkInfo.type.getAnnotationMirrors();
 168                     break;
 169             }
 170 
 171         } else {
 172             return links;
 173         }
 174 
 175         if (annotations.isEmpty())
 176             return links;
 177 
 178         List<Content> annos = m_writer.getAnnotations(0, annotations, false, linkInfo.isJava5DeclarationLocation);
 179 
 180         boolean isFirst = true;
 181         for (Content anno : annos) {
 182             if (!isFirst) {
 183                 links.addContent(" ");
 184             }
 185             links.addContent(anno);
 186             isFirst = false;
 187         }
 188         if (!annos.isEmpty()) {
 189             links.addContent(" ");
 190         }
 191 
 192         return links;
 193     }
 194 
 195     /**
 196      * Given a class, return the appropriate tool tip.
 197      *
 198      * @param typeElement the class to get the tool tip for.
 199      * @return the tool tip for the appropriate class.
 200      */
 201     private String getClassToolTip(TypeElement typeElement, boolean isTypeLink) {
 202         Configuration configuration = m_writer.configuration;
 203         Utils utils = configuration.utils;
 204         if (isTypeLink) {
 205             return configuration.getText("doclet.Href_Type_Param_Title",
 206                     utils.getSimpleName(typeElement));
 207         } else if (utils.isInterface(typeElement)){
 208             return configuration.getText("doclet.Href_Interface_Title",
 209                 utils.getPackageName(utils.containingPackage(typeElement)));
 210         } else if (utils.isAnnotationType(typeElement)) {
 211             return configuration.getText("doclet.Href_Annotation_Title",
 212                 utils.getPackageName(utils.containingPackage(typeElement)));
 213         } else if (utils.isEnum(typeElement)) {
 214             return configuration.getText("doclet.Href_Enum_Title",
 215                 utils.getPackageName(utils.containingPackage(typeElement)));
 216         } else {
 217             return configuration.getText("doclet.Href_Class_Title",
 218                 utils.getPackageName(utils.containingPackage(typeElement)));
 219         }
 220     }
 221 
 222     /**
 223      * Return path to the given file name in the given package. So if the name
 224      * passed is "Object.html" and the name of the package is "java.lang", and
 225      * if the relative path is "../.." then returned string will be
 226      * "../../java/lang/Object.html"
 227      *
 228      * @param linkInfo the information about the link.
 229      */
 230     private DocPath getPath(LinkInfoImpl linkInfo) {
 231         if (linkInfo.context == LinkInfoImpl.Kind.PACKAGE_FRAME) {
 232             //Not really necessary to do this but we want to be consistent
 233             //with 1.4.2 output.
 234             return DocPath.forName(linkInfo.utils, linkInfo.typeElement);
 235         }
 236         return m_writer.pathToRoot.resolve(DocPath.forClass(linkInfo.utils, linkInfo.typeElement));
 237     }
 238 }