/* * 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. */ /* * CALDocComment.java * Creation date: Aug 12, 2005. * By: Joseph Wong */ package org.openquark.cal.compiler; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.openquark.cal.internal.serialization.ModuleSerializationTags; import org.openquark.cal.internal.serialization.RecordInputStream; import org.openquark.cal.internal.serialization.RecordOutputStream; import org.openquark.cal.internal.serialization.RecordInputStream.RecordHeaderInfo; /** * The representation of a CALDoc comment as stored by ModuleTypeInfo objects. A * CALDoc comment consists of a description block followed by a list of tagged * blocks. * <p> * This class is intended to be constructible only by other classes within this * package. Clients should not be able to create instances of this class. * Therefore, the compiler can guarantee that a CALDocComment object represents * a syntactically and semantically correct CALDoc comment. * <p> * This class is intended to be immutable, so that objects of this class can be * safely returned to clients without worrying about the possibility of these * objects being modified. * * @author Joseph Wong */ public final class CALDocComment { /** The serialization schema for CALDoc comments. */ private static final int serializationSchema = 0; /** The summary of the comment. */ private final TextBlock summary; /** The description block at the start of the comment. */ private final TextBlock descriptionBlock; /** The "@author" blocks in the comment. The array is empty if there are none of these blocks. */ private final TextBlock[] authorBlocks; /** The "@version" block in the comment, or null if there is none. */ private final TextBlock versionBlock; /** The "@deprecated" block in the comment, or null if there is none. */ private final TextBlock deprecatedBlock; /** The "@arg" blocks in the comment. The array is empty if there are none of these blocks. */ private final ArgBlock[] argBlocks; /** The "@return" block in the comment, or null if there is none. */ private final TextBlock returnBlock; /** The "@see" module references in the comment. The array is empty if there are none of these references. */ private final ModuleReference[] moduleRefs; /** The "@see" function and class method references in the comment. The array is empty if there are none of these references. */ private final ScopedEntityReference[] functionOrClassMethodRefs; /** The "@see" type constructor references in the comment. The array is empty if there are none of these references. */ private final ScopedEntityReference[] typeConsRefs; /** The "@see" data constructor references in the comment. The array is empty if there are none of these references. */ private final ScopedEntityReference[] dataConsRefs; /** The "@see" type class references in the comment. The array is empty if there are none of these references. */ private final ScopedEntityReference[] typeClassRefs; public static final TextBlock[] NO_TEXT_BLOCKS = new TextBlock[0]; public static final ArgBlock[] NO_ARG_BLOCKS = new ArgBlock[0]; public static final ModuleReference[] NO_MODULE_REFS = new ModuleReference[0]; public static final ScopedEntityReference[] NO_SCOPED_ENTITY_REFS = new ScopedEntityReference[0]; /** The number of spaces per indent in the string generated by the toString() methods. */ private static final int INDENT_LEVEL = 2; /** * Represents a reference in a list of references in a CALDoc "@see" block. * A reference can either be checked or unchecked. An unchecked reference is * represented in source by surrounding the name in double quotes. * * @author Joseph Wong */ public static abstract class Reference { /** Indicates whether the reference is to be statically checked and resolved during compilation. */ private final boolean checked; /** * How the module name portion of the reference appears in source. * Could be the empty string if the reference is unqualified in source. */ private final String moduleNameInSource; /** * Private constructor for this base class for a reference in a list of * references in an "@see" block. Intended to be invoked only by subclass * constructors. * * @param checked * whether the reference is to be statically checked and * resolved during compilation. * @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. */ private Reference(final boolean checked, final String moduleNameInSource) { this.checked = checked; verifyArg(moduleNameInSource, "moduleNameInSource"); this.moduleNameInSource = moduleNameInSource; } /** * @return true if the reference is to be statically checked and resolved during compilation; false otherwise. */ public boolean isChecked() { return checked; } /** * @return how the module name portion of the reference appears in source. * Could be the empty string if the reference is unqualified in source. */ public String getModuleNameInSource() { return moduleNameInSource; } } /** * Represents a (checked/unchecked) module name reference in a CALDoc "@see module = ..." block. * * @author Joseph Wong */ public static final class ModuleReference extends Reference { /** The module name encapsulated by this reference. */ private final ModuleName name; /** * Creates a representation of a module name reference in a CALDoc "@see module = ..." block. * * @param name the module name. * @param checked whether the name is to be checked and resolved statically during compilation. * @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. */ ModuleReference(final ModuleName name, final boolean checked, final String moduleNameInSource) { super(checked, moduleNameInSource); verifyArg(name, "name"); this.name = name; } /** * @return the module name encapsulated by this reference. */ public ModuleName getName() { return name; } /** @return a string representation of this instance. */ @Override public String toString() { return "[ModuleReference " + name + " (" + (isChecked() ? "checked" : "unchecked") + ", module name '" + getModuleNameInSource() + "' in source)]"; } /** * Write this instance of ModuleReference to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ void write(final RecordOutputStream s) throws IOException { s.writeModuleName(name); s.writeBoolean(isChecked()); s.writeUTF(getModuleNameInSource()); } /** * Load an instance of ModuleReference from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a ModuleReference instance deserialized from the stream. */ static ModuleReference load(final RecordInputStream s) throws IOException { final ModuleName name = s.readModuleName(); final boolean checked = s.readBoolean(); final String moduleNameInSource = s.readUTF(); return new ModuleReference(name, checked, moduleNameInSource); } } /** * Represents a (checked/unchecked) scoped entity name reference in a CALDoc "@see" block. * * @author Joseph Wong */ public static final class ScopedEntityReference extends Reference { /** The qualified name encapsulated by this reference. */ private final QualifiedName name; /** * Creates a representation of a scoped entity name reference in a CALDoc "@see" block. * * @param name the name of the scoped entity. * @param checked whether the name is to be checked and resolved statically during compilation. * @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. */ ScopedEntityReference(final QualifiedName name, final boolean checked, final String moduleNameInSource) { super(checked, moduleNameInSource); verifyArg(name, "name"); this.name = name; } /** * @return the qualified name encapsulated by this reference. */ public QualifiedName getName() { return name; } /** @return a string representation of this instance. */ @Override public String toString() { return "[ScopedEntityReference " + name + " (" + (isChecked() ? "checked" : "unchecked") + ", module name '" + getModuleNameInSource() + "' in source)]"; } /** * Write this instance of ScopedEntityReference to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ void write(final RecordOutputStream s) throws IOException { s.writeQualifiedName(name); s.writeBoolean(isChecked()); s.writeUTF(getModuleNameInSource()); } /** * Load an instance of ScopedEntityReference from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a ScopedEntityReference instance deserialized from the stream. */ static ScopedEntityReference load(final RecordInputStream s) throws IOException { final QualifiedName name = s.readQualifiedName(); final boolean checked = s.readBoolean(); final String moduleNameInSource = s.readUTF(); return new ScopedEntityReference(name, checked, moduleNameInSource); } } ////============= /// Paragraphs // /** * Represents a paragraph within a CALDoc comment. A paragraph is simply a * block of text that is to be separated from the text that comes before and * after it with paragraph breaks. * * @author Joseph Wong */ public static abstract class Paragraph { /** * The array of possible record tags used in calls to {@link RecordInputStream#findRecord(short[])} by * the {@link #loadWithRecordTag} method. */ private static final short[] PARAGRAPH_RECORD_TAGS = new short[] { ModuleSerializationTags.CALDOC_TEXT_PARAGRAPH, ModuleSerializationTags.CALDOC_LIST_PARAGRAPH }; /** Private constructor to be called by subclasses only. */ private Paragraph() {} /** * Accepts the visitation of a visitor, which implements the * CALDocCommentTextBlockVisitor interface. This abstract method is to be overridden * by each concrete subclass so that the correct visit method on the * visitor may be called based upon the type of the element being * visited. * <p> * * As the CALDocCommentTextBlockVisitor follows a more general visitor pattern * where arguments can be passed into the visit methods and return * values obtained from them, this method passes through the argument * into the visit method, and returns as its return value the return * value of the visit method. * <p> * * Nonetheless, for a significant portion of the common cases, the state of the * visitation can simply be kept as member variables within the visitor itself, * thereby eliminating the need to use the argument and return value of the * visit methods. In these scenarios, the recommended approach is to use * {@link Void} as the type argument for both <code>T</code> and <code>R</code>, and * pass in null as the argument, and return null as the return value. * <p> * * @param <T> the argument type. If the visitation argument is not used, specify {@link Void}. * @param <R> the return type. If the return value is not used, specify {@link Void}. * * @param visitor * the visitor. * @param arg * the argument to be passed to the visitor's visitXXX method. * @return the return value of the visitor's visitXXX method. */ public abstract <T, R> R accept(CALDocCommentTextBlockVisitor<T, R> visitor, T arg); /** @return a string representation of this instance. */ @Override public final String toString() { final StringBuilder result = new StringBuilder(); toStringBuilder(result, 0); return result.toString(); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ public abstract void toStringBuilder(StringBuilder result, int indentLevel); /** * Write this instance to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ abstract void writeWithRecordTag(RecordOutputStream s) throws IOException; /** * Load an instance of Paragraph from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a Paragraph instance deserialized from the stream. */ static Paragraph loadWithRecordTag(final RecordInputStream s) throws IOException { final short[] paragraphRecordTags = PARAGRAPH_RECORD_TAGS; final RecordHeaderInfo rhi = s.findRecord(paragraphRecordTags); if (rhi == null) { throw new IOException("Unable to find Paragraph record header."); } Paragraph paragraph; switch (rhi.getRecordTag()) { case ModuleSerializationTags.CALDOC_TEXT_PARAGRAPH: paragraph = TextParagraph.load(s); break; case ModuleSerializationTags.CALDOC_LIST_PARAGRAPH: paragraph = ListParagraph.load(s); break; default: throw new IOException("Unrecognized record tag of " + rhi.getRecordTag() + " for Paragraph."); } s.skipRestOfRecord(); return paragraph; } } /** * Represents a text paragraph within a CALDoc comment, i.e. a simple paragraph of text. * * @author Joseph Wong */ public static final class TextParagraph extends Paragraph { /** * The segments which constitute this paragraph. */ private final Segment[] segments; public static final Segment[] NO_SEGMENTS = new Segment[0]; /** * Creates a representation of a text paragraph in a CALDoc comment. * * @param segments the segments which constitute this paragraph. */ TextParagraph(final Segment[] segments) { verifyArrayArg(segments, "segments"); if (segments.length == 0) { this.segments = NO_SEGMENTS; } else { this.segments = segments.clone(); } } /** * @return the number of segments in this paragraph. */ public int getNSegments() { return segments.length; } /** * Returns the segment at the specified position in this paragraph. * @param index the index of the segment to return. * @return the segment at the specified position in this paragraph. */ public Segment getNthSegment(final int index) { return segments[index]; } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitTextParagraph(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[TextParagraph\n"); for (final Segment segment : segments) { segment.toStringBuilder(result, indentLevel + INDENT_LEVEL); } addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * Write this instance of TextParagraph to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_TEXT_PARAGRAPH, serializationSchema); write(s); s.endRecord(); } /** * Write this instance of TextParagraph to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ void write(final RecordOutputStream s) throws IOException { s.writeIntCompressed(segments.length); for (final Segment segment : segments) { segment.writeWithRecordTag(s); } } /** * Load an instance of TextParagraph from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a TextParagraph instance deserialized from the stream. */ static TextParagraph load(final RecordInputStream s) throws IOException { final int nSegments = s.readIntCompressed(); final Segment[] segments = new Segment[nSegments]; for (int i = 0; i < nSegments; i++) { segments[i] = Segment.loadWithRecordTag(s); } return new TextParagraph(segments); } } /** * Represents a list (either ordered or unordered) within a CALDoc comment. * * @author Joseph Wong */ public static final class ListParagraph extends Paragraph { /** * Whether the list is ordered or unordered. */ private final boolean isOrdered; /** * The list items which constitute this list. */ private final ListItem[] items; public static final ListItem[] NO_ITEMS = new ListItem[0]; /** * Creates a representation of a list in a CALDoc comment. * * @param isOrdered whether the list is ordered or unordered. * @param items the list items which constitute this list. */ ListParagraph(final boolean isOrdered, final ListItem[] items) { this.isOrdered = isOrdered; verifyArrayArg(items, "items"); if (items.length == 0) { this.items = NO_ITEMS; } else { this.items = items; // no cloning because this constructor is package-scoped, and we count on the caller to have done the appropriate cloning already } } /** * @return whether the list is ordered or unordered. */ public boolean isOrdered() { return isOrdered; } /** * @return the number of items in this list. */ public int getNItems() { return items.length; } /** * Returns the item at the specified position in this list. * @param index the index of the item to return. * @return the item at the specified position in this list. */ public ListItem getNthItem(final int index) { return items[index]; } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitListParagraph(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); if (isOrdered) { result.append("[Ordered ListParagraph\n"); } else { result.append("[Unordered ListParagraph\n"); } for (final ListItem item : items) { item.toStringBuilder(result, indentLevel + INDENT_LEVEL); } addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * Write this instance of ListParagraph to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_LIST_PARAGRAPH, serializationSchema); s.writeBoolean(isOrdered); s.writeIntCompressed(items.length); for (final ListItem item : items) { item.write(s); } s.endRecord(); } /** * Load an instance of ListParagraph from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a ListParagraph instance deserialized from the stream. */ static ListParagraph load(final RecordInputStream s) throws IOException { final boolean isOrdered = s.readBoolean(); final int nItems = s.readIntCompressed(); final ListItem[] items = new ListItem[nItems]; for (int i = 0; i < nItems; i++) { items[i] = ListItem.load(s); } return new ListParagraph(isOrdered, items); } } /** * Represents a list item within a CALDoc comment, either in a ordered list or * an unordered list. * * @author Joseph Wong */ public static final class ListItem { /** * The text block which constitute the content of this list item. */ private final TextBlock content; /** * Creates a representation of a list item in a CALDoc comment. * * @param content the text block which constitute the content of this list item. */ ListItem(final TextBlock content) { verifyArg(content, "content"); this.content = content; } /** * @return the text block which constitute the content of this list item. */ public TextBlock getContent() { return content; } /** * Accepts the visitation of a visitor, which implements the * CALDocCommentTextBlockVisitor interface. * <p> * * As the CALDocCommentTextBlockVisitor follows a more general visitor pattern * where arguments can be passed into the visit methods and return * values obtained from them, this method passes through the argument * into the visit method, and returns as its return value the return * value of the visit method. * <p> * * Nonetheless, for a significant portion of the common cases, the state of the * visitation can simply be kept as member variables within the visitor itself, * thereby eliminating the need to use the argument and return value of the * visit methods. In these scenarios, the recommended approach is to use * {@link Void} as the type argument for both <code>T</code> and <code>R</code>, and * pass in null as the argument, and return null as the return value. * <p> * * @param <T> the argument type. If the visitation argument is not used, specify {@link Void}. * @param <R> the return type. If the return value is not used, specify {@link Void}. * * @param visitor * the visitor. * @param arg * the argument to be passed to the visitor's visitListItem method. * @return the return value of the visitor's visitListItem method. */ public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitListItem(this, arg); } /** @return a string representation of this instance. */ @Override public final String toString() { final StringBuilder result = new StringBuilder(); toStringBuilder(result, 0); return result.toString(); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[ListItem\n"); content.toStringBuilder(result, indentLevel + INDENT_LEVEL); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * Write this instance of ListItem to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ void write(final RecordOutputStream s) throws IOException { content.write(s); } /** * Load an instance of ListItem from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a ListItem instance deserialized from the stream. */ static ListItem load(final RecordInputStream s) throws IOException { final TextBlock content = TextBlock.load(s); return new ListItem(content); } } ////============= /// Segments // /** * Represents a text segment in a CALDoc comment. A segment is simply * a unit of text, and a text paragraph is represented as a collection * of such segments. * * @author Joseph Wong */ public static abstract class Segment { /** * The array of possible record tags used in calls to {@link RecordInputStream#findRecord(short[])} by * the {@link #loadWithRecordTag} method. */ private static final short[] SEGMENT_RECORD_TAGS = new short[] { ModuleSerializationTags.CALDOC_PLAIN_TEXT_SEGMENT, ModuleSerializationTags.CALDOC_URL_SEGMENT, ModuleSerializationTags.CALDOC_MODULE_LINK_SEGMENT, ModuleSerializationTags.CALDOC_FUNCTION_OR_CLASS_METHOD_LINK_SEGMENT, ModuleSerializationTags.CALDOC_TYPE_CONS_LINK_SEGMENT, ModuleSerializationTags.CALDOC_DATA_CONS_LINK_SEGMENT, ModuleSerializationTags.CALDOC_TYPE_CLASS_LINK_SEGMENT, ModuleSerializationTags.CALDOC_CODE_SEGMENT, ModuleSerializationTags.CALDOC_EMPHASIZED_SEGMENT, ModuleSerializationTags.CALDOC_STRONGLY_EMPHASIZED_SEGMENT, ModuleSerializationTags.CALDOC_SUPERSCRIPT_SEGMENT, ModuleSerializationTags.CALDOC_SUBSCRIPT_SEGMENT }; /** Private constructor to be called by subclasses only. */ private Segment() {} /** * Accepts the visitation of a visitor, which implements the * CALDocCommentTextBlockVisitor interface. This abstract method is to be overridden * by each concrete subclass so that the correct visit method on the * visitor may be called based upon the type of the element being * visited. * <p> * * As the CALDocCommentTextBlockVisitor follows a more general visitor pattern * where arguments can be passed into the visit methods and return * values obtained from them, this method passes through the argument * into the visit method, and returns as its return value the return * value of the visit method. * <p> * * Nonetheless, for a significant portion of the common cases, the state of the * visitation can simply be kept as member variables within the visitor itself, * thereby eliminating the need to use the argument and return value of the * visit methods. In these scenarios, the recommended approach is to use * {@link Void} as the type argument for both <code>T</code> and <code>R</code>, and * pass in null as the argument, and return null as the return value. * <p> * * @param <T> the argument type. If the visitation argument is not used, specify {@link Void}. * @param <R> the return type. If the return value is not used, specify {@link Void}. * * @param visitor * the visitor. * @param arg * the argument to be passed to the visitor's visitXXX method. * @return the return value of the visitor's visitXXX method. */ public abstract <T, R> R accept(CALDocCommentTextBlockVisitor<T, R> visitor, T arg); /** @return a string representation of this instance. */ @Override public final String toString() { final StringBuilder result = new StringBuilder(); toStringBuilder(result, 0); return result.toString(); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ public abstract void toStringBuilder(StringBuilder result, int indentLevel); /** * Unescapes the given text that appears in the source of a CALDoc comment. * @param text the text to be unescaped. * @return the unescaped text. */ static String caldocUnescape(String text) { text = text.replaceAll("\\\\\\{@", "{@"); // '\{@' -> '{@' text = text.replaceAll("\\\\@", "@"); // '\@' -> '@' return text; } /** * Write this instance to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ abstract void writeWithRecordTag(RecordOutputStream s) throws IOException; /** * Load an instance of Segment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a Segment instance deserialized from the stream. */ static Segment loadWithRecordTag(final RecordInputStream s) throws IOException { final short[] paragraphRecordTags = SEGMENT_RECORD_TAGS; final RecordHeaderInfo rhi = s.findRecord(paragraphRecordTags); if (rhi == null) { throw new IOException("Unable to find Segment record header."); } Segment segment; switch (rhi.getRecordTag()) { case ModuleSerializationTags.CALDOC_PLAIN_TEXT_SEGMENT: segment = PlainTextSegment.load(s); break; case ModuleSerializationTags.CALDOC_URL_SEGMENT: segment = URLSegment.load(s); break; case ModuleSerializationTags.CALDOC_MODULE_LINK_SEGMENT: segment = ModuleLinkSegment.load(s); break; case ModuleSerializationTags.CALDOC_FUNCTION_OR_CLASS_METHOD_LINK_SEGMENT: segment = FunctionOrClassMethodLinkSegment.load(s); break; case ModuleSerializationTags.CALDOC_TYPE_CONS_LINK_SEGMENT: segment = TypeConsLinkSegment.load(s); break; case ModuleSerializationTags.CALDOC_DATA_CONS_LINK_SEGMENT: segment = DataConsLinkSegment.load(s); break; case ModuleSerializationTags.CALDOC_TYPE_CLASS_LINK_SEGMENT: segment = TypeClassLinkSegment.load(s); break; case ModuleSerializationTags.CALDOC_CODE_SEGMENT: segment = CodeSegment.load(s); break; case ModuleSerializationTags.CALDOC_EMPHASIZED_SEGMENT: segment = EmphasizedSegment.load(s); break; case ModuleSerializationTags.CALDOC_STRONGLY_EMPHASIZED_SEGMENT: segment = StronglyEmphasizedSegment.load(s); break; case ModuleSerializationTags.CALDOC_SUPERSCRIPT_SEGMENT: segment = SuperscriptSegment.load(s); break; case ModuleSerializationTags.CALDOC_SUBSCRIPT_SEGMENT: segment = SubscriptSegment.load(s); break; default: throw new IOException("Unrecognized record tag of " + rhi.getRecordTag() + " for Segment."); } s.skipRestOfRecord(); return segment; } } /** * Represents a simple piece of text in a CALDoc comment. * * @author Joseph Wong */ public static final class PlainTextSegment extends Segment { /** * The text encapsulated by this instance. */ private final String text; /** * Creates a representation of a simple piece of text in a CALDoc comment. * * @param text the text encapsulated by this instance. */ PlainTextSegment(final String text) { verifyArg(text, "text"); this.text = text; } /** * Factory method for constructing a representation of a simple piece of text in a CALDoc comment * with a piece of escaped text (as in the format used in the source code). * * @param escapedText the escaped text. * @return a new instance of this class. */ static PlainTextSegment makeWithEscapedText(final String escapedText) { return new PlainTextSegment(caldocUnescape(escapedText)); } /** * @return the text encapsulated by this instance. */ public String getText() { return text; } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitPlainTextSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[PlainTextSegment\n"); addSpacesToStringBuilder(result, indentLevel + INDENT_LEVEL - 1); result.append('\"'); result.append(text.replaceAll("(\r\n|\n|\r)", "\n" + spaces(indentLevel + INDENT_LEVEL))).append("\"\n"); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_PLAIN_TEXT_SEGMENT, serializationSchema); s.writeUTF(text); s.endRecord(); } /** * Load an instance of PlainTextSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a PlainTextSegment instance deserialized from the stream. */ static PlainTextSegment load(final RecordInputStream s) throws IOException { final String text = s.readUTF(); return new PlainTextSegment(text); } } /** * Represents an inline tag segment in a CALDoc comment, i.e. segments appearing * in the source code as "{at-tagName ...at-}". * * @author Joseph Wong */ public static abstract class InlineTagSegment extends Segment { /** Private constructor to be called by subclasses only. */ private InlineTagSegment() {} } /** * Represents a hyperlinkable URL in a CALDoc comment. * * @author Joseph Wong */ public static final class URLSegment extends InlineTagSegment { /** * The URL encapsulated by this instance. */ private final String url; /** * Creates a representation of a hyperlinkable URL in a CALDoc comment. * * @param url the URL encapsulated by this instance. */ URLSegment(final String url) { verifyArg(url, "url"); this.url = url; } /** * Factory method for constructing a representation of a hyperlinkable URL in a CALDoc comment * with a piece of escaped text (as in the format used in the source code). * * @param escapedText the escaped text. * @return a new instance of this class. */ static URLSegment makeWithEscapedText(final String escapedText) { return new URLSegment(caldocUnescape(escapedText).trim()); // trim any excess whitespace } /** * @return the URL encapsulated by this instance. */ public String getURL() { return url; } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitURLSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[URLSegment ").append(url).append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_URL_SEGMENT, serializationSchema); s.writeUTF(url); s.endRecord(); } /** * Load an instance of URLSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a URLSegment instance deserialized from the stream. */ static URLSegment load(final RecordInputStream s) throws IOException { final String url = s.readUTF(); return new URLSegment(url); } } /** * Represents an inline, hyperlinkable cross-reference in a CALDoc comment. * * @author Joseph Wong */ public static abstract class LinkSegment extends InlineTagSegment { /** Private constructor to be called by subclasses only. */ private LinkSegment() {} } /** * Represents an inline, hyperlinkable cross-reference to a module in a CALDoc comment. * * @author Joseph Wong */ public static final class ModuleLinkSegment extends LinkSegment { /** * The cross-reference encapsulated by this link segment. */ private final ModuleReference reference; /** * Creates a representation of an inline, hyperlinkable cross-reference to a module in a CALDoc comment. * * @param reference the cross-reference encapsulated by this link segment. */ ModuleLinkSegment(final ModuleReference reference) { verifyArg(reference, "reference"); this.reference = reference; } /** * @return the cross-reference encapsulated by this link segment. */ public ModuleReference getReference() { return reference; } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitModuleLinkSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[ModuleLinkSegment ").append(reference).append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_MODULE_LINK_SEGMENT, serializationSchema); reference.write(s); s.endRecord(); } /** * Load an instance of ModuleLinkSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a ModuleLinkSegment instance deserialized from the stream. */ static ModuleLinkSegment load(final RecordInputStream s) throws IOException { final ModuleReference reference = ModuleReference.load(s); return new ModuleLinkSegment(reference); } } /** * Represents an inline, hyperlinkable cross-reference to a scoped entity in a CALDoc comment. * * @author Joseph Wong */ public static abstract class ScopedEntityLinkSegment extends LinkSegment { /** * The cross-reference encapsulated by this link segment. */ private final ScopedEntityReference reference; /** * Private constructor to be called by subclasses only. * @param reference the cross-reference encapsulated by this link segment. */ private ScopedEntityLinkSegment(final ScopedEntityReference reference) { verifyArg(reference, "reference"); this.reference = reference; } /** * @return the cross-reference encapsulated by this link segment. */ public ScopedEntityReference getReference() { return reference; } } /** * Represents an inline, hyperlinkable cross-reference to a function or class method in a CALDoc comment. * * @author Joseph Wong */ public static final class FunctionOrClassMethodLinkSegment extends ScopedEntityLinkSegment { /** * Creates a representation of an inline, hyperlinkable cross-reference to a function or class method in a CALDoc comment. * * @param reference the cross-reference encapsulated by this link segment. */ FunctionOrClassMethodLinkSegment(final ScopedEntityReference reference) { super(reference); } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitFunctionOrClassMethodLinkSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[FunctionOrClassMethodLinkSegment ").append(getReference()).append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_FUNCTION_OR_CLASS_METHOD_LINK_SEGMENT, serializationSchema); getReference().write(s); s.endRecord(); } /** * Load an instance of FunctionOrClassMethodLinkSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a FunctionOrClassMethodLinkSegment instance deserialized from the stream. */ static FunctionOrClassMethodLinkSegment load(final RecordInputStream s) throws IOException { final ScopedEntityReference reference = ScopedEntityReference.load(s); return new FunctionOrClassMethodLinkSegment(reference); } } /** * Represents an inline, hyperlinkable cross-reference to a type constructor in a CALDoc comment. * * @author Joseph Wong */ public static final class TypeConsLinkSegment extends ScopedEntityLinkSegment { /** * Creates a representation of an inline, hyperlinkable cross-reference to a type constructor in a CALDoc comment. * * @param reference the cross-reference encapsulated by this link segment. */ TypeConsLinkSegment(final ScopedEntityReference reference) { super(reference); } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitTypeConsLinkSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[TypeConsLinkSegment ").append(getReference()).append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_TYPE_CONS_LINK_SEGMENT, serializationSchema); getReference().write(s); s.endRecord(); } /** * Load an instance of TypeConsLinkSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a TypeConsLinkSegment instance deserialized from the stream. */ static TypeConsLinkSegment load(final RecordInputStream s) throws IOException { final ScopedEntityReference reference = ScopedEntityReference.load(s); return new TypeConsLinkSegment(reference); } } /** * Represents an inline, hyperlinkable cross-reference to a data constructor in a CALDoc comment. * * @author Joseph Wong */ public static final class DataConsLinkSegment extends ScopedEntityLinkSegment { /** * Creates a representation of an inline, hyperlinkable cross-reference to a data constructor in a CALDoc comment. * * @param reference the cross-reference encapsulated by this link segment. */ DataConsLinkSegment(final ScopedEntityReference reference) { super(reference); } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitDataConsLinkSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[DataConsLinkSegment ").append(getReference()).append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_DATA_CONS_LINK_SEGMENT, serializationSchema); getReference().write(s); s.endRecord(); } /** * Load an instance of DataConsLinkSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a DataConsLinkSegment instance deserialized from the stream. */ static DataConsLinkSegment load(final RecordInputStream s) throws IOException { final ScopedEntityReference reference = ScopedEntityReference.load(s); return new DataConsLinkSegment(reference); } } /** * Represents an inline, hyperlinkable cross-reference to a type class in a CALDoc comment. * * @author Joseph Wong */ public static final class TypeClassLinkSegment extends ScopedEntityLinkSegment { /** * Creates a representation of an inline, hyperlinkable cross-reference to a type class in a CALDoc comment. * * @param reference the cross-reference encapsulated by this link segment. */ TypeClassLinkSegment(final ScopedEntityReference reference) { super(reference); } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitTypeClassLinkSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[TypeClassLinkSegment ").append(getReference()).append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_TYPE_CLASS_LINK_SEGMENT, serializationSchema); getReference().write(s); s.endRecord(); } /** * Load an instance of TypeClassLinkSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a TypeClassLinkSegment instance deserialized from the stream. */ static TypeClassLinkSegment load(final RecordInputStream s) throws IOException { final ScopedEntityReference reference = ScopedEntityReference.load(s); return new TypeClassLinkSegment(reference); } } /** * Represents a block of source code in a CALDoc comment. In generating formatted output, the * whitespace contained within the text is respected. * * @author Joseph Wong */ public static final class CodeSegment extends InlineTagSegment { /** * The text of the block of source code. */ private final TextBlock content; /** * Creates a representation of a block of source code in a CALDoc comment. * * @param content the text of the block of source code. */ CodeSegment(final TextBlock content) { verifyArg(content, "content"); this.content = content; } /** * @return the text of the block of source code. */ public TextBlock getContent() { return content; } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitCodeSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[CodeSegment\n"); content.toStringBuilder(result, indentLevel + INDENT_LEVEL); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_CODE_SEGMENT, serializationSchema); content.write(s); s.endRecord(); } /** * Load an instance of CodeSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a CodeSegment instance deserialized from the stream. */ static CodeSegment load(final RecordInputStream s) throws IOException { final TextBlock content = TextBlock.load(s); return new CodeSegment(content); } } /** * Represents a piece of text in a CALDoc comment that has formatting applied to it. * * @author Joseph Wong */ public static abstract class FormattedSegment extends InlineTagSegment { /** * The content encapsulated by this formatted segment. */ private final TextParagraph content; /** * Private constructor to be called by subclasses only. * @param content the content encapsulated by this formatted segment. */ private FormattedSegment(final TextParagraph content) { verifyArg(content, "content"); this.content = content; } /** * @return the content encapsulated by this formatted segment. */ public TextParagraph getContent() { return content; } } /** * Represents a piece of text in a CALDoc comment that is emphasized. * * @author Joseph Wong */ public static final class EmphasizedSegment extends FormattedSegment { /** * Creates a representation of a piece of text in a CALDoc comment that is emphasized. * * @param content the content encapsulated by this segment. */ EmphasizedSegment(final TextParagraph content) { super(content); } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitEmphasizedSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[EmphasizedSegment\n"); getContent().toStringBuilder(result, indentLevel + INDENT_LEVEL); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_EMPHASIZED_SEGMENT, serializationSchema); getContent().write(s); s.endRecord(); } /** * Load an instance of EmphasizedSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a EmphasizedSegment instance deserialized from the stream. */ static EmphasizedSegment load(final RecordInputStream s) throws IOException { final TextParagraph content = TextParagraph.load(s); return new EmphasizedSegment(content); } } /** * Represents a piece of text in a CALDoc comment that is strongly emphasized. * * @author Joseph Wong */ public static final class StronglyEmphasizedSegment extends FormattedSegment { /** * Creates a representation of a piece of text in a CALDoc comment that is strongly emphasized. * * @param content the content encapsulated by this segment. */ StronglyEmphasizedSegment(final TextParagraph content) { super(content); } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitStronglyEmphasizedSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[StronglyEmphasizedSegment\n"); getContent().toStringBuilder(result, indentLevel + INDENT_LEVEL); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_STRONGLY_EMPHASIZED_SEGMENT, serializationSchema); getContent().write(s); s.endRecord(); } /** * Load an instance of StronglyEmphasizedSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a StronglyEmphasizedSegment instance deserialized from the stream. */ static StronglyEmphasizedSegment load(final RecordInputStream s) throws IOException { final TextParagraph content = TextParagraph.load(s); return new StronglyEmphasizedSegment(content); } } /** * Represents a piece of text in a CALDoc comment that is superscripted. * * @author Joseph Wong */ public static final class SuperscriptSegment extends FormattedSegment { /** * Creates a representation of a piece of text in a CALDoc comment that is superscripted. * * @param content the content encapsulated by this segment. */ SuperscriptSegment(final TextParagraph content) { super(content); } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitSuperscriptSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[SuperscriptSegment\n"); getContent().toStringBuilder(result, indentLevel + INDENT_LEVEL); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_SUPERSCRIPT_SEGMENT, serializationSchema); getContent().write(s); s.endRecord(); } /** * Load an instance of SuperscriptSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a SuperscriptSegment instance deserialized from the stream. */ static SuperscriptSegment load(final RecordInputStream s) throws IOException { final TextParagraph content = TextParagraph.load(s); return new SuperscriptSegment(content); } } /** * Represents a piece of text in a CALDoc comment that is subscripted. * * @author Joseph Wong */ public static final class SubscriptSegment extends FormattedSegment { /** * Creates a representation of a piece of text in a CALDoc comment that is subscripted. * * @param content the content encapsulated by this segment. */ SubscriptSegment(final TextParagraph content) { super(content); } /** * {@inheritDoc} */ @Override public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitSubscriptSegment(this, arg); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ @Override public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[SubscriptSegment\n"); getContent().toStringBuilder(result, indentLevel + INDENT_LEVEL); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * {@inheritDoc} */ @Override void writeWithRecordTag(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_SUBSCRIPT_SEGMENT, serializationSchema); getContent().write(s); s.endRecord(); } /** * Load an instance of SubscriptSegment from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a SubscriptSegment instance deserialized from the stream. */ static SubscriptSegment load(final RecordInputStream s) throws IOException { final TextParagraph content = TextParagraph.load(s); return new SubscriptSegment(content); } } /** * Represents a block of paragraphs within a CALDoc comment. The description * block of a CALDoc comment is a TextBlock, and so are the descriptions * contained with a number of the tagged blocks. * * @author Joseph Wong */ public static final class TextBlock { /** The paragraphs of text that constitute this text block. */ private final Paragraph[] paragraphs; public static final Paragraph[] NO_PARAGRAPHS = new Paragraph[0]; /** * Creates a representation of a block of paragraphs in a CALDoc * comment. * * @param paragraphs the paragraphs of text that constitute the text block. */ TextBlock(final Paragraph[] paragraphs) { verifyArrayArg(paragraphs, "paragraphs"); if (paragraphs.length == 0) { this.paragraphs = NO_PARAGRAPHS; } else { this.paragraphs = paragraphs.clone(); } } /** * Creates a representation of a block of paragraphs in a CALDoc * comment. * * @param paragraphs the paragraphs of text that constitute the text block. */ TextBlock(final List<Paragraph> paragraphs) { verifyArg(paragraphs, "paragraphs"); final Paragraph[] paragraphsArray = paragraphs.toArray(new Paragraph[0]); if (paragraphsArray.length == 0) { this.paragraphs = NO_PARAGRAPHS; } else { this.paragraphs = paragraphsArray; } } /** * @return the number of paragraphs in this text block. */ public int getNParagraphs() { return paragraphs.length; } /** * Returns the paragraph at the specified position in this text block. * @param index the index of the paragraph to return. * @return the paragraph at the specified position in this text block. */ public Paragraph getNthParagraph(final int index) { return paragraphs[index]; } /** * Accepts the visitation of a visitor, which implements the * CALDocCommentTextBlockVisitor interface. * <p> * * As the CALDocCommentTextBlockVisitor follows a more general visitor pattern * where arguments can be passed into the visit methods and return * values obtained from them, this method passes through the argument * into the visit method, and returns as its return value the return * value of the visit method. * <p> * * Nonetheless, for a significant portion of the common cases, the state of the * visitation can simply be kept as member variables within the visitor itself, * thereby eliminating the need to use the argument and return value of the * visit methods. In these scenarios, the recommended approach is to use * {@link Void} as the type argument for both <code>T</code> and <code>R</code>, and * pass in null as the argument, and return null as the return value. * <p> * * @param <T> the argument type. If the visitation argument is not used, specify {@link Void}. * @param <R> the return type. If the return value is not used, specify {@link Void}. * * @param visitor * the visitor. * @param arg * the argument to be passed to the visitor's visitTextBlock method. * @return the return value of the visitor's visitTextBlock method. */ public <T, R> R accept(final CALDocCommentTextBlockVisitor<T, R> visitor, final T arg) { return visitor.visitTextBlock(this, arg); } /** @return a string representation of this instance. */ @Override public final String toString() { final StringBuilder result = new StringBuilder(); toStringBuilder(result, 0); return result.toString(); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[TextBlock\n"); for (final Paragraph paragraph : paragraphs) { paragraph.toStringBuilder(result, indentLevel + INDENT_LEVEL); } addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * Write this instance of TextBlock to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ void write(final RecordOutputStream s) throws IOException { s.writeIntCompressed(paragraphs.length); for (final Paragraph paragraph : paragraphs) { paragraph.writeWithRecordTag(s); } } /** * Load an instance of TextBlock from the RecordInputStream. * @param s the RecordInputStream to be read from. * @return a TextBlock instance deserialized from the stream. */ static TextBlock load(final RecordInputStream s) throws IOException { final int nParagraphs = s.readIntCompressed(); final Paragraph[] paragraphs = new Paragraph[nParagraphs]; for (int i = 0; i < nParagraphs; i++) { paragraphs[i] = Paragraph.loadWithRecordTag(s); } return new TextBlock(paragraphs); } } /** * Represents the "@arg" CALDoc tag. The tagged block contains a reference * to an argument name, and is therefore valid only for a CALDoc function * comment or a CALDoc data constructor comment. * * @author Joseph Wong */ public static final class ArgBlock { /** The name of the argument referenced by this "@arg" tag. */ private final FieldName argName; /** The text block describing the argument. */ private final TextBlock textBlock; /** * Creates a representation of an "@arg" block in a CALDoc comment. * * @param argName * the name of the argument referenced by this tagged block. * @param textBlock * the text block that forms the trailing portion of this * tagged block. */ ArgBlock(final FieldName argName, final TextBlock textBlock) { this.argName = argName; this.textBlock = textBlock; } /** * @return the name of the argument referenced by this "@arg" tag. */ public FieldName getArgName() { return argName; } /** * @return the text block describing the argument. */ public TextBlock getTextBlock() { return textBlock; } /** @return a string representation of this instance. */ @Override public final String toString() { final StringBuilder result = new StringBuilder(); toStringBuilder(result, 0); return result.toString(); } /** * Fills the given StringBuilder with a string representation of this instance. * @param result the StringBuilder to fill. * @param indentLevel the indent level to use in indenting the generated text. */ public void toStringBuilder(final StringBuilder result, final int indentLevel) { addSpacesToStringBuilder(result, indentLevel); result.append("[ArgBlock - ").append(argName).append(":\n"); textBlock.toStringBuilder(result, indentLevel + INDENT_LEVEL); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); } /** * Write this instance of ArgBlock to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ void write(final RecordOutputStream s) throws IOException { FieldNameIO.writeFieldName(argName, s); textBlock.write(s); } /** * Load an instance of ArgBlock from the RecordInputStream. * @param s the RecordInputStream to be read from. * @param moduleName the name of the module being loaded * @param msgLogger the logger to which to log deserialization messages. * @return an ArgBlock instance deserialized from the stream. */ static ArgBlock load(final RecordInputStream s, final ModuleName moduleName, final CompilerMessageLogger msgLogger) throws IOException { final FieldName argName = FieldNameIO.load(s, moduleName, msgLogger); final TextBlock textBlock = TextBlock.load(s); return new ArgBlock(argName, textBlock); } } /** * A package-private abstract class for associating summary paragraphs * with a CALDocComment. * * @author Joseph Wong */ static abstract class SummaryCollector { /** * Adds a summary paragraph to the comment. * @param paragraph the summary paragraph. */ abstract void addSummaryParagraph(Paragraph paragraph); } /** * A package-private helper class for building a CALDocComment. Once a * CALDocComment is constructed, it cannot be changed since it is immutable. * * @author Joseph Wong */ static final class Builder extends SummaryCollector { /** A list of the paragraphs forming the summary of the comment. */ private final List<Paragraph> summaryParagraphs = new ArrayList<Paragraph>(); /** The description block at the start of the comment. */ private TextBlock descriptionBlock = null; /** A list of the "@author" blocks in the comment. The list is empty if there are none of these blocks. */ private final List<TextBlock> authorBlocks = new ArrayList<TextBlock>(); /** The "@version" block in the comment, or null if there is none. */ private TextBlock versionBlock = null; /** The "@deprecated" block in the comment, or null if there is none. */ private TextBlock deprecatedBlock = null; /** A list of the "@arg" blocks in the comment. The list is empty if there are none of these blocks. */ private final List<ArgBlock> argBlocks = new ArrayList<ArgBlock>(); /** The "@return" block in the comment, or null if there is none. */ private TextBlock returnBlock = null; /** a list of the "@see" module references in the comment. The list is empty if there are none of these references. */ private final List<ModuleReference> moduleRefs = new ArrayList<ModuleReference>(); /** a list of the "@see" function and class method references in the comment. The list is empty if there are none of these references. */ private final List<ScopedEntityReference> functionOrClassMethodRefs = new ArrayList<ScopedEntityReference>(); /** a list of the "@see" type constructor references in the comment. The list is empty if there are none of these references. */ private final List<ScopedEntityReference> typeConsRefs = new ArrayList<ScopedEntityReference>(); /** a list of the "@see" data constructor references in the comment. The list is empty if there are none of these references. */ private final List<ScopedEntityReference> dataConsRefs = new ArrayList<ScopedEntityReference>(); /** a list of the "@see" type class references in the comment. The list is empty if there are none of these references. */ private final List<ScopedEntityReference> typeClassRefs = new ArrayList<ScopedEntityReference>(); /** * Adds a summary paragraph to the comment. * @param paragraph the summary paragraph. */ @Override void addSummaryParagraph(final Paragraph paragraph) { summaryParagraphs.add(paragraph); } /** * Sets the description block of the comment. * @param block the description block. */ void setDescriptionBlock(final TextBlock block) { descriptionBlock = block; } /** * @return true iff the comment has a description block. */ boolean hasDescriptionBlock() { return descriptionBlock != null; } /** * Adds an "@author" block to the comment. * @param block the "@author" block. */ void addAuthorBlock(final TextBlock block) { authorBlocks.add(block); } /** * Sets the "@version" block of the comment. * @param block the "@version" block. */ void setVersionBlock(final TextBlock block) { versionBlock = block; } /** * @return true iff the comment has a "@version" block. */ boolean hasVersionBlock() { return versionBlock != null; } /** * Sets the "@deprecated" block of the comment. * @param block the "@deprecated" block. */ void setDeprecatedBlock(final TextBlock block) { deprecatedBlock = block; } /** * @return true iff the comment has a "@deprecated" block. */ boolean hasDeprecatedBlock() { return deprecatedBlock != null; } /** * Adds an "@arg" block to the comment. * @param block the "@arg" block. */ void addArgBlock(final ArgBlock block) { argBlocks.add(block); } /** * @return the number of "@arg" blocks in the comment. */ int getNArgBlocks() { return argBlocks.size(); } /** * Sets the "@return" block of the comment. * @param block the "@return" block. */ void setReturnBlock(final TextBlock block) { returnBlock = block; } /** * @return true iff the comment has a "@return" block. */ boolean hasReturnBlock() { return returnBlock != null; } /** * Adds an "@see" module reference to the comment. * @param ref the reference. */ void addModuleReference(final ModuleReference ref) { moduleRefs.add(ref); } /** * Adds an "@see" function or class method reference to the comment. * @param ref the reference. */ void addFunctionOrClassMethodReference(final ScopedEntityReference ref) { functionOrClassMethodRefs.add(ref); } /** * Adds an "@see" type constructor reference to the comment. * @param ref the reference. */ void addTypeConstructorReference(final ScopedEntityReference ref) { typeConsRefs.add(ref); } /** * Adds an "@see" data constructor reference to the comment. * @param ref the reference. */ void addDataConstructorReference(final ScopedEntityReference ref) { dataConsRefs.add(ref); } /** * Adds an "@see" type class reference to the comment. * @param ref the reference. */ void addTypeClassReference(final ScopedEntityReference ref) { typeClassRefs.add(ref); } /** * Constructs a CALDocComment based on the values of the fields. * @return a new CALDocComment instance. */ CALDocComment toComment() { TextBlock summary; if (summaryParagraphs.size() > 0) { summary = new TextBlock(summaryParagraphs); } else { summary = null; } return new CALDocComment( summary, descriptionBlock, authorBlocks.toArray(new TextBlock[0]), versionBlock, deprecatedBlock, argBlocks.toArray(new ArgBlock[0]), returnBlock, moduleRefs.toArray(new ModuleReference[0]), functionOrClassMethodRefs.toArray(new ScopedEntityReference[0]), typeConsRefs.toArray(new ScopedEntityReference[0]), dataConsRefs.toArray(new ScopedEntityReference[0]), typeClassRefs.toArray(new ScopedEntityReference[0])); } } /** * Private constructor for a CALDocComment. * * @param summary * the summary of the comment. * @param descriptionBlock * the description block at the start of the comment. * @param authorBlocks * the "@author" blocks in the comment. * @param versionBlock * the "@version" block in the comment. * @param deprecatedBlock * the "@deprecated" block in the comment. * @param argBlocks * the "@arg" blocks in the comment. * @param returnBlock * the "@return" block in the comment. * @param moduleRefs * the "@see" module references in the comment. * @param functionOrClassMethodRefs * the "@see" function and class method references in the * comment. * @param typeConsRefs * the "@see" type constructor references in the comment. * @param dataConsRefs * the "@see" data constructor references in the comment. * @param typeClassRefs * the "@see" type class references in the comment. */ private CALDocComment( final TextBlock summary, final TextBlock descriptionBlock, final TextBlock[] authorBlocks, final TextBlock versionBlock, final TextBlock deprecatedBlock, final ArgBlock[] argBlocks, final TextBlock returnBlock, final ModuleReference[] moduleRefs, final ScopedEntityReference[] functionOrClassMethodRefs, final ScopedEntityReference[] typeConsRefs, final ScopedEntityReference[] dataConsRefs, final ScopedEntityReference[] typeClassRefs) { this.summary = summary; // the summary can be null verifyArg(descriptionBlock, "descriptionBlock"); // the description block cannot be null this.descriptionBlock = descriptionBlock; verifyArrayArg(authorBlocks, "authorBlocks"); if (authorBlocks.length == 0) { this.authorBlocks = NO_TEXT_BLOCKS; } else { this.authorBlocks = authorBlocks; // no cloning because this constructor is private, and we count on the caller to have done the appropriate cloning already } this.versionBlock = versionBlock; this.deprecatedBlock = deprecatedBlock; verifyArrayArg(argBlocks, "argBlocks"); if (argBlocks.length == 0) { this.argBlocks = NO_ARG_BLOCKS; } else { this.argBlocks = argBlocks; // no cloning because this constructor is private, and we count on the caller to have done the appropriate cloning already } this.returnBlock = returnBlock; verifyArrayArg(moduleRefs, "moduleRefs"); if (moduleRefs.length == 0) { this.moduleRefs = NO_MODULE_REFS; } else { this.moduleRefs = moduleRefs; // no cloning because this constructor is private, and we count on the caller to have done the appropriate cloning already } verifyArrayArg(functionOrClassMethodRefs, "functionOrClassMethodRefs"); if (functionOrClassMethodRefs.length == 0) { this.functionOrClassMethodRefs = NO_SCOPED_ENTITY_REFS; } else { this.functionOrClassMethodRefs = functionOrClassMethodRefs; // no cloning because this constructor is private, and we count on the caller to have done the appropriate cloning already } verifyArrayArg(typeConsRefs, "typeConsRefs"); if (typeConsRefs.length == 0) { this.typeConsRefs = NO_SCOPED_ENTITY_REFS; } else { this.typeConsRefs = typeConsRefs; // no cloning because this constructor is private, and we count on the caller to have done the appropriate cloning already } verifyArrayArg(dataConsRefs, "dataConsRefs"); if (dataConsRefs.length == 0) { this.dataConsRefs = NO_SCOPED_ENTITY_REFS; } else { this.dataConsRefs = dataConsRefs; // no cloning because this constructor is private, and we count on the caller to have done the appropriate cloning already } verifyArrayArg(typeClassRefs, "typeClassRefs"); if (typeClassRefs.length == 0) { this.typeClassRefs = NO_SCOPED_ENTITY_REFS; } else { this.typeClassRefs = typeClassRefs; // no cloning because this constructor is private, and we count on the caller to have done the appropriate cloning already } } /** * Throws an exception if the array is null or has any null elements. * @param array Object[] * @param argName String name of the array for display in the error text of any generated exception * @throws NullPointerException if array is null, or any of its elements are null */ static void verifyArrayArg(final Object[] array, final String argName) { verifyArg(array, argName); for (int i = 0, nElems = array.length; i < nElems; i++) { if (array[i] == null) { throw new NullPointerException(argName + "[" + i + "] is null."); } } } /** * Throws an exception if arg is null. * @param arg Object * @param argName String name of the arg to display in the error text of any generated exception * @throws NullPointerException if 'arg' is null */ static void verifyArg(final Object arg, final String argName) { if (arg == null) { throw new NullPointerException("The argument '" + argName + "' cannot be null."); } } /** * @return the summary of the comment. */ public TextBlock getSummary() { return summary; } /** * @return the description block at the start of the comment. */ public TextBlock getDescriptionBlock() { return descriptionBlock; } /** * @return the number of "@author" blocks in the comment. */ public int getNAuthorBlocks() { return authorBlocks.length; } /** * Returns the "@author" block from the array of such blocks at the specified index. * @param index the index of the block. * @return the block at the specified index. */ public TextBlock getNthAuthorBlock(final int index) { return authorBlocks[index]; } /** * @return the "@version" block in the comment, or null if there is none. */ public TextBlock getVersionBlock() { return versionBlock; } /** * @return the "@deprecated" block in the comment, or null if there is none. */ public TextBlock getDeprecatedBlock() { return deprecatedBlock; } /** * @return the number of "@arg" blocks in the comment. */ public int getNArgBlocks() { return argBlocks.length; } /** * Returns the "@arg" block from the array of such blocks at the specified index. * @param index the index of the block. * @return the block at the specified index. */ public ArgBlock getNthArgBlock(final int index) { return argBlocks[index]; } /** * @return the "@return" block in the comment, or null if there is none. */ public TextBlock getReturnBlock() { return returnBlock; } /** * @return the number of "@see" module references in the comment. */ public int getNModuleReferences() { return moduleRefs.length; } /** * Returns the "@see" module reference block from the array of such references at the specified index. * @param index the index of the reference. * @return the reference at the specified index. */ public ModuleReference getNthModuleReference(final int index) { return moduleRefs[index]; } /** * @return the number of "@see" function and class method references in the comment. */ public int getNFunctionOrClassMethodReferences() { return functionOrClassMethodRefs.length; } /** * Returns the "@see" function or class method reference block from the array of such references at the specified index. * @param index the index of the reference. * @return the reference at the specified index. */ public ScopedEntityReference getNthFunctionOrClassMethodReference(final int index) { return functionOrClassMethodRefs[index]; } /** * @return the number of "@see" type constructor references in the comment. */ public int getNTypeConstructorReferences() { return typeConsRefs.length; } /** * Returns the "@see" type constructor reference block from the array of such references at the specified index. * @param index the index of the reference. * @return the reference at the specified index. */ public ScopedEntityReference getNthTypeConstructorReference(final int index) { return typeConsRefs[index]; } /** * @return the number of "@see" data constructor references in the comment. */ public int getNDataConstructorReferences() { return dataConsRefs.length; } /** * Returns the "@see" data constructor reference block from the array of such references at the specified index. * @param index the index of the reference. * @return the reference at the specified index. */ public ScopedEntityReference getNthDataConstructorReference(final int index) { return dataConsRefs[index]; } /** * @return the number of "@see" type class references in the comment. */ public int getNTypeClassReferences() { return typeClassRefs.length; } /** * Returns the "@see" type class reference block from the array of such references at the specified index. * @param index the index of the reference. * @return the reference at the specified index. */ public ScopedEntityReference getNthTypeClassReference(final int index) { return typeClassRefs[index]; } /** @return a string representation of this instance. */ @Override public String toString() { final StringBuilder result = new StringBuilder(); result.append("[**\n"); final int indentLevel = INDENT_LEVEL; // the summary, or null addSpacesToStringBuilder(result, indentLevel); result.append("summary = "); addMaybeNullTextBlockToStringBuilder(result, indentLevel + INDENT_LEVEL, summary); // the description block addSpacesToStringBuilder(result, indentLevel); result.append("descriptionBlock = "); addMaybeNullTextBlockToStringBuilder(result, indentLevel + INDENT_LEVEL, descriptionBlock); // the author blocks addSpacesToStringBuilder(result, indentLevel); result.append("authorBlocks = [\n"); for (final TextBlock authorBlock : authorBlocks) { authorBlock.toStringBuilder(result, indentLevel + INDENT_LEVEL); } addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); // the version block, or null addSpacesToStringBuilder(result, indentLevel); result.append("versionBlock = "); addMaybeNullTextBlockToStringBuilder(result, indentLevel + INDENT_LEVEL, versionBlock); // the deprecated block, or null addSpacesToStringBuilder(result, indentLevel); result.append("deprecatedBlock = "); addMaybeNullTextBlockToStringBuilder(result, indentLevel + INDENT_LEVEL, deprecatedBlock); // the arg blocks addSpacesToStringBuilder(result, indentLevel); result.append("argBlocks = [\n"); for (final ArgBlock argBlock : argBlocks) { argBlock.toStringBuilder(result, indentLevel + INDENT_LEVEL); } addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); // the return block, or null addSpacesToStringBuilder(result, indentLevel); result.append("returnBlock = "); addMaybeNullTextBlockToStringBuilder(result, indentLevel + INDENT_LEVEL, returnBlock); // the module references addSpacesToStringBuilder(result, indentLevel); result.append("moduleRefs = ["); for (final ModuleReference moduleRef : moduleRefs) { result.append('\n'); addSpacesToStringBuilder(result, indentLevel + INDENT_LEVEL); result.append(moduleRef); } result.append('\n'); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); // the function and class method references addSpacesToStringBuilder(result, indentLevel); result.append("functionOrClassMethodRefs = ["); for (final ScopedEntityReference functionOrClassMethodRef : functionOrClassMethodRefs) { result.append('\n'); addSpacesToStringBuilder(result, indentLevel + INDENT_LEVEL); result.append(functionOrClassMethodRef); } result.append('\n'); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); // the type constructor references addSpacesToStringBuilder(result, indentLevel); result.append("typeConsRefs = ["); for (final ScopedEntityReference typeConsRef : typeConsRefs) { result.append('\n'); addSpacesToStringBuilder(result, indentLevel + INDENT_LEVEL); result.append(typeConsRef); } result.append('\n'); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); // the data constructor references addSpacesToStringBuilder(result, indentLevel); result.append("dataConsRefs = ["); for (final ScopedEntityReference dataConsRef : dataConsRefs) { result.append('\n'); addSpacesToStringBuilder(result, indentLevel + INDENT_LEVEL); result.append(dataConsRef); } result.append('\n'); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); // the type class references addSpacesToStringBuilder(result, indentLevel); result.append("typeClassRefs = ["); for (final ScopedEntityReference typeClassRef : typeClassRefs) { result.append('\n'); addSpacesToStringBuilder(result, indentLevel + INDENT_LEVEL); result.append(typeClassRef); } result.append('\n'); addSpacesToStringBuilder(result, indentLevel); result.append("]\n"); result.append("*]\n"); return result.toString(); } /** * Appends the string representation of a TextBlock to the given StringBuilder. The TextBlock can be null. * @param buffer the StringBuilder to fill. * @param indentLevel the indent level to use. * @param block the TextBlock whose string representation is to be added to the buffer. */ private void addMaybeNullTextBlockToStringBuilder(final StringBuilder buffer, final int indentLevel, final TextBlock block) { if (block == null) { buffer.append("null\n"); } else { buffer.append("\n"); block.toStringBuilder(buffer, indentLevel); } } /** * 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(); } /** * Write this instance of CALDocComment to the RecordOutputStream. * @param s the RecordOutputStream to be written to. */ void write(final RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CALDOC_COMMENT, serializationSchema); // the description block descriptionBlock.write(s); // the flags final boolean hasVersionBlock = (versionBlock != null); final boolean hasDeprecatedBlock = (deprecatedBlock != null); final boolean hasReturnBlock = (returnBlock != null); final boolean hasAnySeeBlocks = moduleRefs.length > 0 || functionOrClassMethodRefs.length > 0 || typeConsRefs.length > 0 || dataConsRefs.length > 0 || typeClassRefs.length > 0; final boolean hasSummary = (summary != null); final byte[] flags = RecordOutputStream.booleanArrayToBitArray(new boolean[] {hasVersionBlock, hasDeprecatedBlock, hasReturnBlock, hasAnySeeBlocks, hasSummary}); if (flags.length != 1) { throw new IOException("Unexpected number of flag bytes saving CALDocComment."); } s.writeByte(flags[0]); // the summary, or null if (hasSummary) { summary.write(s); } // the author blocks s.writeIntCompressed(authorBlocks.length); for (final TextBlock authorBlock : authorBlocks) { authorBlock.write(s); } // the version block, or null if (hasVersionBlock) { versionBlock.write(s); } // the deprecated block, or null if (hasDeprecatedBlock) { deprecatedBlock.write(s); } // the arg blocks s.writeIntCompressed(argBlocks.length); for (final ArgBlock argBlock : argBlocks) { argBlock.write(s); } // the return block, or null if (hasReturnBlock) { returnBlock.write(s); } // only write out the arrays of references if at least one of them is non-empty // (this optimizes the common case where a CALDoc comment has no "@see" references) if (hasAnySeeBlocks) { // the module references s.writeIntCompressed(moduleRefs.length); for (final ModuleReference moduleRef : moduleRefs) { moduleRef.write(s); } // the function and class method references s.writeIntCompressed(functionOrClassMethodRefs.length); for (final ScopedEntityReference functionOrClassMethodRef : functionOrClassMethodRefs) { functionOrClassMethodRef.write(s); } // the type constructor references s.writeIntCompressed(typeConsRefs.length); for (final ScopedEntityReference typeConsRef : typeConsRefs) { typeConsRef.write(s); } // the data constructor references s.writeIntCompressed(dataConsRefs.length); for (final ScopedEntityReference dataConsRef : dataConsRefs) { dataConsRef.write(s); } // the type class references s.writeIntCompressed(typeClassRefs.length); for (final ScopedEntityReference typeClassRef : typeClassRefs) { typeClassRef.write(s); } } s.endRecord(); } /** * Load an instance of CALDocComment from the RecordInputStream. * Read position will be before the record header. * @param s the RecordInputStream to be read from. * @param moduleName the name of the module being loaded * @param msgLogger the logger to which to log deserialization messages. * @return a CALDocComment instance deserialized from the stream. */ static CALDocComment load(final RecordInputStream s, final ModuleName moduleName, final CompilerMessageLogger msgLogger) throws IOException { final RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.CALDOC_COMMENT); if (rhi == null) { throw new IOException ("Unable to find CALDoc record header."); } // the description block final TextBlock descriptionBlock = TextBlock.load(s); // the flags final byte flags = s.readByte(); final boolean hasVersionBlock = (flags & 0x01) > 0; final boolean hasDeprecatedBlock = (flags & 0x02) > 0; final boolean hasReturnBlock = (flags & 0x04) > 0; final boolean hasAnySeeBlocks = (flags & 0x08) > 0; final boolean hasSummary = (flags & 0x10) > 0; // the summary, or null final TextBlock summary = hasSummary ? TextBlock.load(s) : null; // the author blocks final int nAuthorBlocks = s.readIntCompressed(); final TextBlock[] authorBlocks = new TextBlock[nAuthorBlocks]; for (int i = 0; i < nAuthorBlocks; i++) { authorBlocks[i] = TextBlock.load(s); } // the version block, or null final TextBlock versionBlock = hasVersionBlock ? TextBlock.load(s) : null; // the deprecated block, or null final TextBlock deprecatedBlock = hasDeprecatedBlock ? TextBlock.load(s) : null; // the arg blocks final int nArgBlocks = s.readIntCompressed(); final ArgBlock[] argBlocks = new ArgBlock[nArgBlocks]; for (int i = 0; i < nArgBlocks; i++) { argBlocks[i] = ArgBlock.load(s, moduleName, msgLogger); } // the return block, or null final TextBlock returnBlock = hasReturnBlock ? TextBlock.load(s) : null; // the arrays of references were only written if at least one of them was non-empty // (this optimizes the common case where a CALDoc comment has no "@see" references) // // so only proceed to reading them if they are indeed in the stream // (as indicated by the hasAnySeeBlocks flag) ModuleReference[] moduleRefs; ScopedEntityReference[] functionOrClassMethodRefs; ScopedEntityReference[] dataConsRefs; ScopedEntityReference[] typeConsRefs; ScopedEntityReference[] typeClassRefs; if (hasAnySeeBlocks) { // the module references final int nModuleRefs = s.readIntCompressed(); moduleRefs = new ModuleReference[nModuleRefs]; for (int i = 0; i < nModuleRefs; i++) { moduleRefs[i] = ModuleReference.load(s); } // the function and class method references final int nFunctionOrClassMethodRefs = s.readIntCompressed(); functionOrClassMethodRefs = new ScopedEntityReference[nFunctionOrClassMethodRefs]; for (int i = 0; i < nFunctionOrClassMethodRefs; i++) { functionOrClassMethodRefs[i] = ScopedEntityReference.load(s); } // the type constructor references final int nTypeConsRefs = s.readIntCompressed(); typeConsRefs = new ScopedEntityReference[nTypeConsRefs]; for (int i = 0; i < nTypeConsRefs; i++) { typeConsRefs[i] = ScopedEntityReference.load(s); } // the data constructor references final int nDataConsRefs = s.readIntCompressed(); dataConsRefs = new ScopedEntityReference[nDataConsRefs]; for (int i = 0; i < nDataConsRefs; i++) { dataConsRefs[i] = ScopedEntityReference.load(s); } // the type class references final int nTypeClassRefs = s.readIntCompressed(); typeClassRefs = new ScopedEntityReference[nTypeClassRefs]; for (int i = 0; i < nTypeClassRefs; i++) { typeClassRefs[i] = ScopedEntityReference.load(s); } } else { // there are no "@see" references in this comment, so all the arrays of references should be empty moduleRefs = new ModuleReference[0]; functionOrClassMethodRefs = new ScopedEntityReference[0]; typeConsRefs = new ScopedEntityReference[0]; dataConsRefs = new ScopedEntityReference[0]; typeClassRefs = new ScopedEntityReference[0]; } s.skipRestOfRecord(); return new CALDocComment( summary, descriptionBlock, authorBlocks, versionBlock, deprecatedBlock, argBlocks, returnBlock, moduleRefs, functionOrClassMethodRefs, typeConsRefs, dataConsRefs, typeClassRefs); } }