1 /*
   2  * Copyright (c) 2003, 2019, 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.ArrayList;
  29 import java.util.List;
  30 
  31 import javax.lang.model.element.AnnotationMirror;
  32 import javax.lang.model.element.Element;
  33 import javax.lang.model.element.TypeElement;
  34 import javax.lang.model.type.DeclaredType;
  35 import javax.lang.model.type.TypeMirror;
  36 
  37 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
  38 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
  39 import jdk.javadoc.internal.doclets.toolkit.Content;
  40 import jdk.javadoc.internal.doclets.toolkit.Resources;
  41 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
  42 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
  43 import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
  44 import jdk.javadoc.internal.doclets.toolkit.util.links.LinkFactory;
  45 import jdk.javadoc.internal.doclets.toolkit.util.links.LinkInfo;
  46 
  47 /**
  48  * A factory that returns a link given the information about it.
  49  *
  50  *  <p><b>This is NOT part of any supported API.
  51  *  If you write code that depends on this, you do so at your own risk.
  52  *  This code and its internal interfaces are subject to change or
  53  *  deletion without notice.</b>
  54  *
  55  * @author Jamie Ho
  56  */
  57 public class LinkFactoryImpl extends LinkFactory {
  58 
  59     private final HtmlDocletWriter m_writer;
  60     private final DocPaths docPaths;
  61 
  62     public LinkFactoryImpl(HtmlDocletWriter writer) {
  63         super(writer.configuration.utils);
  64         m_writer = writer;
  65         docPaths = writer.configuration.docPaths;
  66     }
  67 
  68     /**
  69      * {@inheritDoc}
  70      */
  71     @Override
  72     protected Content newContent() {
  73         return new ContentBuilder();
  74     }
  75 
  76     /**
  77      * {@inheritDoc}
  78      */
  79     @Override
  80     protected Content getClassLink(LinkInfo linkInfo) {
  81         BaseConfiguration configuration = m_writer.configuration;
  82         LinkInfoImpl classLinkInfo = (LinkInfoImpl) linkInfo;
  83         boolean noLabel = linkInfo.label == null || linkInfo.label.isEmpty();
  84         TypeElement typeElement = classLinkInfo.typeElement;
  85         // Create a tool tip if we are linking to a class or interface.  Don't
  86         // create one if we are linking to a member.
  87         String title = "";
  88         if (classLinkInfo.where == null || classLinkInfo.where.length() == 0) {
  89             boolean isTypeLink = classLinkInfo.type != null &&
  90                      utils.isTypeVariable(utils.getComponentType(classLinkInfo.type));
  91             title = getClassToolTip(typeElement, isTypeLink);
  92         }
  93         Content label = classLinkInfo.getClassLinkLabel(configuration);
  94 
  95         Content link = new ContentBuilder();
  96         if (utils.isIncluded(typeElement)) {
  97             if (configuration.isGeneratedDoc(typeElement)) {
  98                 DocPath filename = getPath(classLinkInfo);
  99                 if (linkInfo.linkToSelf ||
 100                                 !(docPaths.forName(typeElement)).equals(m_writer.filename)) {
 101                         link.add(m_writer.links.createLink(
 102                                 filename.fragment(classLinkInfo.where),
 103                                 label,
 104                                 classLinkInfo.isStrong,
 105                                 title,
 106                                 classLinkInfo.target));
 107                         if (noLabel && !classLinkInfo.excludeTypeParameterLinks) {
 108                             link.add(getTypeParameterLinks(linkInfo));
 109                         }
 110                         return link;
 111                 }
 112             }
 113         } else {
 114             Content crossLink = m_writer.getCrossClassLink(
 115                 typeElement, classLinkInfo.where,
 116                 label, classLinkInfo.isStrong, true);
 117             if (crossLink != null) {
 118                 link.add(crossLink);
 119                 if (noLabel && !classLinkInfo.excludeTypeParameterLinks) {
 120                     link.add(getTypeParameterLinks(linkInfo));
 121                 }
 122                 return link;
 123             }
 124         }
 125         // Can't link so just write label.
 126         link.add(label);
 127         if (noLabel && !classLinkInfo.excludeTypeParameterLinks) {
 128             link.add(getTypeParameterLinks(linkInfo));
 129         }
 130         return link;
 131     }
 132 
 133     /**
 134      * {@inheritDoc}
 135      */
 136     @Override
 137     protected Content getTypeParameterLinks(LinkInfo linkInfo, boolean isClassLabel) {
 138         Content links = newContent();
 139         List<TypeMirror> vars = new ArrayList<>();
 140         TypeMirror ctype = linkInfo.type != null
 141                 ? utils.getComponentType(linkInfo.type)
 142                 : null;
 143         if (linkInfo.executableElement != null) {
 144             linkInfo.executableElement.getTypeParameters().stream().forEach((t) -> {
 145                 vars.add(t.asType());
 146             });
 147         } else if (linkInfo.type != null && utils.isDeclaredType(linkInfo.type)) {
 148             ((DeclaredType)linkInfo.type).getTypeArguments().stream().forEach(vars::add);
 149         } else if (ctype != null && utils.isDeclaredType(ctype)) {
 150             ((DeclaredType)ctype).getTypeArguments().stream().forEach(vars::add);
 151         } else if (linkInfo.typeElement != null) {
 152             linkInfo.typeElement.getTypeParameters().stream().forEach((t) -> {
 153                 vars.add(t.asType());
 154             });
 155         } else {
 156             // Nothing to document.
 157             return links;
 158         }
 159         if (((linkInfo.includeTypeInClassLinkLabel && isClassLabel)
 160                 || (linkInfo.includeTypeAsSepLink && !isClassLabel)) && !vars.isEmpty()) {
 161             links.add("<");
 162             int lineStart = 0;
 163             boolean many = false;
 164             for (TypeMirror t : vars) {
 165                 if (many) {
 166                     links.add(",");
 167                     links.add(Contents.ZERO_WIDTH_SPACE);
 168                     if (links.charCount() - lineStart > DocletConstants.PREFORMATTED_LINE_LENGTH) {
 169                         lineStart = links.charCount();
 170                         links.add(DocletConstants.NL + "        ");
 171                     }
 172                 }
 173                 links.add(getTypeParameterLink(linkInfo, t));
 174                 many = true;
 175             }
 176             links.add(">");
 177         }
 178         return links;
 179     }
 180 
 181     /**
 182      * Returns a link to the given type parameter.
 183      *
 184      * @param linkInfo     the information about the link to construct
 185      * @param typeParam the type parameter to link to
 186      * @return the link
 187      */
 188     protected Content getTypeParameterLink(LinkInfo linkInfo, TypeMirror typeParam) {
 189         LinkInfoImpl typeLinkInfo = new LinkInfoImpl(m_writer.configuration,
 190                 ((LinkInfoImpl) linkInfo).getContext(), typeParam);
 191         typeLinkInfo.excludeTypeBounds = linkInfo.excludeTypeBounds;
 192         typeLinkInfo.excludeTypeParameterLinks = linkInfo.excludeTypeParameterLinks;
 193         typeLinkInfo.linkToSelf = linkInfo.linkToSelf;
 194         typeLinkInfo.isJava5DeclarationLocation = false;
 195         return getLink(typeLinkInfo);
 196     }
 197 
 198     @Override
 199     public Content getTypeAnnotationLinks(LinkInfo linkInfo) {
 200         ContentBuilder links = new ContentBuilder();
 201         List<? extends AnnotationMirror> annotations;
 202         if (utils.isAnnotated(linkInfo.type)) {
 203             annotations = linkInfo.type.getAnnotationMirrors();
 204         } else if (utils.isTypeVariable(linkInfo.type)) {
 205             // TODO: use the context for now, and special case for Receiver_Types,
 206             // which takes the default case.
 207             switch (((LinkInfoImpl)linkInfo).context) {
 208                 case MEMBER_TYPE_PARAMS:
 209                 case EXECUTABLE_MEMBER_PARAM:
 210                 case CLASS_SIGNATURE:
 211                     Element element = utils.typeUtils.asElement(linkInfo.type);
 212                     annotations = element.getAnnotationMirrors();
 213                     break;
 214                 default:
 215                     annotations = linkInfo.type.getAnnotationMirrors();
 216                     break;
 217             }
 218 
 219         } else {
 220             return links;
 221         }
 222 
 223         if (annotations.isEmpty())
 224             return links;
 225 
 226         List<Content> annos = m_writer.getAnnotations(0, annotations, false, linkInfo.isJava5DeclarationLocation);
 227 
 228         boolean isFirst = true;
 229         for (Content anno : annos) {
 230             if (!isFirst) {
 231                 links.add(" ");
 232             }
 233             links.add(anno);
 234             isFirst = false;
 235         }
 236         if (!annos.isEmpty()) {
 237             links.add(" ");
 238         }
 239 
 240         return links;
 241     }
 242 
 243     /**
 244      * Given a class, return the appropriate tool tip.
 245      *
 246      * @param typeElement the class to get the tool tip for.
 247      * @return the tool tip for the appropriate class.
 248      */
 249     private String getClassToolTip(TypeElement typeElement, boolean isTypeLink) {
 250         Resources resources = m_writer.configuration.getResources();
 251         if (isTypeLink) {
 252             return resources.getText("doclet.Href_Type_Param_Title",
 253                     utils.getSimpleName(typeElement));
 254         } else if (utils.isInterface(typeElement)){
 255             return resources.getText("doclet.Href_Interface_Title",
 256                 utils.getPackageName(utils.containingPackage(typeElement)));
 257         } else if (utils.isAnnotationType(typeElement)) {
 258             return resources.getText("doclet.Href_Annotation_Title",
 259                 utils.getPackageName(utils.containingPackage(typeElement)));
 260         } else if (utils.isEnum(typeElement)) {
 261             return resources.getText("doclet.Href_Enum_Title",
 262                 utils.getPackageName(utils.containingPackage(typeElement)));
 263         } else {
 264             return resources.getText("doclet.Href_Class_Title",
 265                 utils.getPackageName(utils.containingPackage(typeElement)));
 266         }
 267     }
 268 
 269     /**
 270      * Return path to the given file name in the given package. So if the name
 271      * passed is "Object.html" and the name of the package is "java.lang", and
 272      * if the relative path is "../.." then returned string will be
 273      * "../../java/lang/Object.html"
 274      *
 275      * @param linkInfo the information about the link.
 276      */
 277     private DocPath getPath(LinkInfoImpl linkInfo) {
 278         if (linkInfo.context == LinkInfoImpl.Kind.PACKAGE_FRAME) {
 279             //Not really necessary to do this but we want to be consistent
 280             //with 1.4.2 output.
 281             return docPaths.forName(linkInfo.typeElement);
 282         }
 283         return m_writer.pathToRoot.resolve(docPaths.forClass(linkInfo.typeElement));
 284     }
 285 }
--- EOF ---