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