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