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