1/*
2 * Copyright (c) 2001, 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 com.sun.tools.doclets.formats.html;
27
28import java.io.*;
29
30import javax.tools.FileObject;
31
32import com.sun.javadoc.*;
33import com.sun.tools.doclets.formats.html.markup.*;
34import com.sun.tools.doclets.internal.toolkit.*;
35import com.sun.tools.doclets.internal.toolkit.util.*;
36import com.sun.tools.javadoc.main.SourcePositionImpl;
37
38/**
39 * Converts Java Source Code to HTML.
40 *
41 *  <p><b>This is NOT part of any supported API.
42 *  If you write code that depends on this, you do so at your own risk.
43 *  This code and its internal interfaces are subject to change or
44 *  deletion without notice.</b>
45 *
46 * @author Jamie Ho
47 * @author Bhavesh Patel (Modified)
48 * @since 1.4
49 */
50@Deprecated
51public class SourceToHTMLConverter {
52
53    /**
54     * The number of trailing blank lines at the end of the page.
55     * This is inserted so that anchors at the bottom of small pages
56     * can be reached.
57     */
58    private static final int NUM_BLANK_LINES = 60;
59
60    /**
61     * New line to be added to the documentation.
62     */
63    private static final String NEW_LINE = DocletConstants.NL;
64
65    private final ConfigurationImpl configuration;
66    private final Utils utils;
67
68    private final RootDoc rootDoc;
69
70    private DocPath outputdir;
71
72    /**
73     * Relative path from the documentation root to the file that is being
74     * generated.
75     */
76    private DocPath relativePath = DocPath.empty;
77
78    private SourceToHTMLConverter(ConfigurationImpl configuration, RootDoc rd,
79            DocPath outputdir) {
80        this.configuration  = configuration;
81        this.utils = configuration.utils;
82        this.rootDoc = rd;
83        this.outputdir = outputdir;
84    }
85
86    /**
87     * Convert the Classes in the given RootDoc to an HTML.
88     *
89     * @param configuration the configuration.
90     * @param rd the RootDoc to convert.
91     * @param outputdir the name of the directory to output to.
92     */
93    public static void convertRoot(ConfigurationImpl configuration, RootDoc rd,
94            DocPath outputdir) {
95        new SourceToHTMLConverter(configuration, rd, outputdir).generate();
96    }
97
98    void generate() {
99        if (rootDoc == null || outputdir == null) {
100            return;
101        }
102        for (PackageDoc pd : rootDoc.specifiedPackages()) {
103            // If -nodeprecated option is set and the package is marked as deprecated,
104            // do not convert the package files to HTML.
105            if (!(configuration.nodeprecated && utils.isDeprecated(pd)))
106                convertPackage(pd, outputdir);
107        }
108        for (ClassDoc cd : rootDoc.specifiedClasses()) {
109            // If -nodeprecated option is set and the class is marked as deprecated
110            // or the containing package is deprecated, do not convert the
111            // package files to HTML.
112            if (!(configuration.nodeprecated &&
113                  (utils.isDeprecated(cd) || utils.isDeprecated(cd.containingPackage()))))
114                convertClass(cd, outputdir);
115        }
116    }
117
118    /**
119     * Convert the Classes in the given Package to an HTML.
120     *
121     * @param pd the Package to convert.
122     * @param outputdir the name of the directory to output to.
123     */
124    public void convertPackage(PackageDoc pd, DocPath outputdir) {
125        if (pd == null) {
126            return;
127        }
128        for (ClassDoc cd : pd.allClasses()) {
129            // If -nodeprecated option is set and the class is marked as deprecated,
130            // do not convert the package files to HTML. We do not check for
131            // containing package deprecation since it is already check in
132            // the calling method above.
133            if (!(configuration.nodeprecated && utils.isDeprecated(cd)))
134                convertClass(cd, outputdir);
135        }
136    }
137
138    /**
139     * Convert the given Class to an HTML.
140     *
141     * @param cd the class to convert.
142     * @param outputdir the name of the directory to output to.
143     */
144    public void convertClass(ClassDoc cd, DocPath outputdir) {
145        if (cd == null) {
146            return;
147        }
148        try {
149            SourcePosition sp = cd.position();
150            if (sp == null)
151                return;
152            Reader r;
153            // temp hack until we can update SourcePosition API.
154            if (sp instanceof SourcePositionImpl) {
155                FileObject fo = ((SourcePositionImpl) sp).fileObject();
156                if (fo == null)
157                    return;
158                r = fo.openReader(true);
159            } else {
160                File file = sp.file();
161                if (file == null)
162                    return;
163                r = new FileReader(file);
164            }
165            int lineno = 1;
166            String line;
167            relativePath = DocPaths.SOURCE_OUTPUT
168                    .resolve(DocPath.forPackage(cd))
169                    .invert();
170            Content body = getHeader();
171            Content pre = new HtmlTree(HtmlTag.PRE);
172            try (LineNumberReader reader = new LineNumberReader(r)) {
173                while ((line = reader.readLine()) != null) {
174                    addLineNo(pre, lineno);
175                    addLine(pre, line, lineno);
176                    lineno++;
177                }
178            }
179            addBlankLines(pre);
180            Content div = HtmlTree.DIV(HtmlStyle.sourceContainer, pre);
181            body.addContent((configuration.allowTag(HtmlTag.MAIN)) ? HtmlTree.MAIN(div) : div);
182            writeToFile(body, outputdir.resolve(DocPath.forClass(cd)));
183        } catch (IOException e) {
184            e.printStackTrace();
185        }
186    }
187
188    /**
189     * Write the output to the file.
190     *
191     * @param body the documentation content to be written to the file.
192     * @param path the path for the file.
193     */
194    private void writeToFile(Content body, DocPath path) throws IOException {
195        Content htmlDocType = configuration.isOutputHtml5()
196                ? DocType.HTML5
197                : DocType.TRANSITIONAL;
198        Content head = new HtmlTree(HtmlTag.HEAD);
199        head.addContent(HtmlTree.TITLE(new StringContent(
200                configuration.getText("doclet.Window_Source_title"))));
201        head.addContent(getStyleSheetProperties());
202        Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
203                head, body);
204        Content htmlDocument = new HtmlDocument(htmlDocType, htmlTree);
205        configuration.message.notice("doclet.Generating_0", path.getPath());
206        DocFile df = DocFile.createFileForOutput(configuration, path);
207        try (Writer w = df.openWriter()) {
208            htmlDocument.write(w, true);
209        }
210
211    }
212
213    /**
214     * Returns a link to the stylesheet file.
215     *
216     * @return an HtmlTree for the lINK tag which provides the stylesheet location
217     */
218    public HtmlTree getStyleSheetProperties() {
219        String filename = configuration.stylesheetfile;
220        DocPath stylesheet;
221        if (filename.length() > 0) {
222            DocFile file = DocFile.createFileForInput(configuration, filename);
223            stylesheet = DocPath.create(file.getName());
224        } else {
225            stylesheet = DocPaths.STYLESHEET;
226        }
227        DocPath p = relativePath.resolve(stylesheet);
228        HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", p.getPath(), "Style");
229        return link;
230    }
231
232    /**
233     * Get the header.
234     *
235     * @return the header content for the HTML file
236     */
237    private static Content getHeader() {
238        return new HtmlTree(HtmlTag.BODY);
239    }
240
241    /**
242     * Add the line numbers for the source code.
243     *
244     * @param pre the content tree to which the line number will be added
245     * @param lineno The line number
246     */
247    private static void addLineNo(Content pre, int lineno) {
248        HtmlTree span = new HtmlTree(HtmlTag.SPAN);
249        span.addStyle(HtmlStyle.sourceLineNo);
250        if (lineno < 10) {
251            span.addContent("00" + Integer.toString(lineno));
252        } else if (lineno < 100) {
253            span.addContent("0" + Integer.toString(lineno));
254        } else {
255            span.addContent(Integer.toString(lineno));
256        }
257        pre.addContent(span);
258    }
259
260    /**
261     * Add a line from source to the HTML file that is generated.
262     *
263     * @param pre the content tree to which the line will be added.
264     * @param line the string to format.
265     * @param currentLineNo the current number.
266     */
267    private void addLine(Content pre, String line, int currentLineNo) {
268        if (line != null) {
269            Content anchor = HtmlTree.A(configuration.htmlVersion,
270                    "line." + Integer.toString(currentLineNo),
271                    new StringContent(utils.replaceTabs(configuration, line)));
272            pre.addContent(anchor);
273            pre.addContent(NEW_LINE);
274        }
275    }
276
277    /**
278     * Add trailing blank lines at the end of the page.
279     *
280     * @param pre the content tree to which the blank lines will be added.
281     */
282    private static void addBlankLines(Content pre) {
283        for (int i = 0; i < NUM_BLANK_LINES; i++) {
284            pre.addContent(NEW_LINE);
285        }
286    }
287
288    /**
289     * Given a <code>Doc</code>, return an anchor name for it.
290     *
291     * @param d the <code>Doc</code> to check.
292     * @return the name of the anchor.
293     */
294    public static String getAnchorName(Doc d) {
295        return "line." + d.position().line();
296    }
297}
298