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 26package jdk.javadoc.internal.doclets.toolkit.util.links; 27 28import java.util.ArrayList; 29import java.util.List; 30 31import javax.lang.model.element.AnnotationMirror; 32import javax.lang.model.element.Element; 33import javax.lang.model.element.TypeElement; 34import javax.lang.model.element.TypeParameterElement; 35import javax.lang.model.type.ArrayType; 36import javax.lang.model.type.DeclaredType; 37import javax.lang.model.type.TypeMirror; 38import javax.lang.model.type.TypeVariable; 39import javax.lang.model.type.WildcardType; 40import javax.lang.model.util.SimpleTypeVisitor9; 41 42import jdk.javadoc.internal.doclets.formats.html.LinkInfoImpl; 43import jdk.javadoc.internal.doclets.toolkit.Content; 44import jdk.javadoc.internal.doclets.toolkit.util.Utils; 45 46/** 47 * A factory that constructs links from given link information. 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 */ 56public abstract class LinkFactory { 57 58 /** 59 * Return an empty instance of a content object. 60 * 61 * @return an empty instance of a content object. 62 */ 63 protected abstract Content newContent(); 64 65 /** 66 * Constructs a link from the given link information. 67 * 68 * @param linkInfo the information about the link. 69 * @return the output of the link. 70 */ 71 public Content getLink(LinkInfo linkInfo) { 72 Utils utils = ((LinkInfoImpl) linkInfo).configuration.utils; 73 if (linkInfo.type != null) { 74 SimpleTypeVisitor9<Content, LinkInfo> linkVisitor = 75 new SimpleTypeVisitor9<Content, LinkInfo>() { 76 77 TypeMirror componentType = utils.getComponentType(linkInfo.type); 78 Content link = newContent(); 79 80 // handles primitives, no types and error types 81 @Override 82 protected Content defaultAction(TypeMirror type, LinkInfo linkInfo) { 83 link.addContent(utils.getTypeName(type, false)); 84 return link; 85 } 86 87 int currentDepth = 0; 88 @Override 89 public Content visitArray(ArrayType type, LinkInfo linkInfo) { 90 // keep track of the dimension depth and replace the last dimension 91 // specifier with vararags, when the stack is fully unwound. 92 currentDepth++; 93 linkInfo.type = type.getComponentType(); 94 visit(linkInfo.type, linkInfo); 95 currentDepth--; 96 if (utils.isAnnotated(type)) { 97 linkInfo.type = type; 98 link.addContent(" "); 99 link.addContent(getTypeAnnotationLinks(linkInfo)); 100 } 101 // use vararg if required 102 if (linkInfo.isVarArg && currentDepth == 0) { 103 link.addContent("..."); 104 } else { 105 link.addContent("[]"); 106 } 107 return link; 108 } 109 110 @Override 111 public Content visitWildcard(WildcardType type, LinkInfo linkInfo) { 112 linkInfo.isTypeBound = true; 113 link.addContent("?"); 114 TypeMirror extendsBound = type.getExtendsBound(); 115 if (extendsBound != null) { 116 link.addContent(" extends "); 117 setBoundsLinkInfo(linkInfo, extendsBound); 118 link.addContent(getLink(linkInfo)); 119 } 120 TypeMirror superBound = type.getSuperBound(); 121 if (superBound != null) { 122 link.addContent(" super "); 123 setBoundsLinkInfo(linkInfo, superBound); 124 link.addContent(getLink(linkInfo)); 125 } 126 return link; 127 } 128 129 @Override 130 public Content visitTypeVariable(TypeVariable type, LinkInfo linkInfo) { 131 link.addContent(getTypeAnnotationLinks(linkInfo)); 132 linkInfo.isTypeBound = true; 133 TypeVariable typevariable = (utils.isArrayType(type)) 134 ? (TypeVariable) componentType 135 : type; 136 Element owner = typevariable.asElement().getEnclosingElement(); 137 if ((!linkInfo.excludeTypeParameterLinks) && utils.isTypeElement(owner)) { 138 linkInfo.typeElement = (TypeElement) owner; 139 Content label = newContent(); 140 label.addContent(utils.getTypeName(type, false)); 141 linkInfo.label = label; 142 link.addContent(getClassLink(linkInfo)); 143 } else { 144 // No need to link method type parameters. 145 link.addContent(utils.getTypeName(typevariable, false)); 146 } 147 148 if (!linkInfo.excludeTypeBounds) { 149 linkInfo.excludeTypeBounds = true; 150 TypeParameterElement tpe = ((TypeParameterElement) typevariable.asElement()); 151 boolean more = false; 152 List<? extends TypeMirror> bounds = utils.getBounds(tpe); 153 for (TypeMirror bound : bounds) { 154 // we get everything as extends java.lang.Object we suppress 155 // all of them except those that have multiple extends 156 if (bounds.size() == 1 && 157 bound.equals(utils.getObjectType()) && 158 !utils.isAnnotated(bound)) { 159 continue; 160 } 161 link.addContent(more ? " & " : " extends "); 162 setBoundsLinkInfo(linkInfo, bound); 163 link.addContent(getLink(linkInfo)); 164 more = true; 165 } 166 } 167 return link; 168 } 169 170 @Override 171 public Content visitDeclared(DeclaredType type, LinkInfo linkInfo) { 172 if (linkInfo.isTypeBound && linkInfo.excludeTypeBoundsLinks) { 173 // Since we are excluding type parameter links, we should not 174 // be linking to the type bound. 175 link.addContent(utils.getTypeName(type, false)); 176 link.addContent(getTypeParameterLinks(linkInfo)); 177 return link; 178 } else { 179 link = newContent(); 180 link.addContent(getTypeAnnotationLinks(linkInfo)); 181 linkInfo.typeElement = utils.asTypeElement(type); 182 link.addContent(getClassLink(linkInfo)); 183 if (linkInfo.includeTypeAsSepLink) { 184 link.addContent(getTypeParameterLinks(linkInfo, false)); 185 } 186 } 187 return link; 188 } 189 }; 190 return linkVisitor.visit(linkInfo.type, linkInfo); 191 } else if (linkInfo.typeElement != null) { 192 Content link = newContent(); 193 link.addContent(getClassLink(linkInfo)); 194 if (linkInfo.includeTypeAsSepLink) { 195 link.addContent(getTypeParameterLinks(linkInfo, false)); 196 } 197 return link; 198 } else { 199 return null; 200 } 201 } 202 203 private void setBoundsLinkInfo(LinkInfo linkInfo, TypeMirror bound) { 204 linkInfo.typeElement = null; 205 linkInfo.label = null; 206 linkInfo.type = bound; 207 } 208 209 /** 210 * Return the link to the given class. 211 * 212 * @param linkInfo the information about the link to construct. 213 * 214 * @return the link for the given class. 215 */ 216 protected abstract Content getClassLink(LinkInfo linkInfo); 217 218 /** 219 * Return the link to the given type parameter. 220 * 221 * @param linkInfo the information about the link to construct. 222 * @param typeParam the type parameter to link to. 223 */ 224 protected abstract Content getTypeParameterLink(LinkInfo linkInfo, TypeMirror typeParam); 225 226 protected abstract Content getTypeAnnotationLink(LinkInfo linkInfo, AnnotationMirror annotation); 227 228 /** 229 * Return the links to the type parameters. 230 * 231 * @param linkInfo the information about the link to construct. 232 * @return the links to the type parameters. 233 */ 234 public Content getTypeParameterLinks(LinkInfo linkInfo) { 235 return getTypeParameterLinks(linkInfo, true); 236 } 237 238 /** 239 * Return the links to the type parameters. 240 * 241 * @param linkInfo the information about the link to construct. 242 * @param isClassLabel true if this is a class label. False if it is 243 * the type parameters portion of the link. 244 * @return the links to the type parameters. 245 */ 246 public Content getTypeParameterLinks(LinkInfo linkInfo, boolean isClassLabel) { 247 Utils utils = ((LinkInfoImpl)linkInfo).utils; 248 Content links = newContent(); 249 List<TypeMirror> vars = new ArrayList<>(); 250 TypeMirror ctype = linkInfo.type != null 251 ? utils.getComponentType(linkInfo.type) 252 : null; 253 if (linkInfo.executableElement != null) { 254 linkInfo.executableElement.getTypeParameters().stream().forEach((t) -> { 255 vars.add(t.asType()); 256 }); 257 } else if (linkInfo.type != null && utils.isDeclaredType(linkInfo.type)) { 258 ((DeclaredType)linkInfo.type).getTypeArguments().stream().forEach(vars::add); 259 } else if (ctype != null && utils.isDeclaredType(ctype)) { 260 ((DeclaredType)ctype).getTypeArguments().stream().forEach(vars::add); 261 } else if (linkInfo.typeElement != null) { 262 linkInfo.typeElement.getTypeParameters().stream().forEach((t) -> { 263 vars.add(t.asType()); 264 }); 265 } else { 266 // Nothing to document. 267 return links; 268 } 269 if (((linkInfo.includeTypeInClassLinkLabel && isClassLabel) 270 || (linkInfo.includeTypeAsSepLink && !isClassLabel)) && !vars.isEmpty()) { 271 links.addContent("<"); 272 boolean many = false; 273 for (TypeMirror t : vars) { 274 if (many) { 275 links.addContent(","); 276 } 277 links.addContent(getTypeParameterLink(linkInfo, t)); 278 many = true; 279 } 280 links.addContent(">"); 281 } 282 return links; 283 } 284 285 public Content getTypeAnnotationLinks(LinkInfo linkInfo) { 286 Utils utils = ((LinkInfoImpl)linkInfo).utils; 287 Content links = newContent(); 288 if (!utils.isAnnotated(linkInfo.type)) 289 return links; 290 291 List<? extends AnnotationMirror> annotations = linkInfo.type.getAnnotationMirrors(); 292 boolean needSpace = false; 293 for (AnnotationMirror anno : annotations) { 294 if (needSpace) { 295 links.addContent(" "); 296 } 297 links.addContent(getTypeAnnotationLink(linkInfo, anno)); 298 needSpace = true; 299 } 300 301 links.addContent(" "); 302 return links; 303 } 304} 305