1/*
2 * Copyright (c) 1997, 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.formats.html.markup;
27
28import java.util.*;
29
30import javax.lang.model.element.ModuleElement;
31import javax.lang.model.element.PackageElement;
32import javax.lang.model.element.TypeElement;
33
34import jdk.javadoc.internal.doclets.formats.html.ConfigurationImpl;
35import jdk.javadoc.internal.doclets.formats.html.SectionName;
36import jdk.javadoc.internal.doclets.toolkit.Configuration;
37import jdk.javadoc.internal.doclets.toolkit.Content;
38import jdk.javadoc.internal.doclets.toolkit.Messages;
39import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
40import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
41import jdk.javadoc.internal.doclets.toolkit.util.DocLink;
42import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
43import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
44
45
46/**
47 * Class for the Html Format Code Generation specific to JavaDoc.
48 * This Class contains methods related to the Html Code Generation which
49 * are used by the Sub-Classes in the package jdk.javadoc.internal.tool.standard.
50 *
51 *  <p><b>This is NOT part of any supported API.
52 *  If you write code that depends on this, you do so at your own risk.
53 *  This code and its internal interfaces are subject to change or
54 *  deletion without notice.</b>
55 *
56 * @author Atul M Dambalkar
57 * @author Robert Field
58 */
59public abstract class HtmlDocWriter extends HtmlWriter {
60
61    public static final String CONTENT_TYPE = "text/html";
62
63    DocPath pathToRoot;
64
65    /**
66     * Constructor. Initializes the destination file name through the super
67     * class HtmlWriter.
68     *
69     * @param configuration the configuration for this doclet
70     * @param filename String file name.
71     */
72    public HtmlDocWriter(Configuration configuration, DocPath filename) {
73        super(configuration, filename);
74        this.pathToRoot = filename.parent().invert();
75        Messages messages = configuration.getMessages();
76        messages.notice("doclet.Generating_0",
77            DocFile.createFileForOutput(configuration, filename).getPath());
78    }
79
80    /**
81     * Accessor for configuration.
82     * @return the configuration for this doclet
83     */
84    public abstract Configuration configuration();
85
86    public Content getHyperLink(DocPath link, String label) {
87        return getHyperLink(link, new StringContent(label), false, "", "", "");
88    }
89
90    /**
91     * Get Html Hyper Link Content.
92     *
93     * @param where      Position of the link in the file. Character '#' is not
94     *                   needed.
95     * @param label      Tag for the link.
96     * @return a content tree for the hyper link
97     */
98    public Content getHyperLink(String where,
99                               Content label) {
100        return getHyperLink(getDocLink(where), label, "", "");
101    }
102
103    /**
104     * Get Html Hyper Link Content.
105     *
106     * @param sectionName      The section name to which the link will be created.
107     * @param label            Tag for the link.
108     * @return a content tree for the hyper link
109     */
110    public Content getHyperLink(SectionName sectionName,
111                               Content label) {
112        return getHyperLink(getDocLink(sectionName), label, "", "");
113    }
114
115    /**
116     * Get Html Hyper Link Content.
117     *
118     * @param sectionName      The section name combined with where to which the link
119     *                         will be created.
120     * @param where            The fragment combined with sectionName to which the link
121     *                         will be created.
122     * @param label            Tag for the link.
123     * @return a content tree for the hyper link
124     */
125    public Content getHyperLink(SectionName sectionName, String where,
126                               Content label) {
127        return getHyperLink(getDocLink(sectionName, where), label, "", "");
128    }
129
130    /**
131     * Get the link.
132     *
133     * @param where      Position of the link in the file.
134     * @return a DocLink object for the hyper link
135     */
136    public DocLink getDocLink(String where) {
137        return DocLink.fragment(getName(where));
138    }
139
140    /**
141     * Get the link.
142     *
143     * @param sectionName      The section name to which the link will be created.
144     * @return a DocLink object for the hyper link
145     */
146    public DocLink getDocLink(SectionName sectionName) {
147        return DocLink.fragment(sectionName.getName());
148    }
149
150    /**
151     * Get the link.
152     *
153     * @param sectionName      The section name combined with where to which the link
154     *                         will be created.
155     * @param where            The fragment combined with sectionName to which the link
156     *                         will be created.
157     * @return a DocLink object for the hyper link
158     */
159    public DocLink getDocLink(SectionName sectionName, String where) {
160        return DocLink.fragment(sectionName.getName() + getName(where));
161    }
162
163    /**
164     * Convert the name to a valid HTML name.
165     *
166     * @param name the name that needs to be converted to valid HTML name.
167     * @return a valid HTML name string.
168     */
169    public String getName(String name) {
170        StringBuilder sb = new StringBuilder();
171        char ch;
172        /* The HTML 4 spec at http://www.w3.org/TR/html4/types.html#h-6.2 mentions
173         * that the name/id should begin with a letter followed by other valid characters.
174         * The HTML 5 spec (draft) is more permissive on names/ids where the only restriction
175         * is that it should be at least one character long and should not contain spaces.
176         * The spec draft is @ http://www.w3.org/html/wg/drafts/html/master/dom.html#the-id-attribute.
177         *
178         * For HTML 4, we need to check for non-characters at the beginning of the name and
179         * substitute it accordingly, "_" and "$" can appear at the beginning of a member name.
180         * The method substitutes "$" with "Z:Z:D" and will prefix "_" with "Z:Z".
181         */
182        for (int i = 0; i < name.length(); i++) {
183            ch = name.charAt(i);
184            switch (ch) {
185                case '(':
186                case ')':
187                case '<':
188                case '>':
189                case ',':
190                    sb.append('-');
191                    break;
192                case ' ':
193                case '[':
194                    break;
195                case ']':
196                    sb.append(":A");
197                    break;
198                // Any appearance of $ needs to be substituted with ":D" and not with hyphen
199                // since a field name "P$$ and a method P(), both valid member names, can end
200                // up as "P--". A member name beginning with $ needs to be substituted with
201                // "Z:Z:D".
202                case '$':
203                    if (i == 0)
204                        sb.append("Z:Z");
205                    sb.append(":D");
206                    break;
207                // A member name beginning with _ needs to be prefixed with "Z:Z" since valid anchor
208                // names can only begin with a letter.
209                case '_':
210                    if (i == 0)
211                        sb.append("Z:Z");
212                    sb.append(ch);
213                    break;
214                default:
215                    sb.append(ch);
216            }
217        }
218        return sb.toString();
219    }
220
221    /**
222     * Get Html hyperlink.
223     *
224     * @param link       path of the file.
225     * @param label      Tag for the link.
226     * @return a content tree for the hyper link
227     */
228    public Content getHyperLink(DocPath link, Content label) {
229        return getHyperLink(link, label, "", "");
230    }
231
232    public Content getHyperLink(DocLink link, Content label) {
233        return getHyperLink(link, label, "", "");
234    }
235
236    public Content getHyperLink(DocPath link,
237                               Content label, boolean strong,
238                               String stylename, String title, String target) {
239        return getHyperLink(new DocLink(link), label, strong,
240                stylename, title, target);
241    }
242
243    public Content getHyperLink(DocLink link,
244                               Content label, boolean strong,
245                               String stylename, String title, String target) {
246        Content body = label;
247        if (strong) {
248            body = HtmlTree.SPAN(HtmlStyle.typeNameLink, body);
249        }
250        if (stylename != null && stylename.length() != 0) {
251            HtmlTree t = new HtmlTree(HtmlTag.FONT, body);
252            t.addAttr(HtmlAttr.CLASS, stylename);
253            body = t;
254        }
255        HtmlTree l = HtmlTree.A(link.toString(), body);
256        if (title != null && title.length() != 0) {
257            l.addAttr(HtmlAttr.TITLE, title);
258        }
259        if (target != null && target.length() != 0) {
260            l.addAttr(HtmlAttr.TARGET, target);
261        }
262        return l;
263    }
264
265    /**
266     * Get Html Hyper Link.
267     *
268     * @param link       String name of the file.
269     * @param label      Tag for the link.
270     * @param title      String that describes the link's content for accessibility.
271     * @param target     Target frame.
272     * @return a content tree for the hyper link.
273     */
274    public Content getHyperLink(DocPath link, Content label, String title, String target) {
275        return getHyperLink(new DocLink(link), label, title, target);
276    }
277
278    public Content getHyperLink(DocLink link, Content label, String title, String target) {
279        HtmlTree anchor = HtmlTree.A(link.toString(), label);
280        if (title != null && title.length() != 0) {
281            anchor.addAttr(HtmlAttr.TITLE, title);
282        }
283        if (target != null && target.length() != 0) {
284            anchor.addAttr(HtmlAttr.TARGET, target);
285        }
286        return anchor;
287    }
288
289    public Content getModuleFramesHyperLink(ModuleElement mdle, Content label, String target) {
290        DocLink mdlLink = new DocLink(DocPaths.moduleFrame(mdle));
291        DocLink mtFrameLink = new DocLink(DocPaths.moduleTypeFrame(mdle));
292        DocLink cFrameLink = new DocLink(DocPaths.moduleSummary(mdle));
293        HtmlTree anchor = HtmlTree.A(mdlLink.toString(), label);
294        String onclickStr = "updateModuleFrame('" + mtFrameLink + "','" + cFrameLink + "');";
295        anchor.addAttr(HtmlAttr.TARGET, target);
296        anchor.addAttr(HtmlAttr.ONCLICK, onclickStr);
297        return anchor;
298    }
299
300    /**
301     * Get the enclosed name of the package
302     *
303     * @param te  TypeElement
304     * @return the name
305     */
306    public String getEnclosingPackageName(TypeElement te) {
307
308        PackageElement encl = configuration.utils.containingPackage(te);
309        return (encl.isUnnamed()) ? "" : (encl.getQualifiedName() + ".");
310    }
311
312    /**
313     * Print the frames version of the Html file header.
314     * Called only when generating an HTML frames file.
315     *
316     * @param title Title of this HTML document
317     * @param configuration the configuration object
318     * @param body the body content tree to be added to the HTML document
319     * @throws DocFileIOException if there is an error writing the frames document
320     */
321    public void printFramesDocument(String title, ConfigurationImpl configuration,
322            HtmlTree body) throws DocFileIOException {
323        Content htmlDocType = configuration.isOutputHtml5()
324                ? DocType.HTML5
325                : DocType.TRANSITIONAL;
326        Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
327        Content head = new HtmlTree(HtmlTag.HEAD);
328        head.addContent(getGeneratedBy(!configuration.notimestamp));
329        Content windowTitle = HtmlTree.TITLE(new StringContent(title));
330        head.addContent(windowTitle);
331        Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE,
332                (configuration.charset.length() > 0) ?
333                        configuration.charset : HtmlConstants.HTML_DEFAULT_CHARSET);
334        head.addContent(meta);
335        head.addContent(getStyleSheetProperties(configuration));
336        head.addContent(getFramesJavaScript());
337        Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
338                head, body);
339        Content htmlDocument = new HtmlDocument(htmlDocType,
340                htmlComment, htmlTree);
341        write(htmlDocument);
342    }
343
344    /**
345     * Returns a link to the stylesheet file.
346     *
347     * @param configuration the configuration for this doclet
348     * @return an HtmlTree for the lINK tag which provides the stylesheet location
349     */
350    public HtmlTree getStyleSheetProperties(ConfigurationImpl configuration) {
351        String stylesheetfile = configuration.stylesheetfile;
352        DocPath stylesheet;
353        if (stylesheetfile.isEmpty()) {
354            stylesheet = DocPaths.STYLESHEET;
355        } else {
356            DocFile file = DocFile.createFileForInput(configuration, stylesheetfile);
357            stylesheet = DocPath.create(file.getName());
358        }
359        HtmlTree link = HtmlTree.LINK("stylesheet", "text/css",
360                pathToRoot.resolve(stylesheet).getPath(),
361                "Style");
362        return link;
363    }
364
365    protected Comment getGeneratedBy(boolean timestamp) {
366        String text = "Generated by javadoc"; // marker string, deliberately not localized
367        if (timestamp) {
368            Calendar calendar = new GregorianCalendar(TimeZone.getDefault());
369            Date today = calendar.getTime();
370            text += " ("+ configuration.getDocletSpecificBuildDate() + ") on " + today;
371        }
372        return new Comment(text);
373    }
374}
375