Package org.openquark.cal.caldoc

Source Code of org.openquark.cal.caldoc.CALDocToHTMLUtilities$TextBlockHTMLGenerator

/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Business Objects nor the names of its contributors
*       may be used to endorse or promote products derived from this software
*       without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/


/*
* CALDocToHTMLUtilities.java
* Creation date: Sep 28, 2005.
* By: Joseph Wong
*/
package org.openquark.cal.caldoc;

import java.text.BreakIterator;
import java.util.Locale;

import javax.swing.text.html.HTML;

import org.openquark.cal.caldoc.HTMLBuilder.AttributeList;
import org.openquark.cal.compiler.CALDocComment;
import org.openquark.cal.compiler.CALDocCommentTextBlockTraverser;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.CALDocComment.CodeSegment;
import org.openquark.cal.compiler.CALDocComment.DataConsLinkSegment;
import org.openquark.cal.compiler.CALDocComment.EmphasizedSegment;
import org.openquark.cal.compiler.CALDocComment.FunctionOrClassMethodLinkSegment;
import org.openquark.cal.compiler.CALDocComment.ListItem;
import org.openquark.cal.compiler.CALDocComment.ListParagraph;
import org.openquark.cal.compiler.CALDocComment.ModuleLinkSegment;
import org.openquark.cal.compiler.CALDocComment.ModuleReference;
import org.openquark.cal.compiler.CALDocComment.PlainTextSegment;
import org.openquark.cal.compiler.CALDocComment.ScopedEntityReference;
import org.openquark.cal.compiler.CALDocComment.StronglyEmphasizedSegment;
import org.openquark.cal.compiler.CALDocComment.SubscriptSegment;
import org.openquark.cal.compiler.CALDocComment.SuperscriptSegment;
import org.openquark.cal.compiler.CALDocComment.TextBlock;
import org.openquark.cal.compiler.CALDocComment.TextParagraph;
import org.openquark.cal.compiler.CALDocComment.TypeClassLinkSegment;
import org.openquark.cal.compiler.CALDocComment.TypeConsLinkSegment;
import org.openquark.cal.compiler.CALDocComment.URLSegment;
import org.openquark.cal.services.CALFeatureName;
import org.openquark.cal.services.LocaleUtilities;


/**
* This is a utility class containing helper methods for converting CALDoc into
* properly formatted HTML.
*
* @author Joseph Wong
*/
public final class CALDocToHTMLUtilities {
   
    /**
     * This class encapsulates a piece of content that is convertible to HTML. It is effectively used as a
     * lazily-evaluated thunk that is executed to generate the HTML when needed (and at the correct position!)
     *
     * @author Joseph Wong
     */
    static abstract class ContentConvertibleToHTML {
        /** @return true if there is no content; false otherwise. */
        abstract boolean isEmpty();
        /**
         * Generate the content as HTML.
         * @param builder the HTMLBuilder to use for generating the HTML.
         * @param referenceGenerator the reference generator to use for generating cross references.
         */
        abstract void generateHTML(HTMLBuilder builder, CrossReferenceHTMLGenerator referenceGenerator);
    }

    /**
     * A subclass of ContentConvertibleToHTML for representing simple string content.
     *
     * @author Joseph Wong
     */
    static final class SimpleStringContent extends ContentConvertibleToHTML {
        /** The string content. */
        private final String content;
       
        /** @param content the string content. Can be null.*/
        SimpleStringContent(final String content) {
            this.content = (content != null) ? content.trim() : null; // trim the content of its leading and trailing whitespace
        }
       
        /** {@inheritDoc} */
        @Override
        final boolean isEmpty() {
            return content == null || content.length() == 0;
        }
       
        /** {@inheritDoc} */
        @Override
        final void generateHTML(final HTMLBuilder currentPage, final CrossReferenceHTMLGenerator referenceGenerator) {
            currentPage.addText(content);
        }
    }

    /**
     * A subclass of ContentConvertibleToHTML for representing a single CALDoc paragraph.
     *
     * @author Joseph Wong
     */
    static final class SingleParagraphContent extends ContentConvertibleToHTML {
        /** The CALDoc paragraph. */
        private final CALDocComment.Paragraph paragraph;
       
        /** The style class for code blocks. Can be null. */
        private final StyleClass codeStyleClass;
       
        /** The HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE. */
        private final HTML.Tag codeFormattingTag;
       
        /**
         * @param paragraph the CALDoc paragraph.
         * @param codeStyleClass the style class to use for code blocks. Can be null.
         * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
         */
        SingleParagraphContent(final CALDocComment.Paragraph paragraph, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
            this.paragraph = paragraph;
            this.codeStyleClass = codeStyleClass;
            this.codeFormattingTag = codeFormattingTag;
        }
       
        /** {@inheritDoc} */
        @Override
        final boolean isEmpty() {
            return paragraph == null;
        }
       
        /** {@inheritDoc} */
        @Override
        final void generateHTML(final HTMLBuilder currentPage, final CrossReferenceHTMLGenerator referenceGenerator) {
            generateHTMLForCALDocParagraph(paragraph, currentPage, referenceGenerator, codeStyleClass, codeFormattingTag);
        }
    }
   
    /**
     * A subclass of ContentConvertibleToHTML for representing the initial segments of a text paragraph.
     *
     * @author Joseph Wong
     */
    static final class TextParagraphInitialSegments extends ContentConvertibleToHTML {
        /** The CALDoc text paragraph. */
        private final CALDocComment.TextParagraph paragraph;
       
        /** The number of complete initial segments to include. */
        private final int nCompleteSegments;
       
        /** The plain text to form the end of the generated text. */
        private final String endPlainText;
       
        /** The style class for code blocks. Can be null. */
        private final StyleClass codeStyleClass;
       
        /** The HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE. */
        private final HTML.Tag codeFormattingTag;
       
        /**
         * @param paragraph the CALDoc text paragraph.
         * @param nCompleteSegments the number of complete initial segments to include.
         * @param endPlainText the plain text to form the end of the generated text.
         * @param codeStyleClass the style class to use for code blocks. Can be null.
         * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
         */
        TextParagraphInitialSegments(final CALDocComment.TextParagraph paragraph, final int nCompleteSegments, final String endPlainText, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
            this.paragraph = paragraph;
            this.nCompleteSegments = Math.min(paragraph.getNSegments(), nCompleteSegments);
            this.endPlainText = endPlainText;
            this.codeStyleClass = codeStyleClass;
            this.codeFormattingTag = codeFormattingTag;
        }
       
        /** {@inheritDoc} */
        @Override
        final boolean isEmpty() {
            return paragraph == null || paragraph.getNSegments() == 0 || (nCompleteSegments == 0 && endPlainText.length() == 0);
        }
       
        /** {@inheritDoc} */
        @Override
        final void generateHTML(final HTMLBuilder currentPage, final CrossReferenceHTMLGenerator referenceGenerator) {
            for (int i = 0; i < nCompleteSegments; i++) {
                generateHTMLForCALDocSegment(paragraph.getNthSegment(i), currentPage, referenceGenerator, codeStyleClass, codeFormattingTag);   
            }
            currentPage.addText(htmlEscape(endPlainText));
        }
    }

    /**
     * A subclass of ContentConvertibleToHTML for representing a single CALDoc text block.
     *
     * @author Joseph Wong
     */
    static final class SingleTextBlockContent extends ContentConvertibleToHTML {
        /** The CALDoc text block. */
        private final CALDocComment.TextBlock textBlock;
       
        /** The style class for code blocks. Can be null. */
        private final StyleClass codeStyleClass;
       
        /** The HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE. */
        private final HTML.Tag codeFormattingTag;
       
        /**
         * @param textBlock the CALDoc text block.
         * @param codeStyleClass the style class to use for code blocks. Can be null.
         * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
         */
        SingleTextBlockContent(final CALDocComment.TextBlock textBlock, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
            this.textBlock = textBlock;
            this.codeStyleClass = codeStyleClass;
            this.codeFormattingTag = codeFormattingTag;
        }
       
        /** {@inheritDoc} */
        @Override
        final boolean isEmpty() {
            return textBlock == null || textBlock.getNParagraphs() == 0;
        }
       
        /** {@inheritDoc} */
        @Override
        final void generateHTML(final HTMLBuilder currentPage, final CrossReferenceHTMLGenerator referenceGenerator) {
            generateHTMLForCALDocTextBlock(textBlock, currentPage, referenceGenerator, codeStyleClass, codeFormattingTag);
        }
    }

    /**
     * Abstract base class for a generator capable of generating appropriate HTML for cross-references.
     *
     * Generators that produce the HTML as strings should use {@link CALDocToHTMLUtilities.CrossReferenceHTMLStringGenerator}
     * as the base class.
     *
     * @author Joseph Wong
     * @see CALDocToHTMLUtilities.CrossReferenceHTMLStringGenerator
     */
    public static abstract class CrossReferenceHTMLGenerator {
       
        /** Package-scoped constructor. */
        CrossReferenceHTMLGenerator() {}
       
        /**
         * Generates HTML for a module cross-reference.
         * @param builder the HTMLBuilder to use for generating the cross-reference.
         * @param reference the module cross-reference.
         */
        abstract void generateModuleReference(HTMLBuilder builder, ModuleReference reference);
       
        /**
         * Generates HTML for a type constructor cross-reference.
         * @param builder the HTMLBuilder to use for generating the cross-reference.
         * @param reference the type constructor cross-reference.
         */
        abstract void generateTypeConsReference(HTMLBuilder builder, ScopedEntityReference reference);
       
        /**
         * Generates HTML for a data constructor cross-reference.
         * @param builder the HTMLBuilder to use for generating the cross-reference.
         * @param reference the data constructor cross-reference.
         */
        abstract void generateDataConsReference(HTMLBuilder builder, ScopedEntityReference reference);
       
        /**
         * Generates HTML for a function or class method cross-reference.
         * @param builder the HTMLBuilder to use for generating the cross-reference.
         * @param reference the function or class method cross-reference.
         */
        abstract void generateFunctionOrClassMethodReference(HTMLBuilder builder, ScopedEntityReference reference);
       
        /**
         * Generates HTML for a type class cross-reference.
         * @param builder the HTMLBuilder to use for generating the cross-reference.
         * @param reference the type class cross-reference.
         */
        abstract void generateTypeClassReference(HTMLBuilder builder, ScopedEntityReference reference);
    }
   
    /**
     * Abstract base class for a generator capable of generating appropriate HTML text for cross-references.
     *
     * @author Joseph Wong
     */
    public static abstract class CrossReferenceHTMLStringGenerator extends CrossReferenceHTMLGenerator {
       
        /**
         * {@inheritDoc}
         */
        @Override
        final void generateModuleReference(final HTMLBuilder builder, final ModuleReference reference) {
            builder.addText(getModuleReferenceHTML(reference));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        final void generateTypeConsReference(final HTMLBuilder builder, final ScopedEntityReference reference) {
            builder.addText(getTypeConsReferenceHTML(reference));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        final void generateDataConsReference(final HTMLBuilder builder, final ScopedEntityReference reference) {
            builder.addText(getDataConsReferenceHTML(reference));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        final void generateFunctionOrClassMethodReference(final HTMLBuilder builder, final ScopedEntityReference reference) {
            builder.addText(getFunctionOrClassMethodReferenceHTML(reference));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        final void generateTypeClassReference(final HTMLBuilder builder, final ScopedEntityReference reference) {
            builder.addText(getTypeClassReferenceHTML(reference));
        }

        /**
         * Generates HTML for a module cross-reference.
         * @param reference the module cross-reference.
         * @return the appropriate HTML text.
         */
        public abstract String getModuleReferenceHTML(ModuleReference reference);

        /**
         * Generates HTML for a type constructor cross-reference.
         * @param reference the type constructor cross-reference.
         * @return the appropriate HTML text.
         */
        public abstract String getTypeConsReferenceHTML(ScopedEntityReference reference);

        /**
         * Generates HTML for a data constructor cross-reference.
         * @param reference the data constructor cross-reference.
         * @return the appropriate HTML text.
         */
        public abstract String getDataConsReferenceHTML(ScopedEntityReference reference);

        /**
         * Generates HTML for a function or class method cross-reference.
         * @param reference the function or class method cross-reference.
         * @return the appropriate HTML text.
         */
        public abstract String getFunctionOrClassMethodReferenceHTML(ScopedEntityReference reference);

        /**
         * Generates HTML for a type class cross-reference.
         * @param reference the type class cross-reference.
         * @return the appropriate HTML text.
         */
        public abstract String getTypeClassReferenceHTML(ScopedEntityReference reference);
    }
   
    /**
     * Abstract base class for a CrossReferenceHTMLGenerator implementation that is built to handle cross-references
     * expressed as CALFeatureNames.
     *
     * @author Joseph Wong
     */
    public static abstract class CALFeatureCrossReferenceGenerator extends CrossReferenceHTMLGenerator {

        /**
         * {@inheritDoc}
         */
        @Override
        final void generateModuleReference(final HTMLBuilder builder, final ModuleReference reference) {
            builder.addText(getRelatedFeatureLinkHTML(CALFeatureName.getModuleFeatureName(reference.getName()), reference.getModuleNameInSource()));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        final void generateTypeConsReference(final HTMLBuilder builder, final ScopedEntityReference reference) {
            builder.addText(getRelatedFeatureLinkHTML(CALFeatureName.getTypeConstructorFeatureName(reference.getName()), reference.getModuleNameInSource()));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        final void generateDataConsReference(final HTMLBuilder builder, final ScopedEntityReference reference) {
            builder.addText(getRelatedFeatureLinkHTML(CALFeatureName.getDataConstructorFeatureName(reference.getName()), reference.getModuleNameInSource()));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        final void generateFunctionOrClassMethodReference(final HTMLBuilder builder, final ScopedEntityReference reference) {
            final QualifiedName qualifiedName = reference.getName();
           
            CALFeatureName featureName;
            if (isClassMethodName(qualifiedName)) {
                featureName = CALFeatureName.getClassMethodFeatureName(qualifiedName);
            } else {
                featureName = CALFeatureName.getFunctionFeatureName(qualifiedName);
            }
            builder.addText(getRelatedFeatureLinkHTML(featureName, reference.getModuleNameInSource()));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        final void generateTypeClassReference(final HTMLBuilder builder, final ScopedEntityReference reference) {
            builder.addText(getRelatedFeatureLinkHTML(CALFeatureName.getTypeClassFeatureName(reference.getName()), reference.getModuleNameInSource()));
        }
       
        /**
         * Returns whether the given qualified name is a class method name.
         * @param qualifiedName the name to check.
         * @return true if the given qualified name is a class method name, false otherwise.
         */
        public abstract boolean isClassMethodName(QualifiedName qualifiedName);
       
        /**
         * Builds a well-formed HTML fragment for the cross-reference, given here as a CALFeatureName.
         * @param featureName the cross-reference.
         * @param moduleNameInSource how the module name portion of the reference appears in source. Could be the empty string if the reference is unqualified in source.
         * @return the HTML fragment for the cross-reference.
         */
        public abstract String getRelatedFeatureLinkHTML(CALFeatureName featureName, String moduleNameInSource);
    }
   
    /**
     * Implements a traverser which traverses through a CALDoc text block and generates the corresponding HTML for it.
     *
     * @author Joseph Wong
     */
    private static final class TextBlockHTMLGenerator extends CALDocCommentTextBlockTraverser<TextBlockHTMLGenerator.VisitationOption, Void> {

        /**
         * The HTMLBuilder to use for generating the HTML.
         */
        private final HTMLBuilder builder;
       
        /**
         * The cross-reference generator to use for generating HTML for cross-references.
         */
        private final CrossReferenceHTMLGenerator crossRefGen;
       
        /**
         * The HTML attribute to use for elements that represent "@code" blocks in the text block.
         */
        private final HTMLBuilder.AttributeList codeStyleClassAttribute;
       
        /**
         * The HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
         */
        private final HTML.Tag codeFormattingTag;
       
        /**
         * The nesting level of "@code"/"@link" blocks which require code formatting. It starts out at 0.
         */
        private int codeFormattingLevel = 0;

        /** An enumeration representing the various visitation options. */
        private static enum VisitationOption {
            /**
             * Visitation argument value for suppressing the initial paragraph tag in a block of paragraphs during generation.
             */
            SUPPRESS_INITIAL_PARAGRAPH_TAG,

            /**
             * Visitation argument value for suppressing the initial paragraph tag in a block of paragraphs
             * and trimming one leading whitespace character during generation.
             */
            SUPPRESS_INITIAL_PARAGRAPH_TAG_AND_TRIM_LEADING_WHITESPACE_CHAR,

            /**
             * Visitation argument value for trimming one leading whitespace character during generation.
             */
            TRIM_LEADING_WHITESPACE_CHAR_IN_SEGMENT,

            /**
             * Visitation argument value for indicating that the segment being generated is the last one in a paragraph whose paragraph
             * tags are to be suppressed.
             */
            LAST_SEGMENT_IN_SUPPRESSED_PARAGRAPH
        }

        /**
         * Implements a text block traverser which determines whether a text block spans more than one source line.
         *
         * @author Joseph Wong
         */
        private static final class MultipleLinesChecker extends CALDocCommentTextBlockTraverser<Void, Void> {
           
            /**
             * Keeps track of whether the text block being traversed spans more than one source line.
             */
            private boolean spansMultipleLines = false;
           
            /**
             * Determines whether the text block being traversed spans more than one source line.
             * @param block the text block to be traversed.
             * @param arg unused argument.
             * @return null.
             */
            @Override
            public Void visitTextBlock(final TextBlock block, final Void arg) {
                if (block.getNParagraphs() > 1) {
                    spansMultipleLines = true;
                } else {
                    // only traverse into the block to check if there is just zero or one paragraph in the block
                    super.visitTextBlock(block, arg);
                }
               
                return null;
            }
           
            /**
             * Determines whether the plain text segment being traversed spans more than one source line.
             * @param segment the segment to be traversed.
             * @param arg unused argument.
             * @return null.
             */
            @Override
            public Void visitPlainTextSegment(final PlainTextSegment segment, final Void arg) {
                if (segment.getText().indexOf('\n') >= 0 || segment.getText().indexOf('\r') >= 0) {
                    spansMultipleLines = true;
                }
               
                return super.visitPlainTextSegment(segment, arg);
            }
        }
       
        /**
         * Constructs a TextBlockHTMLGenerator for generating HTML for a text block.
         * @param builder the HTMLBuilder to use for generating the HTML.
         * @param crossRefGen the cross-reference generator to use for generating HTML for cross-references.
         * @param codeStyleClass the style class to use for HTML elements that represent "@code" blocks in the text block. Can be null.
         * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
         */
        private TextBlockHTMLGenerator(final HTMLBuilder builder, final CrossReferenceHTMLGenerator crossRefGen, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
            if (builder == null) {
                throw new NullPointerException();
            }
            this.builder = builder;
           
            if (crossRefGen == null) {
                throw new NullPointerException();
            }
            this.crossRefGen = crossRefGen;

            if (codeStyleClass == null) {
                this.codeStyleClassAttribute = HTMLBuilder.AttributeList.make();
            } else {
                this.codeStyleClassAttribute = HTMLBuilder.AttributeList.make(HTML.Attribute.CLASS, codeStyleClass.toHTML());
            }
           
            if (codeFormattingTag == null) {
                throw new NullPointerException();
            }
            this.codeFormattingTag = codeFormattingTag;
        }
       
        /**
         * Generates HTML for the given text block.
         * @param block the text block to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitTextBlock(final TextBlock block, final VisitationOption arg) {
            for (int i = 0, n = block.getNParagraphs(); i < n; i++) {
                final boolean isFirstSegment = (i == 0);
               
                final VisitationOption paragraphArg;
                if (isFirstSegment) {
                    if (arg == VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG || arg == VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG_AND_TRIM_LEADING_WHITESPACE_CHAR) {
                        paragraphArg = arg;
                    } else {
                        paragraphArg = VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG;
                    }
                } else {
                    paragraphArg = null;
                }
               
                block.getNthParagraph(i).accept(this, paragraphArg);
            }
           
            return null;
        }
       
        /**
         * Generates HTML for the given text paragraph.
         * @param paragraph the text paragraph to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitTextParagraph(final TextParagraph paragraph, final VisitationOption arg) {
            boolean suppressParagraphTag = (arg == VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG || arg == VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG_AND_TRIM_LEADING_WHITESPACE_CHAR);
           
            if (!suppressParagraphTag) {
                builder.emptyTag(HTML.Tag.P);
            }
           
            for (int i = 0, n = paragraph.getNSegments(); i < n; i++) {
                final boolean isFirstSegment = (i == 0);
                final boolean isLastSegment = (i == n - 1);
               
                final VisitationOption segmentArg;
                if (isFirstSegment && arg == VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG_AND_TRIM_LEADING_WHITESPACE_CHAR) {
                    segmentArg = VisitationOption.TRIM_LEADING_WHITESPACE_CHAR_IN_SEGMENT;
                } else if (isLastSegment && suppressParagraphTag) {
                    segmentArg = VisitationOption.LAST_SEGMENT_IN_SUPPRESSED_PARAGRAPH;
                } else {
                    segmentArg = null;
                }
               
                paragraph.getNthSegment(i).accept(this, segmentArg);
            }
           
            return null;
        }

        /**
         * Generates HTML for the given list paragraph.
         * @param paragraph the list paragraph to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitListParagraph(final ListParagraph paragraph, final VisitationOption arg) {
            final HTML.Tag listTag = paragraph.isOrdered() ? HTML.Tag.OL : HTML.Tag.UL;
           
            builder.openTag(listTag);
            super.visitListParagraph(paragraph, arg);
            builder.closeTag(listTag);
           
            return null;
        }

        /**
         * Generates HTML for the given list item.
         * @param item the list item to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitListItem(final ListItem item, final VisitationOption arg) {
            builder.openTag(HTML.Tag.LI);
            super.visitListItem(item, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG_AND_TRIM_LEADING_WHITESPACE_CHAR);
            builder.closeTag(HTML.Tag.LI);
           
            return null;
        }

        /**
         * Generates HTML for the given plain text segment.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitPlainTextSegment(final PlainTextSegment segment, final VisitationOption arg) {
            String text = segment.getText();
           
            if (arg == VisitationOption.TRIM_LEADING_WHITESPACE_CHAR_IN_SEGMENT) {
                if (text.length() > 0 && Character.isWhitespace(text.charAt(0))) {
                    text = text.substring(1);
                }
            } else if (arg == VisitationOption.LAST_SEGMENT_IN_SUPPRESSED_PARAGRAPH) {
                text = text.replaceAll("\\s+\\z", "");
            }
           
            builder.addText(htmlEscape(text));
           
            return super.visitPlainTextSegment(segment, arg);
        }

        /**
         * Generates HTML for the given URL segment.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitURLSegment(final URLSegment segment, final VisitationOption arg) {
            builder.addTaggedText(HTML.Tag.A, AttributeList.make(HTML.Attribute.HREF, htmlEscape(segment.getURL())), htmlEscape(segment.getURL()));
           
            return super.visitURLSegment(segment, arg);
        }

        /**
         * Adds an open tag for code formatting.
         */
        private void startCodeFormatting() {
            startCodeFormatting(codeFormattingTag);
        }

        /**
         * Adds an open tag for code formatting.
         * @param wrapperTag the tag to use.
         */
        private void startCodeFormatting(final HTML.Tag wrapperTag) {
            if (codeFormattingLevel == 0) {
                builder.openTag(wrapperTag, codeStyleClassAttribute);
            }
            codeFormattingLevel++;
        }

        /**
         * Adds a close tag for code formatting.
         */
        private void endCodeFormatting() {
            endCodeFormatting(codeFormattingTag);
        }

        /**
         * Adds a close tag for code formatting.
         * @param wrapperTag the tag to use.
         */
        private void endCodeFormatting(final HTML.Tag wrapperTag) {
            codeFormattingLevel--;
            if (codeFormattingLevel == 0) {
                builder.closeTag(wrapperTag);
            }
        }
       
        /**
         * Generates HTML for the given inline module cross-reference.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitModuleLinkSegment(final ModuleLinkSegment segment, final VisitationOption arg) {
            startCodeFormatting();
            crossRefGen.generateModuleReference(builder, segment.getReference());
            endCodeFormatting();
           
            return super.visitModuleLinkSegment(segment, arg);
        }

        /**
         * Generates HTML for the given inline function or class method cross-reference.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitFunctionOrClassMethodLinkSegment(final FunctionOrClassMethodLinkSegment segment, final VisitationOption arg) {
            startCodeFormatting();
            crossRefGen.generateFunctionOrClassMethodReference(builder, segment.getReference());
            endCodeFormatting();
           
            return super.visitFunctionOrClassMethodLinkSegment(segment, arg);
        }

        /**
         * Generates HTML for the given inline type constructor cross-reference.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitTypeConsLinkSegment(final TypeConsLinkSegment segment, final VisitationOption arg) {
            startCodeFormatting();
            crossRefGen.generateTypeConsReference(builder, segment.getReference());
            endCodeFormatting();
           
            return super.visitTypeConsLinkSegment(segment, arg);
        }

        /**
         * Generates HTML for the given inline data constructor cross-reference.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitDataConsLinkSegment(final DataConsLinkSegment segment, final VisitationOption arg) {
            startCodeFormatting();
            crossRefGen.generateDataConsReference(builder, segment.getReference());
            endCodeFormatting();
           
            return super.visitDataConsLinkSegment(segment, arg);
        }

        /**
         * Generates HTML for the given inline type class cross-reference.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitTypeClassLinkSegment(final TypeClassLinkSegment segment, final VisitationOption arg) {
            startCodeFormatting();
            crossRefGen.generateTypeClassReference(builder, segment.getReference());
            endCodeFormatting();
           
            return super.visitTypeClassLinkSegment(segment, arg);
        }

        /**
         * Generates HTML for the given code segment.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitCodeSegment(final CodeSegment segment, final VisitationOption arg) {
            final MultipleLinesChecker multipleLinesChecker = new MultipleLinesChecker();
            segment.accept(multipleLinesChecker, null);
           
            final HTML.Tag wrapperTag = multipleLinesChecker.spansMultipleLines ? HTML.Tag.PRE : codeFormattingTag;
           
            if (multipleLinesChecker.spansMultipleLines) {
                builder.newline();
            }
           
            startCodeFormatting(wrapperTag);
            super.visitCodeSegment(segment, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG_AND_TRIM_LEADING_WHITESPACE_CHAR);
            endCodeFormatting(wrapperTag);
           
            return null;
        }

        /**
         * Generates HTML for the given emphasized segment.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitEmphasizedSegment(final EmphasizedSegment segment, final VisitationOption arg) {
            builder.openTag(HTML.Tag.EM);
            super.visitEmphasizedSegment(segment, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG_AND_TRIM_LEADING_WHITESPACE_CHAR);
            builder.closeTag(HTML.Tag.EM);
           
            return null;
        }

        /**
         * Generates HTML for the given strongly emphasized segment.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitStronglyEmphasizedSegment(final StronglyEmphasizedSegment segment, final VisitationOption arg) {
            builder.openTag(HTML.Tag.STRONG);
            super.visitStronglyEmphasizedSegment(segment, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG_AND_TRIM_LEADING_WHITESPACE_CHAR);
            builder.closeTag(HTML.Tag.STRONG);
           
            return null;
        }

        /**
         * Generates HTML for the given superscript segment.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitSuperscriptSegment(final SuperscriptSegment segment, final VisitationOption arg) {
            builder.openTag(HTML.Tag.SUP);
            super.visitSuperscriptSegment(segment, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG_AND_TRIM_LEADING_WHITESPACE_CHAR);
            builder.closeTag(HTML.Tag.SUP);
           
            return null;
        }

        /**
         * Generates HTML for the given superscript segment.
         * @param segment the segment to be traversed.
         * @param arg additional argument for the traversal.
         * @return null.
         */
        @Override
        public Void visitSubscriptSegment(final SubscriptSegment segment, final VisitationOption arg) {
            builder.openTag(HTML.Tag.SUB);
            super.visitSubscriptSegment(segment, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG_AND_TRIM_LEADING_WHITESPACE_CHAR);
            builder.closeTag(HTML.Tag.SUB);
           
            return null;
        }
    }
   
    /** Private constructor. */
    private CALDocToHTMLUtilities() {}
   
    /**
     * Generates the HTML for a text block appearing in a CALDoc comment.
     * @param textBlock the text block to be formatted as HTML.
     * @param crossRefGen the cross reference generator.
     * @return the HTML for the text block.
     */
    public static String getHTMLForCALDocTextBlock(final CALDocComment.TextBlock textBlock, final CrossReferenceHTMLGenerator crossRefGen) {
        // specifies 'null' for the style class of code blocks and HTML.Tag.CODE for the tag
        final StyleClass codeStyleClass = null;
        final HTML.Tag codeFormattingTag = HTML.Tag.CODE;
        return getHTMLForCALDocTextBlock(textBlock, crossRefGen, codeStyleClass, codeFormattingTag);
    }
   
    /**
     * Generates the HTML for a text block appearing in a CALDoc comment.
     * @param textBlock the text block to be formatted as HTML.
     * @param crossRefGen the cross reference generator.
     * @param codeStyleClass the style class to use for code blocks. Can be null.
     * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
     * @return the HTML for the text block.
     */
    static String getHTMLForCALDocTextBlock(final CALDocComment.TextBlock textBlock, final CrossReferenceHTMLGenerator crossRefGen, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
        final HTMLBuilder builder = new HTMLBuilder();
        generateHTMLForCALDocTextBlock(textBlock, builder, crossRefGen, codeStyleClass, codeFormattingTag);
        return builder.toString();
    }
   
    /**
     * Generates the HTML for a text block appearing in a CALDoc comment.
     * @param textBlock the text block to be formatted as HTML.
     * @param builder the HTML builder for aggregating the result.
     * @param crossRefGen the cross reference generator.
     * @param codeStyleClass the style class to use for code blocks. Can be null.
     * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
     */
    static void generateHTMLForCALDocTextBlock(final CALDocComment.TextBlock textBlock, final HTMLBuilder builder, final CrossReferenceHTMLGenerator crossRefGen, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
        textBlock.accept(new TextBlockHTMLGenerator(builder, crossRefGen, codeStyleClass, codeFormattingTag), null);
    }
   
    /**
     * Generates the HTML for a paragraph appearing in a CALDoc comment.
     * @param paragraph the paragraph to be formatted as HTML.
     * @param builder the HTML builder for aggregating the result.
     * @param crossRefGen the cross reference generator.
     * @param codeStyleClass the style class to use for code blocks. Can be null.
     * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
     */
    static void generateHTMLForCALDocParagraph(final CALDocComment.Paragraph paragraph, final HTMLBuilder builder, final CrossReferenceHTMLGenerator crossRefGen, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
        paragraph.accept(new TextBlockHTMLGenerator(builder, crossRefGen, codeStyleClass, codeFormattingTag), TextBlockHTMLGenerator.VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_TAG);
    }
   
    /**
     * Generates the HTML for a segment appearing in a CALDoc comment.
     * @param segment the segment to be formatted as HTML.
     * @param builder the HTML builder for aggregating the result.
     * @param crossRefGen the cross reference generator.
     * @param codeStyleClass the style class to use for code blocks. Can be null.
     * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
     */
    static void generateHTMLForCALDocSegment(final CALDocComment.Segment segment, final HTMLBuilder builder, final CrossReferenceHTMLGenerator crossRefGen, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
        segment.accept(new TextBlockHTMLGenerator(builder, crossRefGen, codeStyleClass, codeFormattingTag), null);
    }
   
    /**
     * Builds the HTML for the summary of a CALDoc comment.
     * @param docComment the CALDoc comment.
     * @param crossRefGen the cross reference generator.
     * @param locale the locale for the documentation generation.
     * @param returnsColonPrefix the appropriately localized version of "Returns:".
     * @return the HTML for the summary of a CALDoc comment.
     */
    public static String getHTMLForCALDocSummary(final CALDocComment docComment, final CrossReferenceHTMLGenerator crossRefGen, final Locale locale, final String returnsColonPrefix) {
        // specifies 'null' for the style class of code blocks and HTML.Tag.CODE for the tag
        final StyleClass codeStyleClass = null;
        final HTML.Tag codeFormattingTag = HTML.Tag.CODE;
        return getHTMLForCALDocSummary(docComment, crossRefGen, locale, returnsColonPrefix, codeStyleClass, codeFormattingTag);
    }

    /**
     * Builds the HTML for the summary of a CALDoc comment.
     * @param docComment the CALDoc comment.
     * @param crossRefGen the cross reference generator.
     * @param locale the locale for the documentation generation.
     * @param returnsColonPrefix the appropriately localized version of "Returns:".
     * @param codeStyleClass the style class to use for code blocks. Can be null.
     * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
     * @return the HTML for the summary of a CALDoc comment.
     */
    static String getHTMLForCALDocSummary(final CALDocComment docComment, final CrossReferenceHTMLGenerator crossRefGen, final Locale locale, final String returnsColonPrefix, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
        final ContentConvertibleToHTML summaryFromCALDoc = getSummaryFromCALDoc(docComment, crossRefGen, locale, returnsColonPrefix, codeStyleClass, codeFormattingTag);
       
        if (summaryFromCALDoc != null) {
            final HTMLBuilder builder = new HTMLBuilder();
            summaryFromCALDoc.generateHTML(builder, crossRefGen);
            return builder.toString();
        } else {
            return ""; // there is no summary, so the valid HTML for no summary is the empty string.
        }
    }

    /**
     * Fetches the summary (short description) from a CALDoc comment.
     * @param docComment the CALDoc comment to be extracted. Can be null.
     * @param referenceGenerator the reference generator to use for generating cross references.
     * @param locale the locale to use for obtaining the appropriate BreakIterator.
     * @param returnsColonPrefix the appropriately localized version of "Returns:".
     * @param codeStyleClass the style class to use for code blocks. Can be null.
     * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
     * @return the summary, or null if none is available.
     */
    static ContentConvertibleToHTML getSummaryFromCALDoc(final CALDocComment docComment, final CrossReferenceHTMLGenerator referenceGenerator, final Locale locale, final String returnsColonPrefix, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
        /// If there is no comment or if it has no main description, return null
        //
        if (docComment == null) {
            return null;
        }
       
        /// If there is an {@summary} block, use it.
        //
        final CALDocComment.TextBlock summaryBlock = docComment.getSummary();
        if (summaryBlock != null) {
            return new SingleTextBlockContent(summaryBlock, codeStyleClass, codeFormattingTag);
        }
       
        /// There is no {@summary} block, so try using the first sentence as the summary.
        //
        final CALDocComment.TextBlock textBlock = docComment.getDescriptionBlock();
        if (textBlock != null) {
            final ContentConvertibleToHTML firstSentence = getFirstSentenceFromCALDocTextBlock(textBlock, locale, codeStyleClass, codeFormattingTag);
           
            if (firstSentence != null && !firstSentence.isEmpty()) {
                return firstSentence;
            }
        }
       
        /// There is no available first sentence, so use the return block instead.
        //
        final CALDocComment.TextBlock returnBlock = docComment.getReturnBlock();
        if (returnBlock != null) {
            final String returnBlockHTML = getHTMLForCALDocTextBlock(returnBlock, referenceGenerator, codeStyleClass, codeFormattingTag);
           
            if (returnBlockHTML != null && returnBlockHTML.length() > 0) {
                return new SimpleStringContent(returnsColonPrefix + " " + returnBlockHTML);
            }
        }

        /// Absolutely nothing available, so we return null as per the spec.
        //
        return null;
    }

    /**
     * Obtains the first sentence from a CALDoc text block.
     * @param textBlock the text block. Can be null.
     * @param locale the locale to use for obtaining the appropriate BreakIterator.
     * @param codeStyleClass the style class to use for code blocks. Can be null.
     * @param codeFormattingTag the HTML tag to use for formatting a code fragment. Should be either HTML.Tag.TT or HTML.Tag.CODE.
     * @return the first sentence, or null if none is available.
     */
    static ContentConvertibleToHTML getFirstSentenceFromCALDocTextBlock(final CALDocComment.TextBlock textBlock, final Locale locale, final StyleClass codeStyleClass, final HTML.Tag codeFormattingTag) {
        if (textBlock == null) {
            return null;
        }
       
        if (textBlock.getNParagraphs() == 0) {
            return null;
        }
       
        /// Set up a BreakIterator to parse the text and identify the first sentence.
        //
        BreakIterator breakIterator;
        if (LocaleUtilities.isInvariantLocale(locale)) {
            breakIterator = BreakIterator.getSentenceInstance();
        } else {
            breakIterator = BreakIterator.getSentenceInstance(locale);
        }
       
        /// If the first paragraph is a text paragraph, then try to identify which one contains the first "sentence break".
        //
        final CALDocComment.Paragraph paragraph = textBlock.getNthParagraph(0);
        if (paragraph instanceof CALDocComment.TextParagraph) {
            final CALDocComment.TextParagraph textParagraph = (CALDocComment.TextParagraph)paragraph;
           
            for (int i = 0, n = textParagraph.getNSegments(); i < n; i++) {
                final CALDocComment.Segment segment = textParagraph.getNthSegment(i);
               
                if (segment instanceof CALDocComment.PlainTextSegment) {
                    final CALDocComment.PlainTextSegment plainTextSegment = (CALDocComment.PlainTextSegment)segment;
                   
                    // Pad the string out with some whitespace at the end, so that if the break iterator returns
                    // the end of string as the boundary, we know that the original string could not have contained the
                    // boundary (or otherwise the boundary would've occurred before the end of the padded string).
                    final String text = plainTextSegment.getText() + "   ";
                   
                    breakIterator.setText(text);
                    final int firstSentenceBoundary = breakIterator.next();
                   
                    if (firstSentenceBoundary != BreakIterator.DONE && firstSentenceBoundary < text.length()) {
                        return new TextParagraphInitialSegments(textParagraph, i, text.substring(0, firstSentenceBoundary), codeStyleClass, codeFormattingTag);
                    }
                }
            }
        }
       
        // it's either not a text paragraph, or a "first sentence" could not be found, so just use the entire paragraph
        return new SingleParagraphContent(paragraph, codeStyleClass, codeFormattingTag);
    }

    /**
     * Escapes the text into proper HTML, converting characters into character entities when required.
     * @param text the text to be escaped.
     * @return the escaped text.
     */
    static String htmlEscape(final String text) {
        return text
            .replaceAll("&", "&amp;") // replace the ampersand first because it will show up later as part of character entities
            .replaceAll("<", "&lt;")
            .replaceAll(">", "&gt;");
    }
}
TOP

Related Classes of org.openquark.cal.caldoc.CALDocToHTMLUtilities$TextBlockHTMLGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.