1 /* 2 * Copyright (c) 1997, 2018, 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.markup; 27 28 import jdk.javadoc.internal.doclets.formats.html.SectionName; 29 import jdk.javadoc.internal.doclets.toolkit.Content; 30 import jdk.javadoc.internal.doclets.toolkit.util.DocLink; 31 import jdk.javadoc.internal.doclets.toolkit.util.DocPath; 32 33 /** 34 * Factory for HTML A elements, both links (with a {@code href} attribute) 35 * and anchors (with an {@code id} or {@code name} attribute). 36 * 37 * Most methods in this class are static factory methods. 38 * The exceptions are those methods that directly or indirectly depend on the HTML version 39 * being used, when determining valid HTML names (ids), 40 * and those methods that generate anchors. 41 * 42 * <p><b>This is NOT part of any supported API. 43 * If you write code that depends on this, you do so at your own risk. 44 * This code and its internal interfaces are subject to change or 45 * deletion without notice.</b> 46 */ 47 public class Links { 48 49 private final DocPath file; 50 private final HtmlVersion version; 51 52 /** 53 * Creates a {@code Links} object for a specific file, to be written in a specific HTML version. 54 * The version is used by the {@link #getName(String) getName} method 55 * to help determine valid HTML names (ids), and to determine whether 56 * to use an {@code id} or {@code name} attribute when creating anchors. 57 * 58 * @param file the file 59 * @param version the HTML version 60 */ Links(DocPath file, HtmlVersion version)61 public Links(DocPath file, HtmlVersion version) { 62 this.file = file; 63 this.version = version; 64 } 65 66 /** 67 * Creates an anchor of the form {@code <a id="name"><!-- --></a>}. 68 * In HTML4, a {@code name} attribute will be generated instead of an {@code id} attribute. 69 * 70 * @param name the value for the {@code id} or {@code name} attribute 71 * @return a content tree for the anchor 72 */ createAnchor(String name)73 public Content createAnchor(String name) { 74 return createAnchor(getName(name), null); 75 } 76 77 /** 78 * Creates an anchor of the form {@code <a id="sectionName"><!-- --></a>}. 79 * In HTML4, a {@code name} attribute will be generated instead of an {@code id} attribute. 80 * 81 * @param sectionName the value for the {@code id} or {@code name} attribute 82 * @return a content tree for the anchor 83 */ createAnchor(SectionName sectionName)84 public Content createAnchor(SectionName sectionName) { 85 return createAnchor(sectionName.getName(), null); 86 } 87 88 /** 89 * Creates an anchor of the form {@code <a id="sectionNameName"><!-- --></a>}. 90 * In HTML4, a {@code name} attribute will be generated instead of an {@code id} attribute. 91 * 92 * @param sectionName the first part of the value for the {@code id} or {@code name} attribute 93 * @param name the second part of the value for the {@code id} or {@code name} attribute 94 * @return a content tree for the anchor 95 */ createAnchor(SectionName sectionName, String name)96 public Content createAnchor(SectionName sectionName, String name) { 97 return createAnchor(sectionName.getName() + getName(name), null); 98 } 99 100 /** 101 * Creates an anchor of the form {@code <a id="anchorName">content</a>}. 102 * In HTML4, a {@code name} attribute will be generated instead of an {@code id} attribute. 103 * 104 * @param name the value for the {@code id} or {@code name} attribute 105 * @param content the content that should be added to the anchor, 106 * or null, to use an empty comment 107 * @return a content tree for the marker anchor 108 */ createAnchor(String name, Content content)109 public Content createAnchor(String name, Content content) { 110 return HtmlTree.A(version, name, (content == null ? EMPTY_COMMENT : content)); 111 } 112 113 private static final Content EMPTY_COMMENT = new Comment(" "); 114 115 /** 116 * Creates a link of the form {@code <a href="#where">label</a>}. 117 * 118 * @param where the position of the link in the file 119 * @param label the content for the link 120 * @return a content tree for the link 121 */ createLink(String where, Content label)122 public Content createLink(String where, Content label) { 123 DocLink l = DocLink.fragment(getName(where)); 124 return createLink(l, label, "", ""); 125 } 126 127 /** 128 * Creates a link of the form {@code <a href="#sectionName">label</a>}. 129 * 130 * @param sectionName the section name to which the link will be created 131 * @param label the content for the link 132 * @return a content tree for the link 133 */ createLink(SectionName sectionName, Content label)134 public Content createLink(SectionName sectionName, Content label) { 135 DocLink l = DocLink.fragment(sectionName.getName()); 136 return createLink(l, label, "", ""); 137 } 138 139 /** 140 * Creates a link of the form {@code <a href="#sectionNameWhere">label</a>}. 141 * 142 * @param sectionName the section name combined with where to which the link 143 * will be created 144 * @param where the fragment combined with sectionName to which the link 145 * will be created 146 * @param label the content for the link 147 * @return a content tree for the link 148 */ createLink(SectionName sectionName, String where, Content label)149 public Content createLink(SectionName sectionName, String where, Content label) { 150 DocLink l = DocLink.fragment(sectionName.getName() + getName(where)); 151 return createLink(l, label, "", ""); 152 } 153 154 /** 155 * Creates a link of the form {@code <a href="#stylename" title="title" target="target">label</a>}. 156 * 157 * @param sectionName the section name to which the link will be created 158 * @param label the content for the link 159 * @param title the title for the link 160 * @param target the target for the link, or null 161 * @return a content tree for the link 162 */ createLink(SectionName sectionName, Content label, String title, String target)163 public Content createLink(SectionName sectionName, Content label, String title, String target) { 164 DocLink l = DocLink.fragment(sectionName.getName()); 165 return createLink(l, label, title, target); 166 } 167 168 /** 169 * Creates a link of the form {@code <a href="path">label</a>}. 170 * 171 * @param path the path for the link 172 * @param label the content for the link 173 * @return a content tree for the link 174 */ createLink(DocPath path, String label)175 public Content createLink(DocPath path, String label) { 176 return createLink(path, new StringContent(label), false, "", ""); 177 } 178 179 /** 180 * Creates a link of the form {@code <a href="path">label</a>}. 181 * 182 * @param path the path for the link 183 * @param label the content for the link 184 * @return a content tree for the link 185 */ createLink(DocPath path, Content label)186 public Content createLink(DocPath path, Content label) { 187 return createLink(path, label, "", ""); 188 } 189 190 /** 191 * Creates a link of the form {@code <a href="path" title="title" target="target">label</a>}. 192 * If {@code strong} is set, the label will be wrapped in 193 * {@code <span style="typeNameLink">...</span>}. 194 * 195 * @param path the path for the link 196 * @param label the content for the link 197 * @param strong whether to wrap the {@code label} in a SPAN element 198 * @param title the title for the link 199 * @param target the target for the link, or null 200 * @return a content tree for the link 201 */ createLink(DocPath path, Content label, boolean strong, String title, String target)202 public Content createLink(DocPath path, Content label, boolean strong, 203 String title, String target) { 204 return createLink(new DocLink(path), label, strong, title, target); 205 } 206 207 /** 208 * Creates a link of the form {@code <a href="path" title="title" target="target">label</a>}. 209 * 210 * @param path the path for the link 211 * @param label the content for the link 212 * @param title the title for the link 213 * @param target the target for the link, or null 214 * @return a content tree for the link 215 */ createLink(DocPath path, Content label, String title, String target)216 public Content createLink(DocPath path, Content label, String title, String target) { 217 return createLink(new DocLink(path), label, title, target); 218 } 219 220 /** 221 * Creates a link of the form {@code <a href="link">label</a>}. 222 * 223 * @param link the details for the link 224 * @param label the content for the link 225 * @return a content tree for the link 226 */ createLink(DocLink link, Content label)227 public Content createLink(DocLink link, Content label) { 228 return createLink(link, label, "", ""); 229 } 230 231 /** 232 * Creates a link of the form {@code <a href="path" title="title" target="target">label</a>}. 233 * 234 * @param link the details for the link 235 * @param label the content for the link 236 * @param title the title for the link 237 * @param target the target for the link, or null 238 * @return a content tree for the link 239 */ createLink(DocLink link, Content label, String title, String target)240 public Content createLink(DocLink link, Content label, String title, String target) { 241 HtmlTree anchor = HtmlTree.A(link.relativizeAgainst(file).toString(), label); 242 if (title != null && title.length() != 0) { 243 anchor.addAttr(HtmlAttr.TITLE, title); 244 } 245 if (target != null && target.length() != 0) { 246 anchor.addAttr(HtmlAttr.TARGET, target); 247 } 248 return anchor; 249 } 250 251 /** 252 * Creates a link of the form {@code <a href="link" title="title" target="target">label</a>}. 253 * If {@code strong} is set, the label will be wrapped in 254 * {@code <span style="typeNameLink">...</span>}. 255 * 256 * @param link the details for the link 257 * @param label the content for the link 258 * @param strong whether to wrap the {@code label} in a SPAN element 259 * @param title the title for the link 260 * @param target the target for the link, or null 261 * @return a content tree for the link 262 */ createLink(DocLink link, Content label, boolean strong, String title, String target)263 public Content createLink(DocLink link, Content label, boolean strong, 264 String title, String target) { 265 return createLink(link, label, strong, title, target, false); 266 } 267 268 /** 269 * Creates a link of the form {@code <a href="link" title="title" target="target">label</a>}. 270 * If {@code strong} is set, the label will be wrapped in 271 * {@code <span style="typeNameLink">...</span>}. 272 * 273 * @param link the details for the link 274 * @param label the content for the link 275 * @param strong whether to wrap the {@code label} in a SPAN element 276 * @param title the title for the link 277 * @param target the target for the link, or null 278 * @param isExternal is the link external to the generated documentation 279 * @return a content tree for the link 280 */ createLink(DocLink link, Content label, boolean strong, String title, String target, boolean isExternal)281 public Content createLink(DocLink link, Content label, boolean strong, 282 String title, String target, boolean isExternal) { 283 Content body = label; 284 if (strong) { 285 body = HtmlTree.SPAN(HtmlStyle.typeNameLink, body); 286 } 287 HtmlTree l = HtmlTree.A(link.relativizeAgainst(file).toString(), body); 288 if (title != null && title.length() != 0) { 289 l.addAttr(HtmlAttr.TITLE, title); 290 } 291 if (target != null && target.length() != 0) { 292 l.addAttr(HtmlAttr.TARGET, target); 293 } 294 if (isExternal) { 295 l.setStyle(HtmlStyle.externalLink); 296 } 297 return l; 298 } 299 300 /** 301 * Creates a link. 302 * 303 * @param link the details for the link 304 * @param label the content for the link 305 * @param isExternal is the link external to the generated documentation 306 * @return a content tree for the link 307 */ createLink(DocLink link, Content label, boolean isExternal)308 public Content createLink(DocLink link, Content label, boolean isExternal) { 309 HtmlTree anchor = HtmlTree.A(link.relativizeAgainst(file).toString(), label); 310 anchor.setStyle(HtmlStyle.externalLink); 311 return anchor; 312 } 313 314 /** 315 * Converts a name to a valid HTML name (id). 316 * This depends on the HTML version specified when the {@code Links} object was created. 317 * 318 * @param name the string that needs to be converted to a valid HTML name 319 * @return a valid HTML name 320 */ getName(String name)321 public String getName(String name) { 322 /* The HTML 4 spec at http://www.w3.org/TR/html4/types.html#h-6.2 mentions 323 * that the name/id should begin with a letter followed by other valid characters. 324 * The HTML 5 spec (draft) is more permissive on names/ids where the only restriction 325 * is that it should be at least one character long and should not contain spaces. 326 * The spec draft is @ http://www.w3.org/html/wg/drafts/html/master/dom.html#the-id-attribute. 327 * 328 * For HTML 4, we need to check for non-characters at the beginning of the name and 329 * substitute it accordingly, "_" and "$" can appear at the beginning of a member name. 330 * The method substitutes "$" with "Z:Z:D" and will prefix "_" with "Z:Z". 331 */ 332 333 if (version == HtmlVersion.HTML5) { 334 return name.replaceAll(" +", ""); 335 } 336 337 StringBuilder sb = new StringBuilder(); 338 for (int i = 0; i < name.length(); i++) { 339 char ch = name.charAt(i); 340 switch (ch) { 341 case '(': 342 case ')': 343 case '<': 344 case '>': 345 case ',': 346 sb.append('-'); 347 break; 348 case ' ': 349 case '[': 350 break; 351 case ']': 352 sb.append(":A"); 353 break; 354 // Any appearance of $ needs to be substituted with ":D" and not with hyphen 355 // since a field name "P$$ and a method P(), both valid member names, can end 356 // up as "P--". A member name beginning with $ needs to be substituted with 357 // "Z:Z:D". 358 case '$': 359 if (i == 0) 360 sb.append("Z:Z"); 361 sb.append(":D"); 362 break; 363 // A member name beginning with _ needs to be prefixed with "Z:Z" since valid anchor 364 // names can only begin with a letter. 365 case '_': 366 if (i == 0) 367 sb.append("Z:Z"); 368 sb.append(ch); 369 break; 370 default: 371 sb.append(ch); 372 } 373 } 374 return sb.toString(); 375 } 376 377 } 378