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 }