/* * 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. */ /* * CALDocToTextUtilities.java * Creation date: Nov 9, 2005. * By: Joseph Wong */ package org.openquark.cal.caldoc; import java.util.HashSet; import java.util.Set; import org.openquark.cal.compiler.CALDocComment; import org.openquark.cal.compiler.CALDocCommentTextBlockTraverser; import org.openquark.cal.compiler.ClassMethod; import org.openquark.cal.compiler.Function; import org.openquark.cal.compiler.FunctionalAgent; import org.openquark.cal.compiler.ScopedEntity; import org.openquark.cal.compiler.ScopedEntityNamingPolicy; import org.openquark.cal.compiler.TypeExpr; import org.openquark.cal.compiler.CALDocComment.ArgBlock; 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; /** * This is a utility class containing helper methods for converting CALDoc into * pretty-printed text. * * @author Joseph Wong */ public class CALDocToTextUtilities { /** The number of spaces per indent in the generated text. */ private static final int INDENT_LEVEL = 2; /** * Implements a traverser which traverses through a CALDoc text block and builds up a text representation for it. * * @author Joseph Wong */ private static final class TextBuilder extends CALDocCommentTextBlockTraverser<TextBuilder.VisitationArg, Void> { /** * The string builder used to aggregate the generated text. */ private final StringBuilder buffer = new StringBuilder(); /** * The current indentation level. */ private int indentLevel; /** A marker interface for the types that can be used as arguments in this visitor. */ private static interface VisitationArg {} /** An enumeration representing the various visitation options. */ private static enum VisitationOption implements VisitationArg { /** * Visitation argument value for suppressing the initial paragraph break in a block of paragraphs during generation. */ SUPPRESS_INITIAL_PARAGRAPH_BREAK, /** * Visitation argument value for suppressing the initial paragraph break in a block of paragraphs * and trimming one leading whitespace character during generation. */ SUPPRESS_INITIAL_PARAGRAPH_BREAK_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 * breaks are to be suppressed. */ LAST_SEGMENT_IN_SUPPRESSED_PARAGRAPH } /** * Base class for a bullet maker for making bullets for list items. * * @author Joseph Wong */ private static abstract class ListItemBulletMaker implements VisitationArg { /** * Implements a bullet maker for an ordered list, generating bullets '1. ', '2. ', '3. ',... * * @author Joseph Wong */ private static final class Ordered extends ListItemBulletMaker { /** * The next item's index. The first item starts at 1. */ private int nextItemIndex = 1; /** * @return the bullet for the next list item. */ @Override String getNextBullet() { final int index = nextItemIndex++; return index + ". "; } } /** * Implements a bullet maker for an unordered list, generating bullets of the form: '- '. * * @author Joseph Wong */ private static final class Unordered extends ListItemBulletMaker { /** * @return the bullet for the next list item. */ @Override String getNextBullet() { return "- "; } } /** * @return the bullet for the next list item. */ abstract String getNextBullet(); } /** * Constructs an instance of this class with no initial indentation. */ TextBuilder() { this(0); } /** * Constructs an instance of this class with the specified initial indentation. * @param indentLevel the initial indentation level. */ TextBuilder(final int indentLevel) { this.indentLevel = indentLevel; } /** * @return the generated text for the text block. */ String getText() { return buffer.toString(); } /** * Appends a newline and leading spaces for indenting the next line to the string builder. */ private void appendNewline() { addNewlineToStringBuilder(buffer); } /** * Creates a string containing a newline and leading spaces for indenting the next line. * @return the requested string of newline and spaces. */ private String getNewline() { final StringBuilder buf = new StringBuilder(); addNewlineToStringBuilder(buf); return buf.toString(); } /** * Appends a newline and leading spaces for indenting the next line to the given string builder. * @param buf the string builder to be modified. */ private void addNewlineToStringBuilder(final StringBuilder buf) { buf.append('\n'); for (int i = 0; i < indentLevel; i++) { buf.append(' '); } } /** * Generates text 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 VisitationArg arg) { for (int i = 0, n = block.getNParagraphs(); i < n; i++) { final boolean isFirstSegment = (i == 0); final VisitationArg paragraphArg; if (isFirstSegment) { if (arg == VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK || arg == VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK_AND_TRIM_LEADING_WHITESPACE_CHAR) { paragraphArg = arg; } else { paragraphArg = VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK; } } else { paragraphArg = null; } block.getNthParagraph(i).accept(this, paragraphArg); } return null; } /** * Generates text 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 VisitationArg arg) { boolean suppressParagraphBreak = (arg == VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK || arg == VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK_AND_TRIM_LEADING_WHITESPACE_CHAR); if (!suppressParagraphBreak) { appendNewline(); appendNewline(); } for (int i = 0, n = paragraph.getNSegments(); i < n; i++) { final boolean isFirstSegment = (i == 0); final boolean isLastSegment = (i == n - 1); final VisitationArg segmentArg; if (isFirstSegment && arg == VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK_AND_TRIM_LEADING_WHITESPACE_CHAR) { segmentArg = VisitationOption.TRIM_LEADING_WHITESPACE_CHAR_IN_SEGMENT; } else if (isLastSegment && suppressParagraphBreak) { segmentArg = VisitationOption.LAST_SEGMENT_IN_SUPPRESSED_PARAGRAPH; } else { segmentArg = null; } paragraph.getNthSegment(i).accept(this, segmentArg); } return null; } /** * Generates text 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 VisitationArg arg) { ListItemBulletMaker bulletMaker; if (paragraph.isOrdered()) { bulletMaker = new ListItemBulletMaker.Ordered(); } else { bulletMaker = new ListItemBulletMaker.Unordered(); } super.visitListParagraph(paragraph, bulletMaker); appendNewline(); return null; } /** * Generates text 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 VisitationArg arg) { final ListItemBulletMaker bulletMaker = (ListItemBulletMaker)arg; appendNewline(); final String bullet = bulletMaker.getNextBullet(); final int oldIndentLevel = indentLevel; indentLevel += bullet.length(); buffer.append(bullet); super.visitListItem(item, arg); indentLevel = oldIndentLevel; appendNewline(); return null; } /** * Generates text 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 VisitationArg 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", ""); } text = text.replaceAll("(\r\n|\r|\n)", getNewline()); buffer.append(text); return super.visitPlainTextSegment(segment, arg); } /** * Generates text 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 VisitationArg arg) { buffer.append('<').append(segment.getURL()).append('>'); return super.visitURLSegment(segment, arg); } /** * Generates text 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 VisitationArg arg) { final ModuleReference reference = segment.getReference(); final String moduleNameInSource = reference.getModuleNameInSource(); if (moduleNameInSource.length() == 0) { buffer.append(reference.getName()); } else { buffer.append(moduleNameInSource); } return super.visitModuleLinkSegment(segment, arg); } /** * Returns the appropriately qualified name for a scoped entity reference. * @param reference the reference whose name is to be obtained. * @return the appropriately qualified name for a scoped entity reference. */ private String getScopedEntityReferenceName(final ScopedEntityReference reference) { final String moduleNameInSource = reference.getModuleNameInSource(); final String unqualifiedName = reference.getName().getUnqualifiedName(); if (moduleNameInSource.length() == 0) { return unqualifiedName; } else { return moduleNameInSource + '.' + unqualifiedName; } } /** * Generates text 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 VisitationArg arg) { buffer.append(getScopedEntityReferenceName(segment.getReference())); return super.visitFunctionOrClassMethodLinkSegment(segment, arg); } /** * Generates text 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 VisitationArg arg) { buffer.append(getScopedEntityReferenceName(segment.getReference())); return super.visitTypeConsLinkSegment(segment, arg); } /** * Generates text 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 VisitationArg arg) { buffer.append(getScopedEntityReferenceName(segment.getReference())); return super.visitDataConsLinkSegment(segment, arg); } /** * Generates text 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 VisitationArg arg) { buffer.append(getScopedEntityReferenceName(segment.getReference())); return super.visitTypeClassLinkSegment(segment, arg); } /** * Generates text 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 VisitationArg arg) { super.visitCodeSegment(segment, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK_AND_TRIM_LEADING_WHITESPACE_CHAR); return null; } /** * Generates text 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 VisitationArg arg) { buffer.append('/'); super.visitEmphasizedSegment(segment, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK_AND_TRIM_LEADING_WHITESPACE_CHAR); buffer.append('/'); return null; } /** * Generates text 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 VisitationArg arg) { buffer.append('*'); super.visitStronglyEmphasizedSegment(segment, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK_AND_TRIM_LEADING_WHITESPACE_CHAR); buffer.append('*'); return null; } /** * Generates text 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 VisitationArg arg) { buffer.append('^'); super.visitSuperscriptSegment(segment, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK_AND_TRIM_LEADING_WHITESPACE_CHAR); return null; } /** * Generates text 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 VisitationArg arg) { buffer.append('_'); super.visitSubscriptSegment(segment, VisitationOption.SUPPRESS_INITIAL_PARAGRAPH_BREAK_AND_TRIM_LEADING_WHITESPACE_CHAR); return null; } } /** Private constructor. */ private CALDocToTextUtilities() {} /** * Generates the text for a text block appearing in a CALDoc comment. * @param textBlock the text block to be formatted as text. * @return the text for the text block. */ public static String getTextFromCALDocTextBlock(final CALDocComment.TextBlock textBlock) { return getTextFromCALDocTextBlock(textBlock, 0); } /** * Generates the text for a text block appearing in a CALDoc comment. * @param textBlock the text block to be formatted as text. * @param initialIndentLevel the number of spaces to indent all of the text. * @return the text for the text block. */ public static String getTextFromCALDocTextBlock(final CALDocComment.TextBlock textBlock, final int initialIndentLevel) { final TextBuilder builder = new TextBuilder(initialIndentLevel); textBlock.accept(builder, null); return builder.getText(); } /** * Generates the text for a text block appearing in a CALDoc comment with the first line indented and with * a trailing newline at the end of the block. * @param textBlock the text block to be formatted as text. * @param initialIndentLevel the number of spaces to indent all of the text. * @return the text for the text block. */ private static String getTextFromCALDocTextBlockWithLeadingIndentAndTrailingNewline(final CALDocComment.TextBlock textBlock, final int initialIndentLevel) { return spaces(initialIndentLevel) + getTextFromCALDocTextBlock(textBlock, initialIndentLevel) + '\n'; } /** * Generates a plain text representation for a CALDoc comment of a ScopedEntity. * @param entity the entity with the CALDoc comment. * @return a plain text representation for the comment, or null if the entity does not have a CALDoc comment. */ public static String getTextFromCALDocCommentOfScopedEntity(final ScopedEntity entity) { final CALDocComment caldoc = entity.getCALDocComment(); if (caldoc == null) { return null; } if (entity instanceof FunctionalAgent) { return getTextFromCALDocComment(caldoc, (FunctionalAgent)entity, ScopedEntityNamingPolicy.FULLY_QUALIFIED); } else { return getTextFromCALDocComment(caldoc); } } /** * Generates a plain text representation for a CALDoc comment. * @param caldoc the CALDoc comment. * @return a plain text representation for the comment. */ public static String getTextFromCALDocComment(final CALDocComment caldoc) { // even though we specify ScopedEntityNamingPolicy.QUALIFIED here, it has no effect on the generated // text as there is no FunctionalAgent from which a type signature (potentially containing types in other modules) // can be extracted. return getTextFromCALDocComment(caldoc, null, ScopedEntityNamingPolicy.FULLY_QUALIFIED); } /** * Generates a plain text representation for a CALDoc comment. * @param caldoc the CALDoc comment. * @param envEntity the FunctionalAgent being documented. Can be null if the comment is not for an FunctionalAgent. * @param scopedEntityNamingPolicy the naming policy to use for generating qualified/unqualified names. * @return a plain text representation for the comment. */ public static String getTextFromCALDocComment(final CALDocComment caldoc, final FunctionalAgent envEntity, final ScopedEntityNamingPolicy scopedEntityNamingPolicy) { TypeExpr typeExpr; if (envEntity == null) { typeExpr = null; } else { typeExpr = envEntity.getTypeExpr(); } return getTextFromCALDocComment(caldoc, envEntity, typeExpr, scopedEntityNamingPolicy); } /** * Generates a plain text representation for a CALDoc comment. * @param caldoc the CALDoc comment. * @param envEntity the FunctionalAgent being documented. Can be null if the comment is not for an FunctionalAgent. * @param typeExpr the type of the entity. Can be null if envEntity is null. * @param scopedEntityNamingPolicy the naming policy to use for generating qualified/unqualified names. * @return a plain text representation for the comment. */ public static String getTextFromCALDocComment(final CALDocComment caldoc, final FunctionalAgent envEntity, final TypeExpr typeExpr, final ScopedEntityNamingPolicy scopedEntityNamingPolicy) { final StringBuilder buffer = new StringBuilder(); /// The deprecated block comes first. // final TextBlock deprecatedBlock = caldoc.getDeprecatedBlock(); if (deprecatedBlock != null) { buffer.append("[").append(CALDocMessages.getString("deprecatedColon")).append("\n"); buffer.append(getTextFromCALDocTextBlockWithLeadingIndentAndTrailingNewline(deprecatedBlock, INDENT_LEVEL)); buffer.append("]\n\n"); } /// If the entity is a class method, then display whether it is required or optional // if (envEntity instanceof ClassMethod) { final ClassMethod method = (ClassMethod)envEntity; buffer.append("\n"); if (method.getDefaultClassMethodName() == null) { // no default - required buffer.append(CALDocMessages.getString("requiredMethodIndicator")); } else { // has default - optional buffer.append(CALDocMessages.getString("optionalMethodIndicator")); } buffer.append("\n\n"); } /// The main description // buffer.append(getTextFromCALDocTextBlockWithLeadingIndentAndTrailingNewline(caldoc.getDescriptionBlock(), 0)); /// The arguments and the return value // buffer.append(getTextForArgumentsAndReturnValue(caldoc, envEntity, typeExpr, scopedEntityNamingPolicy)); /// The authors // final int nAuthors = caldoc.getNAuthorBlocks(); if (nAuthors > 0) { buffer.append("\n").append(CALDocMessages.getString("authorColon")).append("\n"); for (int i = 0; i < nAuthors; i++) { final TextBlock authorBlock = caldoc.getNthAuthorBlock(i); buffer.append(getTextFromCALDocTextBlockWithLeadingIndentAndTrailingNewline(authorBlock, INDENT_LEVEL)); } } /// The version // final TextBlock versionBlock = caldoc.getVersionBlock(); if (versionBlock != null) { buffer.append("\n").append(CALDocMessages.getString("versionColon")).append("\n"); buffer.append(getTextFromCALDocTextBlockWithLeadingIndentAndTrailingNewline(versionBlock, INDENT_LEVEL)); } /// The "See Also" references // final StringBuilder seeAlso = new StringBuilder(); // module cross references final int nModuleRefs = caldoc.getNModuleReferences(); if (nModuleRefs > 0) { seeAlso.append(spaces(INDENT_LEVEL)).append(CALDocMessages.getString("modulesColon")).append("\n"); for (int i = 0; i < nModuleRefs; i++) { seeAlso.append(spaces(INDENT_LEVEL * 2)).append(caldoc.getNthModuleReference(i).getName()).append('\n'); } seeAlso.append('\n'); } // function and class method references final int nFuncRefs = caldoc.getNFunctionOrClassMethodReferences(); if (nFuncRefs > 0) { seeAlso.append(spaces(INDENT_LEVEL)).append(CALDocMessages.getString("functionsAndClassMethodsColon")).append("\n"); for (int i = 0; i < nFuncRefs; i++) { seeAlso.append(spaces(INDENT_LEVEL * 2)).append(caldoc.getNthFunctionOrClassMethodReference(i).getName()).append('\n'); } seeAlso.append('\n'); } // type constructor references final int nTypeConsRefs = caldoc.getNTypeConstructorReferences(); if (nTypeConsRefs > 0) { seeAlso.append(spaces(INDENT_LEVEL)).append(CALDocMessages.getString("typeConstructorsColon")).append("\n"); for (int i = 0; i < nTypeConsRefs; i++) { seeAlso.append(spaces(INDENT_LEVEL * 2)).append(caldoc.getNthTypeConstructorReference(i).getName()).append('\n'); } seeAlso.append('\n'); } // data constructor references final int nDataConsRefs = caldoc.getNDataConstructorReferences(); if (nDataConsRefs > 0) { seeAlso.append(spaces(INDENT_LEVEL)).append(CALDocMessages.getString("dataConstructorsColon")).append("\n"); for (int i = 0; i < nDataConsRefs; i++) { seeAlso.append(spaces(INDENT_LEVEL * 2)).append(caldoc.getNthDataConstructorReference(i).getName()).append('\n'); } seeAlso.append('\n'); } // type class references final int nTypeClassRefs = caldoc.getNTypeClassReferences(); if (nTypeClassRefs > 0) { seeAlso.append(spaces(INDENT_LEVEL)).append(CALDocMessages.getString("typeClassesColon")).append("\n"); for (int i = 0; i < nTypeClassRefs; i++) { seeAlso.append(spaces(INDENT_LEVEL * 2)).append(caldoc.getNthTypeClassReference(i).getName()).append('\n'); } seeAlso.append('\n'); } if (seeAlso.length() > 0) { buffer.append("\n").append(CALDocMessages.getString("seeAlsoColon")).append("\n").append(seeAlso); } return buffer.toString(); } /** * Generates a plain text representation for the arguments and return value of a function, class method, or data constructor. * @param caldoc the associated CALDoc. Can be null. * @param envEntity the associated entity. Can be null. * @param scopedEntityNamingPolicy the naming policy to use for generating qualified/unqualified names. */ public static String getTextForArgumentsAndReturnValue(final CALDocComment caldoc, final FunctionalAgent envEntity, final ScopedEntityNamingPolicy scopedEntityNamingPolicy) { TypeExpr typeExpr; if (envEntity == null) { typeExpr = null; } else { typeExpr = envEntity.getTypeExpr(); } return getTextForArgumentsAndReturnValue(caldoc, envEntity, typeExpr, scopedEntityNamingPolicy); } /** * Generates a plain text representation for the arguments and return value of a function, class method, or data constructor. * @param caldoc the associated CALDoc. Can be null. * @param envEntity the associated entity. Can be null. * @param typeExpr the type of the entity. Can be null if envEntity is null. * @param scopedEntityNamingPolicy the naming policy to use for generating qualified/unqualified names. */ public static String getTextForArgumentsAndReturnValue(final CALDocComment caldoc, final FunctionalAgent envEntity, final TypeExpr typeExpr, final ScopedEntityNamingPolicy scopedEntityNamingPolicy) { final StringBuilder buffer = new StringBuilder(); /// The arguments // final int nArgsInCALDoc = (caldoc == null) ? -1 : caldoc.getNArgBlocks(); final int arity = (typeExpr == null) ? -1 : typeExpr.getArity(); final int nArgs = Math.max(nArgsInCALDoc, arity); TypeExpr[] typePieces = null; String[] typePieceStrings = null; if (typeExpr != null) { typePieces = typeExpr.getTypePieces(); typePieceStrings = TypeExpr.toStringArray(typePieces, true, scopedEntityNamingPolicy); } if (nArgs > 0) { buffer.append("\n").append(CALDocMessages.getString("argumentsColon")).append("\n"); final Set<String> setOfArgumentNames = new HashSet<String>(); for (int i = 0; i < nArgs; i++) { buffer.append(spaces(INDENT_LEVEL)).append(getNthArgumentName(caldoc, envEntity, i, setOfArgumentNames)); if (typePieces != null) { buffer.append(" :: ").append(typePieceStrings[i]); } buffer.append("\n"); if (caldoc != null && i < nArgsInCALDoc) { final ArgBlock argBlock = caldoc.getNthArgBlock(i); buffer.append(getTextFromCALDocTextBlockWithLeadingIndentAndTrailingNewline(argBlock.getTextBlock(), INDENT_LEVEL * 2)); } } } /// The return value // final boolean hasReturnBlock = (caldoc != null) && (caldoc.getReturnBlock() != null); if (hasReturnBlock || envEntity instanceof Function || envEntity instanceof ClassMethod) { buffer.append("\n").append(CALDocMessages.getString("returnsColon")).append("\n"); if (typePieces != null) { final String returnTypeString = typePieceStrings[arity]; buffer.append(spaces(INDENT_LEVEL)).append(CALDocMessages.getString("returnValueIndicator")).append(" :: ").append(returnTypeString).append("\n"); } if (hasReturnBlock) { final TextBlock returnBlock = caldoc.getReturnBlock(); buffer.append(getTextFromCALDocTextBlockWithLeadingIndentAndTrailingNewline(returnBlock, INDENT_LEVEL * 2)); } } return buffer.toString(); } /** * Generates an argument name of an FunctionalAgent. It gives preference to the name given in * CALDoc, if it is different from the name appearing in the entity itself. * * @param caldoc the CALDoc comment of the entity. Can be null. * @param envEntity the FunctionalAgent whose argument name is being generated. * @param index the position of the argument in the argument list. * @param setOfArgumentNames the (Set of Strings) of argument names already used (for disambiguation purposes). * @return the appropriate argument name. */ private static String getNthArgumentName(final CALDocComment caldoc, final FunctionalAgent envEntity, final int index, final Set<String> setOfArgumentNames) { //// /// First fetch the name from the entity. This will mostly be the same name as the one appearing in code, except for /// foreign functions, which may have their sames extracted from the Java classes' debug info. // String nameFromEntity = null; if (index < envEntity.getNArgumentNames()) { nameFromEntity = envEntity.getArgumentName(index); } String result; if (caldoc != null && index < caldoc.getNArgBlocks()) { //// /// Get the CALDoc name, if available. // final String nameFromCALDoc = caldoc.getNthArgBlock(index).getArgName().getCalSourceForm(); result = nameFromCALDoc; setOfArgumentNames.add(nameFromCALDoc); } else if (nameFromEntity != null) { //// /// If the entity yielded a name, use it. // result = nameFromEntity; setOfArgumentNames.add(nameFromEntity); } else { //// /// Since not even the entity yielded a name, construct an artificial one of the form arg_x, where x is the /// 1-based index of the argument in the argument list, or of the form arg_x_y if arg_x, arg_x_1, ... /// arg_x_(y-1) have all appeared previously in the argument list. // // the base artificial name we'll attempt to use is arg_x, where x is the 1-based index of this argument final String baseArtificialName = "arg_" + (index + 1); String artificialName = baseArtificialName; // if the base artificial name already appears in previous arguments, then // make the argument name arg_x_y, where y is a supplementary disambiguating number // chosen so that the resulting name will not collide with any of the previous argument names int supplementaryDisambiguator = 1; while (setOfArgumentNames.contains(artificialName)) { artificialName = baseArtificialName + "_" + supplementaryDisambiguator; supplementaryDisambiguator++; } result = artificialName; setOfArgumentNames.add(artificialName); } return result; } /** * Fills the specified StringBuilder with the specified number of spaces. * @param buffer the StringBuilder to fill. * @param nSpaces the number of spaces to be appended to the buffer. */ private static void addSpacesToStringBuilder(final StringBuilder buffer, final int nSpaces) { for (int i = 0; i < nSpaces; i++) { buffer.append(' '); } } /** * Returns a string of spaces. * @param nSpaces the number of space characters. * @return the requested string of spaces. */ private static String spaces(final int nSpaces) { final StringBuilder buffer = new StringBuilder(); addSpacesToStringBuilder(buffer, nSpaces); return buffer.toString(); } }