001    /*
002     * Copyright 1998-2008 Sun Microsystems, Inc.  All Rights Reserved.
003     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004     *
005     * This code is free software; you can redistribute it and/or modify it
006     * under the terms of the GNU General Public License version 2 only, as
007     * published by the Free Software Foundation.  Sun designates this
008     * particular file as subject to the "Classpath" exception as provided
009     * by Sun in the LICENSE file that accompanied this code.
010     *
011     * This code is distributed in the hope that it will be useful, but WITHOUT
012     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014     * version 2 for more details (a copy is included in the LICENSE file that
015     * accompanied this code).
016     *
017     * You should have received a copy of the GNU General Public License version
018     * 2 along with this work; if not, write to the Free Software Foundation,
019     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020     *
021     * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022     * CA 95054 USA or visit www.sun.com if you need additional information or
023     * have any questions.
024     */
025    
026    package com.sun.tools.doclets.formats.html;
027    import com.sun.tools.doclets.formats.html.markup.*;
028    
029    import com.sun.tools.doclets.internal.toolkit.*;
030    import com.sun.tools.doclets.internal.toolkit.util.*;
031    import com.sun.tools.doclets.internal.toolkit.taglets.*;
032    
033    import com.sun.javadoc.*;
034    import java.io.*;
035    import java.text.SimpleDateFormat;
036    import java.util.*;
037    
038    
039    /**
040     * Class for the Html Format Code Generation specific to JavaDoc.
041     * This Class contains methods related to the Html Code Generation which
042     * are used extensively while generating the entire documentation.
043     *
044     * @since 1.2
045     * @author Atul M Dambalkar
046     * @author Robert Field
047     */
048    public class HtmlDocletWriter extends HtmlDocWriter {
049    
050        /**
051         * Relative path from the file getting generated to the destination
052         * directory. For example, if the file getting generated is
053         * "java/lang/Object.html", then the relative path string is "../../".
054         * This string can be empty if the file getting generated is in
055         * the destination directory.
056         */
057        public String relativePath = "";
058    
059        /**
060         * Same as relativepath, but normalized to never be empty or
061         * end with a slash.
062         */
063        public String relativepathNoSlash = "";
064    
065        /**
066         * Platform-dependent directory path from the current or the
067         * destination directory to the file getting generated.
068         * Used when creating the file.
069         * For example, if the file getting generated is
070         * "java/lang/Object.html", then the path string is "java/lang".
071         */
072        public String path = "";
073    
074        /**
075         * Name of the file getting generated. If the file getting generated is
076         * "java/lang/Object.html", then the filename is "Object.html".
077         */
078        public String filename = "";
079    
080        /**
081         * The display length used for indentation while generating the class page.
082         */
083        public int displayLength = 0;
084    
085        /**
086         * The global configuration information for this run.
087         */
088        public ConfigurationImpl configuration;
089    
090        /**
091         * Constructor to construct the HtmlStandardWriter object.
092         *
093         * @param filename File to be generated.
094         */
095        public HtmlDocletWriter(ConfigurationImpl configuration,
096                                  String filename) throws IOException {
097            super(configuration, filename);
098            this.configuration = configuration;
099            this.filename = filename;
100        }
101    
102        /**
103         * Constructor to construct the HtmlStandardWriter object.
104         *
105         * @param path         Platform-dependent {@link #path} used when
106         *                     creating file.
107         * @param filename     Name of file to be generated.
108         * @param relativePath Value for the variable {@link #relativePath}.
109         */
110        public HtmlDocletWriter(ConfigurationImpl configuration,
111                                  String path, String filename,
112                                  String relativePath) throws IOException {
113            super(configuration, path, filename);
114            this.configuration = configuration;
115            this.path = path;
116            this.relativePath = relativePath;
117            this.relativepathNoSlash =
118                DirectoryManager.getPathNoTrailingSlash(this.relativePath);
119            this.filename = filename;
120        }
121    
122        /**
123         * Replace {@docRoot} tag used in options that accept HTML text, such
124         * as -header, -footer, -top and -bottom, and when converting a relative
125         * HREF where commentTagsToString inserts a {@docRoot} where one was
126         * missing.  (Also see DocRootTaglet for {@docRoot} tags in doc
127         * comments.)
128         * <p>
129         * Replace {&#064;docRoot} tag in htmlstr with the relative path to the
130         * destination directory from the directory where the file is being
131         * written, looping to handle all such tags in htmlstr.
132         * <p>
133         * For example, for "-d docs" and -header containing {&#064;docRoot}, when
134         * the HTML page for source file p/C1.java is being generated, the
135         * {&#064;docRoot} tag would be inserted into the header as "../",
136         * the relative path from docs/p/ to docs/ (the document root).
137         * <p>
138         * Note: This doc comment was written with '&amp;#064;' representing '@'
139         * to prevent the inline tag from being interpreted.
140         */
141        public String replaceDocRootDir(String htmlstr) {
142            // Return if no inline tags exist
143            int index = htmlstr.indexOf("{@");
144            if (index < 0) {
145                return htmlstr;
146            }
147            String lowerHtml = htmlstr.toLowerCase();
148            // Return index of first occurrence of {@docroot}
149            // Note: {@docRoot} is not case sensitive when passed in w/command line option
150            index = lowerHtml.indexOf("{@docroot}", index);
151            if (index < 0) {
152                return htmlstr;
153            }
154            StringBuffer buf = new StringBuffer();
155            int previndex = 0;
156            while (true) {
157                // Search for lowercase version of {@docRoot}
158                index = lowerHtml.indexOf("{@docroot}", previndex);
159                // If next {@docRoot} tag not found, append rest of htmlstr and exit loop
160                if (index < 0) {
161                    buf.append(htmlstr.substring(previndex));
162                    break;
163                }
164                // If next {@docroot} tag found, append htmlstr up to start of tag
165                buf.append(htmlstr.substring(previndex, index));
166                previndex = index + 10;  // length for {@docroot} string
167                // Insert relative path where {@docRoot} was located
168                buf.append(relativepathNoSlash);
169                // Append slash if next character is not a slash
170                if (relativepathNoSlash.length() > 0 && previndex < htmlstr.length()
171                        && htmlstr.charAt(previndex) != '/') {
172                    buf.append(DirectoryManager.URL_FILE_SEPERATOR);
173                }
174            }
175            return buf.toString();
176        }
177    
178        /**
179         * Print Html Hyper Link, with target frame.  This
180         * link will only appear if page is not in a frame.
181         *
182         * @param link String name of the file.
183         * @param where Position in the file
184         * @param target Name of the target frame.
185         * @param label Tag for the link.
186         * @param strong Whether the label should be strong or not?
187         */
188        public void printNoFramesTargetHyperLink(String link, String where,
189                                                   String target, String label,
190                                                   boolean strong) {
191            script();
192            println("  <!--");
193            println("  if(window==top) {");
194            println("    document.writeln('"
195                + getHyperLink(link, where, label, strong, "", "", target) + "');");
196            println("  }");
197            println("  //-->");
198            scriptEnd();
199            noScript();
200            println("  " + getHyperLink(link, where, label, strong, "", "", target));
201            noScriptEnd();
202            println(DocletConstants.NL);
203        }
204    
205        private void printMethodInfo(MethodDoc method) {
206            ClassDoc[] intfacs = method.containingClass().interfaces();
207            MethodDoc overriddenMethod = method.overriddenMethod();
208            if (intfacs.length > 0 || overriddenMethod != null) {
209                dd();
210                printTagsInfoHeader();
211                MethodWriterImpl.printImplementsInfo(this, method);
212                if (overriddenMethod != null) {
213                    MethodWriterImpl.printOverridden(this,
214                        method.overriddenType(), overriddenMethod);
215                }
216                printTagsInfoFooter();
217                ddEnd();
218            }
219            dd();
220        }
221    
222        protected void printTags(Doc doc) {
223            if(configuration.nocomment){
224                return;
225            }
226            if (doc instanceof MethodDoc) {
227                printMethodInfo((MethodDoc) doc);
228            }
229            TagletOutputImpl output = new TagletOutputImpl("");
230            TagletWriter.genTagOuput(configuration.tagletManager, doc,
231                configuration.tagletManager.getCustomTags(doc),
232                    getTagletWriterInstance(false), output);
233            if (output.toString().trim().length() > 0) {
234                printTagsInfoHeader();
235                print(output.toString());
236                printTagsInfoFooter();
237            } else if (! (doc instanceof ConstructorDoc ||
238                doc instanceof RootDoc || doc instanceof ClassDoc)) {
239                //To be consistent with 1.4.2 output.
240                //I hate to do this but we have to pass the diff test to prove
241                //nothing has broken.
242                printTagsInfoHeader();
243                printTagsInfoFooter();
244            }
245        }
246    
247        /**
248         * Returns a TagletWriter that knows how to write HTML.
249         *
250         * @return a TagletWriter that knows how to write HTML.
251         */
252        public TagletWriter getTagletWriterInstance(boolean isFirstSentence) {
253            return new TagletWriterImpl(this, isFirstSentence);
254        }
255    
256        protected void printTagsInfoHeader() {
257            dl();
258        }
259    
260        protected void printTagsInfoFooter() {
261            dlEnd();
262        }
263    
264        /**
265         * Print Package link, with target frame.
266         *
267         * @param pd The link will be to the "package-summary.html" page for this
268         * package.
269         * @param target Name of the target frame.
270         * @param label Tag for the link.
271         */
272        public void printTargetPackageLink(PackageDoc pd, String target,
273            String label) {
274            print(getHyperLink(pathString(pd, "package-summary.html"), "", label,
275                false, "", "", target));
276        }
277    
278        /**
279         * Print the html file header. Also print Html page title and stylesheet
280         * default properties.
281         *
282         * @param title         String window title to go in the &lt;TITLE&gt; tag
283         * @param metakeywords  Array of String keywords for META tag.  Each element
284         *                      of the array is assigned to a separate META tag.
285         *                      Pass in null for no array.
286         * @param includeScript boolean true if printing windowtitle script.
287         *             False for files that appear in the left-hand frames.
288         */
289        public void printHtmlHeader(String title, String[] metakeywords,
290                boolean includeScript) {
291            println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 " +
292                        "Transitional//EN\" " +
293                        "\"http://www.w3.org/TR/html4/loose.dtd\">");
294            println("<!--NewPage-->");
295            html();
296            head();
297            if (! configuration.notimestamp) {
298                print("<!-- Generated by javadoc (build " + ConfigurationImpl.BUILD_DATE + ") on ");
299                print(today());
300                println(" -->");
301            }
302            if (configuration.charset.length() > 0) {
303                println("<META http-equiv=\"Content-Type\" content=\"text/html; "
304                            + "charset=" + configuration.charset + "\">");
305            }
306            if ( configuration.windowtitle.length() > 0 ) {
307                title += " (" + configuration.windowtitle  + ")";
308            }
309            title(title);
310            println(title);
311            titleEnd();
312            println("");
313            if (! configuration.notimestamp) {
314                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
315                    println("<META NAME=\"date\" "
316                                        + "CONTENT=\"" + dateFormat.format(new Date()) + "\">");
317            }
318            if ( metakeywords != null ) {
319                for ( int i=0; i < metakeywords.length; i++ ) {
320                    println("<META NAME=\"keywords\" "
321                                + "CONTENT=\"" + metakeywords[i] + "\">");
322                }
323            }
324            println("");
325            printStyleSheetProperties();
326            println("");
327            // Don't print windowtitle script for overview-frame, allclasses-frame
328            // and package-frame
329            if (includeScript) {
330                printWinTitleScript(title);
331            }
332            println("");
333            headEnd();
334            println("");
335            body("white", includeScript);
336        }
337    
338        /**
339         * Print user specified header and the footer.
340         *
341         * @param header if true print the user provided header else print the
342         * user provided footer.
343         */
344        public void printUserHeaderFooter(boolean header) {
345            em();
346            if (header) {
347                print(replaceDocRootDir(configuration.header));
348            } else {
349                if (configuration.footer.length() != 0) {
350                    print(replaceDocRootDir(configuration.footer));
351                } else {
352                    print(replaceDocRootDir(configuration.header));
353                }
354            }
355            emEnd();
356        }
357    
358        /**
359         * Print the user specified top.
360         */
361        public void printTop() {
362            print(replaceDocRootDir(configuration.top));
363            hr();
364        }
365    
366        /**
367         * Print the user specified bottom.
368         */
369        public void printBottom() {
370            hr();
371            print(replaceDocRootDir(configuration.bottom));
372        }
373    
374        /**
375         * Print the navigation bar for the Html page at the top and and the bottom.
376         *
377         * @param header If true print navigation bar at the top of the page else
378         * print the nevigation bar at the bottom.
379         */
380        protected void navLinks(boolean header) {
381            println("");
382            if (!configuration.nonavbar) {
383                if (header) {
384                    println(DocletConstants.NL + "<!-- ========= START OF TOP NAVBAR ======= -->");
385                    anchor("navbar_top");
386                    println();
387                    print(getHyperLink("", "skip-navbar_top", "", false, "",
388                        configuration.getText("doclet.Skip_navigation_links"), ""));
389                } else {
390                    println(DocletConstants.NL + "<!-- ======= START OF BOTTOM NAVBAR ====== -->");
391                    anchor("navbar_bottom");
392                    println();
393                    print(getHyperLink("", "skip-navbar_bottom", "", false, "",
394                        configuration.getText("doclet.Skip_navigation_links"), ""));
395                }
396                table(0, "100%", 1, 0);
397                tr();
398                tdColspanBgcolorStyle(2, "#EEEEFF", "NavBarCell1");
399                println("");
400                if (header) {
401                    anchor("navbar_top_firstrow");
402                } else {
403                    anchor("navbar_bottom_firstrow");
404                }
405                table(0, 0, 3);
406                print("  ");
407                trAlignVAlign("center", "top");
408    
409                if (configuration.createoverview) {
410                    navLinkContents();
411                }
412    
413                if (configuration.packages.length == 1) {
414                    navLinkPackage(configuration.packages[0]);
415                } else if (configuration.packages.length > 1) {
416                    navLinkPackage();
417                }
418    
419                navLinkClass();
420    
421                if(configuration.classuse) {
422                    navLinkClassUse();
423                }
424                if(configuration.createtree) {
425                    navLinkTree();
426                }
427                if(!(configuration.nodeprecated ||
428                         configuration.nodeprecatedlist)) {
429                    navLinkDeprecated();
430                }
431                if(configuration.createindex) {
432                    navLinkIndex();
433                }
434                if (!configuration.nohelp) {
435                    navLinkHelp();
436                }
437                print("  ");
438                trEnd();
439                tableEnd();
440                tdEnd();
441    
442                tdAlignVAlignRowspan("right", "top", 3);
443    
444                printUserHeaderFooter(header);
445                tdEnd();
446                trEnd();
447                println("");
448    
449                tr();
450                tdBgcolorStyle("white", "NavBarCell2");
451                font("-2");
452                space();
453                navLinkPrevious();
454                space();
455                println("");
456                space();
457                navLinkNext();
458                fontEnd();
459                tdEnd();
460    
461                tdBgcolorStyle("white", "NavBarCell2");
462                font("-2");
463                print("  ");
464                navShowLists();
465                print("  ");
466                space();
467                println("");
468                space();
469                navHideLists(filename);
470                print("  ");
471                space();
472                println("");
473                space();
474                navLinkClassIndex();
475                fontEnd();
476                tdEnd();
477    
478                trEnd();
479    
480                printSummaryDetailLinks();
481    
482                tableEnd();
483                if (header) {
484                    aName("skip-navbar_top");
485                    aEnd();
486                    println(DocletConstants.NL + "<!-- ========= END OF TOP NAVBAR ========= -->");
487                } else {
488                    aName("skip-navbar_bottom");
489                    aEnd();
490                    println(DocletConstants.NL + "<!-- ======== END OF BOTTOM NAVBAR ======= -->");
491                }
492                println("");
493            }
494        }
495    
496        /**
497         * Print the word "NEXT" to indicate that no link is available.  Override
498         * this method to customize next link.
499         */
500        protected void navLinkNext() {
501            navLinkNext(null);
502        }
503    
504        /**
505         * Print the word "PREV" to indicate that no link is available.  Override
506         * this method to customize prev link.
507         */
508        protected void navLinkPrevious() {
509            navLinkPrevious(null);
510        }
511    
512        /**
513         * Do nothing. This is the default method.
514         */
515        protected void printSummaryDetailLinks() {
516        }
517    
518        /**
519         * Print link to the "overview-summary.html" page.
520         */
521        protected void navLinkContents() {
522            navCellStart();
523            printHyperLink(relativePath + "overview-summary.html", "",
524                           configuration.getText("doclet.Overview"), true, "NavBarFont1");
525            navCellEnd();
526        }
527    
528        /**
529         * Description for a cell in the navigation bar.
530         */
531        protected void navCellStart() {
532            print("  ");
533            tdBgcolorStyle("#EEEEFF", "NavBarCell1");
534            print("    ");
535        }
536    
537        /**
538         * Description for a cell in the navigation bar, but with reverse
539         * high-light effect.
540         */
541        protected void navCellRevStart() {
542            print("  ");
543            tdBgcolorStyle("#FFFFFF", "NavBarCell1Rev");
544            print(" ");
545            space();
546        }
547    
548        /**
549         * Closing tag for navigation bar cell.
550         */
551        protected void navCellEnd() {
552            space();
553            tdEnd();
554        }
555    
556        /**
557         * Print link to the "package-summary.html" page for the package passed.
558         *
559         * @param pkg Package to which link will be generated.
560         */
561        protected void navLinkPackage(PackageDoc pkg) {
562            navCellStart();
563            printPackageLink(pkg, configuration.getText("doclet.Package"), true,
564                "NavBarFont1");
565            navCellEnd();
566        }
567    
568        /**
569         * Print the word "Package" in the navigation bar cell, to indicate that
570         * link is not available here.
571         */
572        protected void navLinkPackage() {
573            navCellStart();
574            fontStyle("NavBarFont1");
575            printText("doclet.Package");
576            fontEnd();
577            navCellEnd();
578        }
579    
580        /**
581         * Print the word "Use" in the navigation bar cell, to indicate that link
582         * is not available.
583         */
584        protected void navLinkClassUse() {
585            navCellStart();
586            fontStyle("NavBarFont1");
587            printText("doclet.navClassUse");
588            fontEnd();
589            navCellEnd();
590        }
591    
592        /**
593         * Print link for previous file.
594         *
595         * @param prev File name for the prev link.
596         */
597        public void navLinkPrevious(String prev) {
598            String tag = configuration.getText("doclet.Prev");
599            if (prev != null) {
600                printHyperLink(prev, "", tag, true) ;
601            } else {
602                print(tag);
603            }
604        }
605    
606        /**
607         * Print link for next file.  If next is null, just print the label
608         * without linking it anywhere.
609         *
610         * @param next File name for the next link.
611         */
612        public void navLinkNext(String next) {
613            String tag = configuration.getText("doclet.Next");
614            if (next != null) {
615                printHyperLink(next, "", tag, true);
616            } else {
617                print(tag);
618            }
619        }
620    
621        /**
622         * Print "FRAMES" link, to switch to the frame version of the output.
623         *
624         * @param link File to be linked, "index.html".
625         */
626        protected void navShowLists(String link) {
627            print(getHyperLink(link + "?" + path + filename, "",
628                configuration.getText("doclet.FRAMES"), true, "", "", "_top"));
629        }
630    
631        /**
632         * Print "FRAMES" link, to switch to the frame version of the output.
633         */
634        protected void navShowLists() {
635            navShowLists(relativePath + "index.html");
636        }
637    
638        /**
639         * Print "NO FRAMES" link, to switch to the non-frame version of the output.
640         *
641         * @param link File to be linked.
642         */
643        protected void navHideLists(String link) {
644            print(getHyperLink(link, "", configuration.getText("doclet.NO_FRAMES"),
645                true, "", "", "_top"));
646        }
647    
648        /**
649         * Print "Tree" link in the navigation bar. If there is only one package
650         * specified on the command line, then the "Tree" link will be to the
651         * only "package-tree.html" file otherwise it will be to the
652         * "overview-tree.html" file.
653         */
654        protected void navLinkTree() {
655            navCellStart();
656            PackageDoc[] packages = configuration.root.specifiedPackages();
657            if (packages.length == 1 && configuration.root.specifiedClasses().length == 0) {
658                printHyperLink(pathString(packages[0], "package-tree.html"), "",
659                               configuration.getText("doclet.Tree"), true, "NavBarFont1");
660            } else {
661                printHyperLink(relativePath + "overview-tree.html", "",
662                               configuration.getText("doclet.Tree"), true, "NavBarFont1");
663            }
664            navCellEnd();
665        }
666    
667        /**
668         * Print "Tree" link to the "overview-tree.html" file.
669         */
670        protected void navLinkMainTree(String label) {
671            printHyperLink(relativePath + "overview-tree.html", label);
672        }
673    
674        /**
675         * Print the word "Class" in the navigation bar cell, to indicate that
676         * class link is not available.
677         */
678        protected void navLinkClass() {
679            navCellStart();
680            fontStyle("NavBarFont1");
681            printText("doclet.Class");
682            fontEnd();
683            navCellEnd();
684        }
685    
686        /**
687         * Print "Deprecated" API link in the navigation bar.
688         */
689        protected void navLinkDeprecated() {
690            navCellStart();
691            printHyperLink(relativePath + "deprecated-list.html", "",
692                           configuration.getText("doclet.navDeprecated"), true, "NavBarFont1");
693            navCellEnd();
694        }
695    
696        /**
697         * Print link for generated index. If the user has used "-splitindex"
698         * command line option, then link to file "index-files/index-1.html" is
699         * generated otherwise link to file "index-all.html" is generated.
700         */
701        protected void navLinkClassIndex() {
702            printNoFramesTargetHyperLink(relativePath +
703                    AllClassesFrameWriter.OUTPUT_FILE_NAME_NOFRAMES,
704                "", "", configuration.getText("doclet.All_Classes"), true);
705        }
706        /**
707         * Print link for generated class index.
708         */
709        protected void navLinkIndex() {
710            navCellStart();
711            printHyperLink(relativePath +
712                               (configuration.splitindex?
713                                    DirectoryManager.getPath("index-files") +
714                                    fileseparator: "") +
715                               (configuration.splitindex?
716                                    "index-1.html" : "index-all.html"), "",
717                           configuration.getText("doclet.Index"), true, "NavBarFont1");
718            navCellEnd();
719        }
720    
721        /**
722         * Print help file link. If user has provided a help file, then generate a
723         * link to the user given file, which is already copied to current or
724         * destination directory.
725         */
726        protected void navLinkHelp() {
727            String helpfilenm = configuration.helpfile;
728            if (helpfilenm.equals("")) {
729                helpfilenm = "help-doc.html";
730            } else {
731                int lastsep;
732                if ((lastsep = helpfilenm.lastIndexOf(File.separatorChar)) != -1) {
733                    helpfilenm = helpfilenm.substring(lastsep + 1);
734                }
735            }
736            navCellStart();
737            printHyperLink(relativePath + helpfilenm, "",
738                           configuration.getText("doclet.Help"), true, "NavBarFont1");
739            navCellEnd();
740        }
741    
742        /**
743         * Print the word "Detail" in the navigation bar. No link is available.
744         */
745        protected void navDetail() {
746            printText("doclet.Detail");
747        }
748    
749        /**
750         * Print the word "Summary" in the navigation bar. No link is available.
751         */
752        protected void navSummary() {
753            printText("doclet.Summary");
754        }
755    
756        /**
757         * Print the Html table tag for the index summary tables. The table tag
758         * printed is
759         * &lt;TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
760         */
761        public void tableIndexSummary() {
762            table(1, "100%", 3, 0);
763        }
764    
765        /**
766         * Same as {@link #tableIndexSummary()}.
767         */
768        public void tableIndexDetail() {
769            table(1, "100%", 3, 0);
770        }
771    
772        /**
773         * Print Html tag for table elements. The tag printed is
774         * &lt;TD ALIGN="right" VALIGN="top" WIDTH="1%"&gt;.
775         */
776        public void tdIndex() {
777            print("<TD ALIGN=\"right\" VALIGN=\"top\" WIDTH=\"1%\">");
778        }
779    
780        /**
781         * Prine table header information about color, column span and the font.
782         *
783         * @param color Background color.
784         * @param span  Column span.
785         */
786        public void tableHeaderStart(String color, int span) {
787            trBgcolorStyle(color, "TableHeadingColor");
788            thAlignColspan("left", span);
789            font("+2");
790        }
791    
792        /**
793         * Print table header for the inherited members summary tables. Print the
794         * background color information.
795         *
796         * @param color Background color.
797         */
798        public void tableInheritedHeaderStart(String color) {
799            trBgcolorStyle(color, "TableSubHeadingColor");
800            thAlign("left");
801        }
802    
803        /**
804         * Print "Use" table header. Print the background color and the column span.
805         *
806         * @param color Background color.
807         */
808        public void tableUseInfoHeaderStart(String color) {
809            trBgcolorStyle(color, "TableSubHeadingColor");
810            thAlignColspan("left", 2);
811        }
812    
813        /**
814         * Print table header with the background color with default column span 2.
815         *
816         * @param color Background color.
817         */
818        public void tableHeaderStart(String color) {
819            tableHeaderStart(color, 2);
820        }
821    
822        /**
823         * Print table header with the column span, with the default color #CCCCFF.
824         *
825         * @param span Column span.
826         */
827        public void tableHeaderStart(int span) {
828            tableHeaderStart("#CCCCFF", span);
829        }
830    
831        /**
832         * Print table header with default column span 2 and default color #CCCCFF.
833         */
834        public void tableHeaderStart() {
835            tableHeaderStart(2);
836        }
837    
838        /**
839         * Print table header end tags for font, column and row.
840         */
841        public void tableHeaderEnd() {
842            fontEnd();
843            thEnd();
844            trEnd();
845        }
846    
847        /**
848         * Print table header end tags in inherited tables for column and row.
849         */
850        public void tableInheritedHeaderEnd() {
851            thEnd();
852            trEnd();
853        }
854    
855        /**
856         * Print the summary table row cell attribute width.
857         *
858         * @param width Width of the table cell.
859         */
860        public void summaryRow(int width) {
861            if (width != 0) {
862                tdWidth(width + "%");
863            } else {
864                td();
865            }
866        }
867    
868        /**
869         * Print the summary table row cell end tag.
870         */
871        public void summaryRowEnd() {
872            tdEnd();
873        }
874    
875        /**
876         * Print the heading in Html &lt;H2> format.
877         *
878         * @param str The Header string.
879         */
880        public void printIndexHeading(String str) {
881            h2();
882            print(str);
883            h2End();
884        }
885    
886        /**
887         * Print Html tag &lt;FRAMESET=arg&gt;.
888         *
889         * @param arg Argument for the tag.
890         */
891        public void frameSet(String arg) {
892            println("<FRAMESET " + arg + ">");
893        }
894    
895        /**
896         * Print Html closing tag &lt;/FRAMESET&gt;.
897         */
898        public void frameSetEnd() {
899            println("</FRAMESET>");
900        }
901    
902        /**
903         * Print Html tag &lt;FRAME=arg&gt;.
904         *
905         * @param arg Argument for the tag.
906         */
907        public void frame(String arg) {
908            println("<FRAME " + arg + ">");
909        }
910    
911        /**
912         * Print Html closing tag &lt;/FRAME&gt;.
913         */
914        public void frameEnd() {
915            println("</FRAME>");
916        }
917    
918        /**
919         * Return path to the class page for a classdoc. For example, the class
920         * name is "java.lang.Object" and if the current file getting generated is
921         * "java/io/File.html", then the path string to the class, returned is
922         * "../../java/lang.Object.html".
923         *
924         * @param cd Class to which the path is requested.
925         */
926        protected String pathToClass(ClassDoc cd) {
927            return pathString(cd.containingPackage(), cd.name() + ".html");
928        }
929    
930        /**
931         * Return the path to the class page for a classdoc. Works same as
932         * {@link #pathToClass(ClassDoc)}.
933         *
934         * @param cd   Class to which the path is requested.
935         * @param name Name of the file(doesn't include path).
936         */
937        protected String pathString(ClassDoc cd, String name) {
938            return pathString(cd.containingPackage(), name);
939        }
940    
941        /**
942         * Return path to the given file name in the given package. So if the name
943         * passed is "Object.html" and the name of the package is "java.lang", and
944         * if the relative path is "../.." then returned string will be
945         * "../../java/lang/Object.html"
946         *
947         * @param pd Package in which the file name is assumed to be.
948         * @param name File name, to which path string is.
949         */
950        protected String pathString(PackageDoc pd, String name) {
951            StringBuffer buf = new StringBuffer(relativePath);
952            buf.append(DirectoryManager.getPathToPackage(pd, name));
953            return buf.toString();
954        }
955    
956        /**
957         * Print the link to the given package.
958         *
959         * @param pkg the package to link to.
960         * @param label the label for the link.
961         * @param isStrong true if the label should be strong.
962         */
963        public void printPackageLink(PackageDoc pkg, String label, boolean isStrong) {
964            print(getPackageLink(pkg, label, isStrong));
965        }
966    
967        /**
968         * Print the link to the given package.
969         *
970         * @param pkg the package to link to.
971         * @param label the label for the link.
972         * @param isStrong true if the label should be strong.
973         * @param style  the font of the package link label.
974         */
975        public void printPackageLink(PackageDoc pkg, String label, boolean isStrong,
976                String style) {
977            print(getPackageLink(pkg, label, isStrong, style));
978        }
979    
980        /**
981         * Return the link to the given package.
982         *
983         * @param pkg the package to link to.
984         * @param label the label for the link.
985         * @param isStrong true if the label should be strong.
986         * @return the link to the given package.
987         */
988        public String getPackageLink(PackageDoc pkg, String label,
989                                     boolean isStrong) {
990            return getPackageLink(pkg, label, isStrong, "");
991        }
992    
993        /**
994         * Return the link to the given package.
995         *
996         * @param pkg the package to link to.
997         * @param label the label for the link.
998         * @param isStrong true if the label should be strong.
999         * @param style  the font of the package link label.
1000         * @return the link to the given package.
1001         */
1002        public String getPackageLink(PackageDoc pkg, String label, boolean isStrong,
1003                String style) {
1004            boolean included = pkg != null && pkg.isIncluded();
1005            if (! included) {
1006                PackageDoc[] packages = configuration.packages;
1007                for (int i = 0; i < packages.length; i++) {
1008                    if (packages[i].equals(pkg)) {
1009                        included = true;
1010                        break;
1011                    }
1012                }
1013            }
1014            if (included || pkg == null) {
1015                return getHyperLink(pathString(pkg, "package-summary.html"),
1016                                    "", label, isStrong, style);
1017            } else {
1018                String crossPkgLink = getCrossPackageLink(Util.getPackageName(pkg));
1019                if (crossPkgLink != null) {
1020                    return getHyperLink(crossPkgLink, "", label, isStrong, style);
1021                } else {
1022                    return label;
1023                }
1024            }
1025        }
1026    
1027        public String italicsClassName(ClassDoc cd, boolean qual) {
1028            String name = (qual)? cd.qualifiedName(): cd.name();
1029            return (cd.isInterface())?  italicsText(name): name;
1030        }
1031    
1032        public void printSrcLink(ProgramElementDoc d, String label) {
1033            if (d == null) {
1034                return;
1035            }
1036            ClassDoc cd = d.containingClass();
1037            if (cd == null) {
1038                //d must be a class doc since in has no containing class.
1039                cd = (ClassDoc) d;
1040            }
1041            String href = relativePath + DocletConstants.SOURCE_OUTPUT_DIR_NAME
1042                + DirectoryManager.getDirectoryPath(cd.containingPackage())
1043                + cd.name() + ".html#" + SourceToHTMLConverter.getAnchorName(d);
1044            printHyperLink(href, "", label, true);
1045        }
1046    
1047        /**
1048         * Return the link to the given class.
1049         *
1050         * @param linkInfo the information about the link.
1051         *
1052         * @return the link for the given class.
1053         */
1054        public String getLink(LinkInfoImpl linkInfo) {
1055            LinkFactoryImpl factory = new LinkFactoryImpl(this);
1056            String link = ((LinkOutputImpl) factory.getLinkOutput(linkInfo)).toString();
1057            displayLength += linkInfo.displayLength;
1058            return link;
1059        }
1060    
1061        /**
1062         * Return the type parameters for the given class.
1063         *
1064         * @param linkInfo the information about the link.
1065         * @return the type for the given class.
1066         */
1067        public String getTypeParameterLinks(LinkInfoImpl linkInfo) {
1068            LinkFactoryImpl factory = new LinkFactoryImpl(this);
1069            return ((LinkOutputImpl)
1070                factory.getTypeParameterLinks(linkInfo, false)).toString();
1071        }
1072    
1073        /**
1074         * Print the link to the given class.
1075         */
1076        public void printLink(LinkInfoImpl linkInfo) {
1077            print(getLink(linkInfo));
1078        }
1079    
1080        /*************************************************************
1081         * Return a class cross link to external class documentation.
1082         * The name must be fully qualified to determine which package
1083         * the class is in.  The -link option does not allow users to
1084         * link to external classes in the "default" package.
1085         *
1086         * @param qualifiedClassName the qualified name of the external class.
1087         * @param refMemName the name of the member being referenced.  This should
1088         * be null or empty string if no member is being referenced.
1089         * @param label the label for the external link.
1090         * @param strong true if the link should be strong.
1091         * @param style the style of the link.
1092         * @param code true if the label should be code font.
1093         */
1094        public String getCrossClassLink(String qualifiedClassName, String refMemName,
1095                                        String label, boolean strong, String style,
1096                                        boolean code) {
1097            String className = "",
1098                packageName = qualifiedClassName == null ? "" : qualifiedClassName;
1099            int periodIndex;
1100            while((periodIndex = packageName.lastIndexOf('.')) != -1) {
1101                className = packageName.substring(periodIndex + 1, packageName.length()) +
1102                    (className.length() > 0 ? "." + className : "");
1103                String defaultLabel = code ? getCode() + className + getCodeEnd() : className;
1104                packageName = packageName.substring(0, periodIndex);
1105                if (getCrossPackageLink(packageName) != null) {
1106                    //The package exists in external documentation, so link to the external
1107                    //class (assuming that it exists).  This is definitely a limitation of
1108                    //the -link option.  There are ways to determine if an external package
1109                    //exists, but no way to determine if the external class exists.  We just
1110                    //have to assume that it does.
1111                    return getHyperLink(
1112                        configuration.extern.getExternalLink(packageName, relativePath,
1113                                    className + ".html?is-external=true"),
1114                        refMemName == null ? "" : refMemName,
1115                        label == null || label.length() == 0 ? defaultLabel : label,
1116                        strong, style,
1117                        configuration.getText("doclet.Href_Class_Or_Interface_Title", packageName),
1118                        "");
1119                }
1120            }
1121            return null;
1122        }
1123    
1124        public boolean isClassLinkable(ClassDoc cd) {
1125            if (cd.isIncluded()) {
1126                return configuration.isGeneratedDoc(cd);
1127            }
1128            return configuration.extern.isExternal(cd);
1129        }
1130    
1131        public String getCrossPackageLink(String pkgName) {
1132            return configuration.extern.getExternalLink(pkgName, relativePath,
1133                "package-summary.html?is-external=true");
1134        }
1135    
1136        public void printQualifiedClassLink(int context, ClassDoc cd) {
1137            printLink(new LinkInfoImpl(context, cd,
1138                configuration.getClassName(cd), ""));
1139        }
1140    
1141        /**
1142         * Print Class link, with only class name as the link and prefixing
1143         * plain package name.
1144         */
1145        public void printPreQualifiedClassLink(int context, ClassDoc cd) {
1146            print(getPreQualifiedClassLink(context, cd, false));
1147        }
1148    
1149        /**
1150         * Retrieve the class link with the package portion of the label in
1151         * plain text.  If the qualifier is excluded, it willnot be included in the
1152         * link label.
1153         *
1154         * @param cd the class to link to.
1155         * @param isStrong true if the link should be strong.
1156         * @return the link with the package portion of the label in plain text.
1157         */
1158        public String getPreQualifiedClassLink(int context,
1159                ClassDoc cd, boolean isStrong) {
1160            String classlink = "";
1161            PackageDoc pd = cd.containingPackage();
1162            if(pd != null && ! configuration.shouldExcludeQualifier(pd.name())) {
1163                classlink = getPkgName(cd);
1164            }
1165            classlink += getLink(new LinkInfoImpl(context, cd, cd.name(), isStrong));
1166            return classlink;
1167        }
1168    
1169    
1170        /**
1171         * Print Class link, with only class name as the strong link and prefixing
1172         * plain package name.
1173         */
1174        public void printPreQualifiedStrongClassLink(int context, ClassDoc cd) {
1175            print(getPreQualifiedClassLink(context, cd, true));
1176        }
1177    
1178        public void printText(String key) {
1179            print(configuration.getText(key));
1180        }
1181    
1182        public void printText(String key, String a1) {
1183            print(configuration.getText(key, a1));
1184        }
1185    
1186        public void printText(String key, String a1, String a2) {
1187            print(configuration.getText(key, a1, a2));
1188        }
1189    
1190        public void strongText(String key) {
1191            strong(configuration.getText(key));
1192        }
1193    
1194        public void strongText(String key, String a1) {
1195            strong(configuration.getText(key, a1));
1196        }
1197    
1198        public void strongText(String key, String a1, String a2) {
1199            strong(configuration.getText(key, a1, a2));
1200        }
1201    
1202        /**
1203         * Print the link for the given member.
1204         *
1205         * @param context the id of the context where the link will be printed.
1206         * @param doc the member being linked to.
1207         * @param label the label for the link.
1208         * @param strong true if the link should be strong.
1209         */
1210        public void printDocLink(int context, MemberDoc doc, String label,
1211                boolean strong) {
1212            print(getDocLink(context, doc, label, strong));
1213        }
1214    
1215        /**
1216         * Print the link for the given member.
1217         *
1218         * @param context the id of the context where the link will be printed.
1219         * @param classDoc the classDoc that we should link to.  This is not
1220         *                 necessarily equal to doc.containingClass().  We may be
1221         *                 inheriting comments.
1222         * @param doc the member being linked to.
1223         * @param label the label for the link.
1224         * @param strong true if the link should be strong.
1225         */
1226        public void printDocLink(int context, ClassDoc classDoc, MemberDoc doc,
1227                String label, boolean strong) {
1228            print(getDocLink(context, classDoc, doc, label, strong));
1229        }
1230    
1231        /**
1232         * Return the link for the given member.
1233         *
1234         * @param context the id of the context where the link will be printed.
1235         * @param doc the member being linked to.
1236         * @param label the label for the link.
1237         * @param strong true if the link should be strong.
1238         * @return the link for the given member.
1239         */
1240        public String getDocLink(int context, MemberDoc doc, String label,
1241                    boolean strong) {
1242            return getDocLink(context, doc.containingClass(), doc, label, strong);
1243        }
1244    
1245        /**
1246         * Return the link for the given member.
1247         *
1248         * @param context the id of the context where the link will be printed.
1249         * @param classDoc the classDoc that we should link to.  This is not
1250         *                 necessarily equal to doc.containingClass().  We may be
1251         *                 inheriting comments.
1252         * @param doc the member being linked to.
1253         * @param label the label for the link.
1254         * @param strong true if the link should be strong.
1255         * @return the link for the given member.
1256         */
1257        public String getDocLink(int context, ClassDoc classDoc, MemberDoc doc,
1258            String label, boolean strong) {
1259            if (! (doc.isIncluded() ||
1260                Util.isLinkable(classDoc, configuration()))) {
1261                return label;
1262            } else if (doc instanceof ExecutableMemberDoc) {
1263                ExecutableMemberDoc emd = (ExecutableMemberDoc)doc;
1264                return getLink(new LinkInfoImpl(context, classDoc,
1265                    getAnchor(emd), label, strong));
1266            } else if (doc instanceof MemberDoc) {
1267                return getLink(new LinkInfoImpl(context, classDoc,
1268                    doc.name(), label, strong));
1269            } else {
1270                return label;
1271            }
1272        }
1273    
1274        public void anchor(ExecutableMemberDoc emd) {
1275            anchor(getAnchor(emd));
1276        }
1277    
1278        public String getAnchor(ExecutableMemberDoc emd) {
1279            StringBuilder signature = new StringBuilder(emd.signature());
1280            StringBuilder signatureParsed = new StringBuilder();
1281            int counter = 0;
1282            for (int i = 0; i < signature.length(); i++) {
1283                char c = signature.charAt(i);
1284                if (c == '<') {
1285                    counter++;
1286                } else if (c == '>') {
1287                    counter--;
1288                } else if (counter == 0) {
1289                    signatureParsed.append(c);
1290                }
1291            }
1292            return emd.name() + signatureParsed.toString();
1293        }
1294    
1295        public String seeTagToString(SeeTag see) {
1296            String tagName = see.name();
1297            if (! (tagName.startsWith("@link") || tagName.equals("@see"))) {
1298                return "";
1299            }
1300            StringBuffer result = new StringBuffer();
1301            boolean isplaintext = tagName.toLowerCase().equals("@linkplain");
1302            String label = see.label();
1303            label = (label.length() > 0)?
1304                ((isplaintext) ? label :
1305                     getCode() + label + getCodeEnd()):"";
1306            String seetext = replaceDocRootDir(see.text());
1307    
1308            //Check if @see is an href or "string"
1309            if (seetext.startsWith("<") || seetext.startsWith("\"")) {
1310                result.append(seetext);
1311                return result.toString();
1312            }
1313    
1314            //The text from the @see tag.  We will output this text when a label is not specified.
1315            String text = (isplaintext) ? seetext : getCode() + seetext + getCodeEnd();
1316    
1317            ClassDoc refClass = see.referencedClass();
1318            String refClassName = see.referencedClassName();
1319            MemberDoc refMem = see.referencedMember();
1320            String refMemName = see.referencedMemberName();
1321            if (refClass == null) {
1322                //@see is not referencing an included class
1323                PackageDoc refPackage = see.referencedPackage();
1324                if (refPackage != null && refPackage.isIncluded()) {
1325                    //@see is referencing an included package
1326                    String packageName = isplaintext ? refPackage.name() :
1327                        getCode() + refPackage.name() + getCodeEnd();
1328                    result.append(getPackageLink(refPackage,
1329                        label.length() == 0 ? packageName : label, false));
1330                } else {
1331                    //@see is not referencing an included class or package.  Check for cross links.
1332                    String classCrossLink, packageCrossLink = getCrossPackageLink(refClassName);
1333                    if (packageCrossLink != null) {
1334                        //Package cross link found
1335                        result.append(getHyperLink(packageCrossLink, "",
1336                            (label.length() == 0)? text : label, false));
1337                    } else if ((classCrossLink = getCrossClassLink(refClassName,
1338                            refMemName, label, false, "", ! isplaintext)) != null) {
1339                        //Class cross link found (possiblly to a member in the class)
1340                        result.append(classCrossLink);
1341                    } else {
1342                        //No cross link found so print warning
1343                        configuration.getDocletSpecificMsg().warning(see.position(), "doclet.see.class_or_package_not_found",
1344                                tagName, seetext);
1345                        result.append((label.length() == 0)? text: label);
1346                    }
1347                }
1348            } else if (refMemName == null) {
1349                // Must be a class reference since refClass is not null and refMemName is null.
1350                if (label.length() == 0) {
1351                    label = (isplaintext) ? refClass.name() : getCode() + refClass.name() + getCodeEnd();
1352                    result.append(getLink(new LinkInfoImpl(refClass, label)));
1353                } else {
1354                    result.append(getLink(new LinkInfoImpl(refClass, label)));
1355                }
1356            } else if (refMem == null) {
1357                // Must be a member reference since refClass is not null and refMemName is not null.
1358                // However, refMem is null, so this referenced member does not exist.
1359                result.append((label.length() == 0)? text: label);
1360            } else {
1361                // Must be a member reference since refClass is not null and refMemName is not null.
1362                // refMem is not null, so this @see tag must be referencing a valid member.
1363                ClassDoc containing = refMem.containingClass();
1364                if (see.text().trim().startsWith("#") &&
1365                    ! (containing.isPublic() ||
1366                    Util.isLinkable(containing, configuration()))) {
1367                    // Since the link is relative and the holder is not even being
1368                    // documented, this must be an inherited link.  Redirect it.
1369                    // The current class either overrides the referenced member or
1370                    // inherits it automatically.
1371                    containing = ((ClassWriterImpl) this).getClassDoc();
1372                }
1373                if (configuration.currentcd != containing) {
1374                    refMemName = containing.name() + "." + refMemName;
1375                }
1376                if (refMem instanceof ExecutableMemberDoc) {
1377                    if (refMemName.indexOf('(') < 0) {
1378                        refMemName += ((ExecutableMemberDoc)refMem).signature();
1379                    }
1380                }
1381                text = (isplaintext) ?
1382                    refMemName : getCode() + refMemName + getCodeEnd();
1383    
1384                result.append(getDocLink(LinkInfoImpl.CONTEXT_SEE_TAG, containing,
1385                    refMem, (label.length() == 0)? text: label, false));
1386            }
1387            return result.toString();
1388        }
1389    
1390        public void printInlineComment(Doc doc, Tag tag) {
1391            printCommentTags(doc, tag.inlineTags(), false, false);
1392        }
1393    
1394        public void printInlineDeprecatedComment(Doc doc, Tag tag) {
1395            printCommentTags(doc, tag.inlineTags(), true, false);
1396        }
1397    
1398        public void printSummaryComment(Doc doc) {
1399            printSummaryComment(doc, doc.firstSentenceTags());
1400        }
1401    
1402        public void printSummaryComment(Doc doc, Tag[] firstSentenceTags) {
1403            printCommentTags(doc, firstSentenceTags, false, true);
1404        }
1405    
1406        public void printSummaryDeprecatedComment(Doc doc) {
1407            printCommentTags(doc, doc.firstSentenceTags(), true, true);
1408        }
1409    
1410        public void printSummaryDeprecatedComment(Doc doc, Tag tag) {
1411            printCommentTags(doc, tag.firstSentenceTags(), true, true);
1412        }
1413    
1414        public void printInlineComment(Doc doc) {
1415            printCommentTags(doc, doc.inlineTags(), false, false);
1416            p();
1417        }
1418    
1419        public void printInlineDeprecatedComment(Doc doc) {
1420            printCommentTags(doc, doc.inlineTags(), true, false);
1421        }
1422    
1423        private void printCommentTags(Doc doc, Tag[] tags, boolean depr, boolean first) {
1424            if(configuration.nocomment){
1425                return;
1426            }
1427            if (depr) {
1428                italic();
1429            }
1430            String result = commentTagsToString(null, doc, tags, first);
1431            print(result);
1432            if (depr) {
1433                italicEnd();
1434            }
1435            if (tags.length == 0) {
1436                space();
1437            }
1438        }
1439    
1440        /**
1441         * Converts inline tags and text to text strings, expanding the
1442         * inline tags along the way.  Called wherever text can contain
1443         * an inline tag, such as in comments or in free-form text arguments
1444         * to non-inline tags.
1445         *
1446         * @param holderTag    specific tag where comment resides
1447         * @param doc    specific doc where comment resides
1448         * @param tags   array of text tags and inline tags (often alternating)
1449         *               present in the text of interest for this doc
1450         * @param isFirstSentence  true if text is first sentence
1451         */
1452        public String commentTagsToString(Tag holderTag, Doc doc, Tag[] tags,
1453                boolean isFirstSentence) {
1454            StringBuffer result = new StringBuffer();
1455            // Array of all possible inline tags for this javadoc run
1456            configuration.tagletManager.checkTags(doc, tags, true);
1457            for (int i = 0; i < tags.length; i++) {
1458                Tag tagelem = tags[i];
1459                String tagName = tagelem.name();
1460                if (tagelem instanceof SeeTag) {
1461                    result.append(seeTagToString((SeeTag)tagelem));
1462                } else if (! tagName.equals("Text")) {
1463                    int originalLength = result.length();
1464                    TagletOutput output = TagletWriter.getInlineTagOuput(
1465                        configuration.tagletManager, holderTag,
1466                        tagelem, getTagletWriterInstance(isFirstSentence));
1467                    result.append(output == null ? "" : output.toString());
1468                    if (originalLength == 0 && isFirstSentence && tagelem.name().equals("@inheritDoc") && result.length() > 0) {
1469                        break;
1470                    } else {
1471                            continue;
1472                    }
1473                } else {
1474                    //This is just a regular text tag.  The text may contain html links (<a>)
1475                    //or inline tag {@docRoot}, which will be handled as special cases.
1476                    String text = redirectRelativeLinks(tagelem.holder(), tagelem.text());
1477    
1478                    // Replace @docRoot only if not represented by an instance of DocRootTaglet,
1479                    // that is, only if it was not present in a source file doc comment.
1480                    // This happens when inserted by the doclet (a few lines
1481                    // above in this method).  [It might also happen when passed in on the command
1482                    // line as a text argument to an option (like -header).]
1483                    text = replaceDocRootDir(text);
1484                    if (isFirstSentence) {
1485                        text = removeNonInlineHtmlTags(text);
1486                    }
1487                    StringTokenizer lines = new StringTokenizer(text, "\r\n", true);
1488                    StringBuffer textBuff = new StringBuffer();
1489                    while (lines.hasMoreTokens()) {
1490                        StringBuffer line = new StringBuffer(lines.nextToken());
1491                        Util.replaceTabs(configuration.sourcetab, line);
1492                        textBuff.append(line.toString());
1493                    }
1494                    result.append(textBuff);
1495                }
1496            }
1497            return result.toString();
1498        }
1499    
1500        /**
1501         * Return true if relative links should not be redirected.
1502         *
1503         * @return Return true if a relative link should not be redirected.
1504         */
1505        private boolean shouldNotRedirectRelativeLinks() {
1506            return  this instanceof AnnotationTypeWriter ||
1507                    this instanceof ClassWriter ||
1508                    this instanceof PackageSummaryWriter;
1509        }
1510    
1511        /**
1512         * Suppose a piece of documentation has a relative link.  When you copy
1513         * that documetation to another place such as the index or class-use page,
1514         * that relative link will no longer work.  We should redirect those links
1515         * so that they will work again.
1516         * <p>
1517         * Here is the algorithm used to fix the link:
1518         * <p>
1519         * &lt;relative link&gt; => docRoot + &lt;relative path to file&gt; + &lt;relative link&gt;
1520         * <p>
1521         * For example, suppose com.sun.javadoc.RootDoc has this link:
1522         * &lt;a href="package-summary.html"&gt;The package Page&lt;/a&gt;
1523         * <p>
1524         * If this link appeared in the index, we would redirect
1525         * the link like this:
1526         *
1527         * &lt;a href="./com/sun/javadoc/package-summary.html"&gt;The package Page&lt;/a&gt;
1528         *
1529         * @param doc the Doc object whose documentation is being written.
1530         * @param text the text being written.
1531         *
1532         * @return the text, with all the relative links redirected to work.
1533         */
1534        private String redirectRelativeLinks(Doc doc, String text) {
1535            if (doc == null || shouldNotRedirectRelativeLinks()) {
1536                return text;
1537            }
1538    
1539            String redirectPathFromRoot;
1540            if (doc instanceof ClassDoc) {
1541                redirectPathFromRoot = DirectoryManager.getDirectoryPath(((ClassDoc) doc).containingPackage());
1542            } else if (doc instanceof MemberDoc) {
1543                redirectPathFromRoot = DirectoryManager.getDirectoryPath(((MemberDoc) doc).containingPackage());
1544            } else if (doc instanceof PackageDoc) {
1545                redirectPathFromRoot = DirectoryManager.getDirectoryPath((PackageDoc) doc);
1546            } else {
1547                return text;
1548            }
1549    
1550            if (! redirectPathFromRoot.endsWith(DirectoryManager.URL_FILE_SEPERATOR)) {
1551                redirectPathFromRoot += DirectoryManager.URL_FILE_SEPERATOR;
1552            }
1553    
1554            //Redirect all relative links.
1555            int end, begin = text.toLowerCase().indexOf("<a");
1556            if(begin >= 0){
1557                StringBuffer textBuff = new StringBuffer(text);
1558    
1559                while(begin >=0){
1560                    if (textBuff.length() > begin + 2 && ! Character.isWhitespace(textBuff.charAt(begin+2))) {
1561                        begin = textBuff.toString().toLowerCase().indexOf("<a", begin + 1);
1562                        continue;
1563                    }
1564    
1565                    begin = textBuff.indexOf("=", begin) + 1;
1566                    end = textBuff.indexOf(">", begin +1);
1567                    if(begin == 0){
1568                        //Link has no equal symbol.
1569                        configuration.root.printWarning(
1570                            doc.position(),
1571                            configuration.getText("doclet.malformed_html_link_tag", text));
1572                        break;
1573                    }
1574                    if (end == -1) {
1575                        //Break without warning.  This <a> tag is not necessarily malformed.  The text
1576                        //might be missing '>' character because the href has an inline tag.
1577                        break;
1578                    }
1579                    if(textBuff.substring(begin, end).indexOf("\"") != -1){
1580                        begin = textBuff.indexOf("\"", begin) + 1;
1581                        end = textBuff.indexOf("\"", begin +1);
1582                        if(begin == 0 || end == -1){
1583                            //Link is missing a quote.
1584                            break;
1585                        }
1586                    }
1587                    String relativeLink = textBuff.substring(begin, end);
1588                    if(!(relativeLink.toLowerCase().startsWith("mailto:") ||
1589                         relativeLink.toLowerCase().startsWith("http:") ||
1590                         relativeLink.toLowerCase().startsWith("https:") ||
1591                         relativeLink.toLowerCase().startsWith("file:"))){
1592                         relativeLink = "{@"+(new DocRootTaglet()).getName() + "}"
1593                            + redirectPathFromRoot
1594                            + relativeLink;
1595                        textBuff.replace(begin, end, relativeLink);
1596                    }
1597                    begin = textBuff.toString().toLowerCase().indexOf("<a", begin + 1);
1598                }
1599                return textBuff.toString();
1600            }
1601            return text;
1602        }
1603    
1604        public String removeNonInlineHtmlTags(String text) {
1605            if (text.indexOf('<') < 0) {
1606                return text;
1607            }
1608            String noninlinetags[] = { "<ul>", "</ul>", "<ol>", "</ol>",
1609                    "<dl>", "</dl>", "<table>", "</table>",
1610                    "<tr>", "</tr>", "<td>", "</td>",
1611                    "<th>", "</th>", "<p>", "</p>",
1612                    "<li>", "</li>", "<dd>", "</dd>",
1613                    "<dir>", "</dir>", "<dt>", "</dt>",
1614                    "<h1>", "</h1>", "<h2>", "</h2>",
1615                    "<h3>", "</h3>", "<h4>", "</h4>",
1616                    "<h5>", "</h5>", "<h6>", "</h6>",
1617                    "<pre>", "</pre>", "<menu>", "</menu>",
1618                    "<listing>", "</listing>", "<hr>",
1619                    "<blockquote>", "</blockquote>",
1620                    "<center>", "</center>",
1621                    "<UL>", "</UL>", "<OL>", "</OL>",
1622                    "<DL>", "</DL>", "<TABLE>", "</TABLE>",
1623                    "<TR>", "</TR>", "<TD>", "</TD>",
1624                    "<TH>", "</TH>", "<P>", "</P>",
1625                    "<LI>", "</LI>", "<DD>", "</DD>",
1626                    "<DIR>", "</DIR>", "<DT>", "</DT>",
1627                    "<H1>", "</H1>", "<H2>", "</H2>",
1628                    "<H3>", "</H3>", "<H4>", "</H4>",
1629                    "<H5>", "</H5>", "<H6>", "</H6>",
1630                    "<PRE>", "</PRE>", "<MENU>", "</MENU>",
1631                    "<LISTING>", "</LISTING>", "<HR>",
1632                    "<BLOCKQUOTE>", "</BLOCKQUOTE>",
1633                    "<CENTER>", "</CENTER>"
1634            };
1635            for (int i = 0; i < noninlinetags.length; i++) {
1636                text = replace(text, noninlinetags[i], "");
1637            }
1638            return text;
1639        }
1640    
1641        public String replace(String text, String tobe, String by) {
1642            while (true) {
1643                int startindex = text.indexOf(tobe);
1644                if (startindex < 0) {
1645                    return text;
1646                }
1647                int endindex = startindex + tobe.length();
1648                StringBuffer replaced = new StringBuffer();
1649                if (startindex > 0) {
1650                    replaced.append(text.substring(0, startindex));
1651                }
1652                replaced.append(by);
1653                if (text.length() > endindex) {
1654                    replaced.append(text.substring(endindex));
1655                }
1656                text = replaced.toString();
1657            }
1658        }
1659    
1660        public void printStyleSheetProperties() {
1661            String filename = configuration.stylesheetfile;
1662            if (filename.length() > 0) {
1663                File stylefile = new File(filename);
1664                String parent = stylefile.getParent();
1665                filename = (parent == null)?
1666                    filename:
1667                    filename.substring(parent.length() + 1);
1668            } else {
1669                filename = "stylesheet.css";
1670            }
1671            filename = relativePath + filename;
1672            link("REL =\"stylesheet\" TYPE=\"text/css\" HREF=\"" +
1673                     filename + "\" " + "TITLE=\"Style\"");
1674        }
1675    
1676        /**
1677         * According to the Java Language Specifications, all the outer classes
1678         * and static nested classes are core classes.
1679         */
1680        public boolean isCoreClass(ClassDoc cd) {
1681            return cd.containingClass() == null || cd.isStatic();
1682        }
1683    
1684        /**
1685         * Write the annotatation types for the given packageDoc.
1686         *
1687         * @param packageDoc the package to write annotations for.
1688         */
1689        public void writeAnnotationInfo(PackageDoc packageDoc) {
1690            writeAnnotationInfo(packageDoc, packageDoc.annotations());
1691        }
1692    
1693        /**
1694         * Write the annotatation types for the given doc.
1695         *
1696         * @param doc the doc to write annotations for.
1697         */
1698        public void writeAnnotationInfo(ProgramElementDoc doc) {
1699            writeAnnotationInfo(doc, doc.annotations());
1700        }
1701    
1702        /**
1703         * Write the annotatation types for the given doc and parameter.
1704         *
1705         * @param indent the number of spaced to indent the parameters.
1706         * @param doc the doc to write annotations for.
1707         * @param param the parameter to write annotations for.
1708         */
1709        public boolean writeAnnotationInfo(int indent, Doc doc, Parameter param) {
1710            return writeAnnotationInfo(indent, doc, param.annotations(), false);
1711        }
1712    
1713        /**
1714         * Write the annotatation types for the given doc.
1715         *
1716         * @param doc the doc to write annotations for.
1717         * @param descList the array of {@link AnnotationDesc}.
1718         */
1719        private void writeAnnotationInfo(Doc doc, AnnotationDesc[] descList) {
1720            writeAnnotationInfo(0, doc, descList, true);
1721        }
1722    
1723        /**
1724         * Write the annotatation types for the given doc.
1725         *
1726         * @param indent the number of extra spaces to indent the annotations.
1727         * @param doc the doc to write annotations for.
1728         * @param descList the array of {@link AnnotationDesc}.
1729         */
1730        private boolean writeAnnotationInfo(int indent, Doc doc, AnnotationDesc[] descList, boolean lineBreak) {
1731            List<String> annotations = getAnnotations(indent, descList, lineBreak);
1732            if (annotations.size() == 0) {
1733                return false;
1734            }
1735            fontNoNewLine("-1");
1736            for (Iterator<String> iter = annotations.iterator(); iter.hasNext();) {
1737                print(iter.next());
1738            }
1739            fontEnd();
1740            return true;
1741        }
1742    
1743        /**
1744         * Return the string representations of the annotation types for
1745         * the given doc.
1746         *
1747         * @param indent the number of extra spaces to indent the annotations.
1748         * @param descList the array of {@link AnnotationDesc}.
1749         * @param linkBreak if true, add new line between each member value.
1750         * @return an array of strings representing the annotations being
1751         *         documented.
1752         */
1753        private List<String> getAnnotations(int indent, AnnotationDesc[] descList, boolean linkBreak) {
1754            List<String> results = new ArrayList<String>();
1755            StringBuffer annotation;
1756            for (int i = 0; i < descList.length; i++) {
1757                AnnotationTypeDoc annotationDoc = descList[i].annotationType();
1758                if (! Util.isDocumentedAnnotation(annotationDoc)){
1759                    continue;
1760                }
1761                annotation = new StringBuffer();
1762                LinkInfoImpl linkInfo = new LinkInfoImpl(
1763                    LinkInfoImpl.CONTEXT_ANNOTATION, annotationDoc);
1764                linkInfo.label = "@" + annotationDoc.name();
1765                annotation.append(getLink(linkInfo));
1766                AnnotationDesc.ElementValuePair[] pairs = descList[i].elementValues();
1767                if (pairs.length > 0) {
1768                    annotation.append('(');
1769                    for (int j = 0; j < pairs.length; j++) {
1770                        if (j > 0) {
1771                            annotation.append(",");
1772                            if (linkBreak) {
1773                                annotation.append(DocletConstants.NL);
1774                                int spaces = annotationDoc.name().length() + 2;
1775                                for (int k = 0; k < (spaces + indent); k++) {
1776                                    annotation.append(' ');
1777                                }
1778                            }
1779                        }
1780                        annotation.append(getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION,
1781                            pairs[j].element(), pairs[j].element().name(), false));
1782                        annotation.append('=');
1783                        AnnotationValue annotationValue = pairs[j].value();
1784                        List<AnnotationValue> annotationTypeValues = new ArrayList<AnnotationValue>();
1785                        if (annotationValue.value() instanceof AnnotationValue[]) {
1786                            AnnotationValue[] annotationArray =
1787                                (AnnotationValue[]) annotationValue.value();
1788                            for (int k = 0; k < annotationArray.length; k++) {
1789                                annotationTypeValues.add(annotationArray[k]);
1790                            }
1791                        } else {
1792                            annotationTypeValues.add(annotationValue);
1793                        }
1794                        annotation.append(annotationTypeValues.size() == 1 ? "" : "{");
1795                        for (Iterator<AnnotationValue> iter = annotationTypeValues.iterator(); iter.hasNext(); ) {
1796                            annotation.append(annotationValueToString(iter.next()));
1797                            annotation.append(iter.hasNext() ? "," : "");
1798                        }
1799                        annotation.append(annotationTypeValues.size() == 1 ? "" : "}");
1800                    }
1801                    annotation.append(")");
1802                }
1803                annotation.append(linkBreak ? DocletConstants.NL : "");
1804                results.add(annotation.toString());
1805            }
1806            return results;
1807        }
1808    
1809        private String annotationValueToString(AnnotationValue annotationValue) {
1810            if (annotationValue.value() instanceof Type) {
1811                Type type = (Type) annotationValue.value();
1812                if (type.asClassDoc() != null) {
1813                    LinkInfoImpl linkInfo = new LinkInfoImpl(
1814                        LinkInfoImpl.CONTEXT_ANNOTATION, type);
1815                        linkInfo.label = (type.asClassDoc().isIncluded() ?
1816                            type.typeName() :
1817                            type.qualifiedTypeName()) + type.dimension() + ".class";
1818                    return getLink(linkInfo);
1819                } else {
1820                    return type.typeName() + type.dimension() + ".class";
1821                }
1822            } else if (annotationValue.value() instanceof AnnotationDesc) {
1823                List<String> list = getAnnotations(0,
1824                    new AnnotationDesc[]{(AnnotationDesc) annotationValue.value()},
1825                        false);
1826                StringBuffer buf = new StringBuffer();
1827                for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
1828                    buf.append(iter.next());
1829                }
1830                return buf.toString();
1831            } else if (annotationValue.value() instanceof MemberDoc) {
1832                return getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION,
1833                    (MemberDoc) annotationValue.value(),
1834                    ((MemberDoc) annotationValue.value()).name(), false);
1835             } else {
1836                return annotationValue.toString();
1837             }
1838        }
1839    
1840        /**
1841         * Return the configuation for this doclet.
1842         *
1843         * @return the configuration for this doclet.
1844         */
1845        public Configuration configuration() {
1846            return configuration;
1847        }
1848    }