Class AbstractCodeWriter<T extends AbstractCodeWriter<T>>

  • Direct Known Subclasses:
    CodeWriter, SimpleCodeWriter, SymbolWriter, SymbolWriter

    public abstract class AbstractCodeWriter<T extends AbstractCodeWriter<T>>
    extends java.lang.Object
    Helper class for generating code.

    An AbstractCodeWriter can be used to write basically any kind of code, including whitespace sensitive and brace-based.

    The following example generates some Python code:

    
     SimpleCodeWriter writer = new SimpleCodeWriter();
     writer.write("def Foo(str):")
           .indent()
           .write("print str");
     String code = writer.toString();
     

    Code interpolation

    The write(java.lang.Object, java.lang.Object...), openBlock(java.lang.String, java.lang.Object...), and closeBlock(java.lang.String, java.lang.Object...) methods take a code expression and a variadic list of arguments that are interpolated into the expression. Consider the following call to write:

    
     SimpleCodeWriter = new SimpleCodeWriter();
     writer.write("Hello, $L", "there!");
     String code = writer.toString();
     

    In the above example, $L is interpolated and replaced with the relative argument there!.

    An AbstractCodeWriter supports three kinds of interpolations: relative, positional, and named. Each of these kinds of interpolations pass a value to a formatter.

    Formatters

    Formatters are named functions that accept an object as input, accepts a string that contains the current indentation (it can be ignored if not useful), and returns a string as output. AbstractCodeWriter registers two built-in formatters:

    • L (literal): Outputs a literal value of an Object using the following implementation: (1) A null value is formatted as "". (2) An empty Optional value is formatted as "". (3) A non-empty Optional value is recursively formatted using the value inside of the Optional. (3) All other valeus are formatted using the result of calling String.valueOf(java.lang.Object).
    • C (call): Runs a Runnable or Consumer argument that is expected to write to the same writer. Any text written to the AbstractCodeWriter inside of the Runnable is used as the value of the argument. Note that a single trailing newline is removed from the captured text. If a Runnable is provided, it is required to have a reference to the AbstractCodeWriter. A Consumer is provided a reference to the AbstractCodeWriter as a single argument.
      
           SimpleCodeWriter writer = new SimpleCodeWriter();
           writer.write("Hello, $C.", (Runnable) () -> writer.write("there"));
           assert(writer.toString().equals("Hello, there.\n"));
           
    • S (string): Adds double quotes around the result of formatting a value first using the default literal "L" implementation described above and then wrapping the value in an escaped string safe for use in Java according to https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6. This formatter can be overridden if needed to support other programming languages.

    Custom formatters can be registered using putFormatter(char, java.util.function.BiFunction<java.lang.Object, java.lang.String, java.lang.String>). Custom formatters can be used only within the state they were added. Because states inherit the formatters of parent states, adding a formatter to the root state of the AbstractCodeWriter allows the formatter to be used in any state.

    The identifier given to a formatter must match one of the following characters:

        "!" / "#" / "%" / "&" / "*" / "+" / "," / "-" / "." / "/" / ";"
      / "=" / "?" / "@" / "A" / "B" / "C" / "D" / "E" / "F" / "G" / "H"
      / "I" / "J" / "K" / "L" / "M" / "N" / "O" / "P" / "Q" / "R" / "S"
      / "T" / "U" / "V" / "W" / "X" / "Y" / "Z" / "^" / "_" / "`" / "~"
     

    Relative parameters

    Placeholders in the form of "$" followed by a formatter name are treated as relative parameters. The first instance of a relative parameter interpolates the first positional argument, the second the second, etc.

    
     SimpleCodeWriter = new SimpleCodeWriter();
     writer.write("$L $L $L", "a", "b", "c");
     System.out.println(writer.toString());
     // Outputs: "a b c"
     

    All relative arguments must be used as part of an expression and relative interpolation cannot be mixed with positional variables.

    Positional parameters

    Placeholders in the form of "$" followed by a positive number, followed by a formatter name are treated as positional parameters. The number refers to the 1-based index of the argument to interpolate.

    
     SimpleCodeWriter = new SimpleCodeWriter();
     writer.write("$1L $2L $3L, $3L $2L $1L", "a", "b", "c");
     System.out.println(writer.toString());
     // Outputs: "a b c c b a"
     

    All positional arguments must be used as part of an expression and relative interpolation cannot be mixed with positional variables.

    Named parameters

    Named parameters are parameters that take a value from the context bag of the current state or using getters of the CodeSection associated with the current state. They take the following form $<variable>:<formatter>, where <variable> is a string that starts with a lowercase letter, followed by any number of [A-Za-z0-9_#$.] characters, and <formatter> is the name of a formatter.

    
     SimpleCodeWriter = new SimpleCodeWriter();
     writer.putContext("foo", "a");
     writer.putContext("baz.bar", "b");
     writer.write("$foo:L $baz.bar:L");
     System.out.println(writer.toString());
     // Outputs: "a b"
     

    The context bag is checked first, and then if the parameter is not found, getters of the currently associated CodeSection are checked. If a getter is found that matches the key exactly, then that getter is invoked and used as the named parameter. If a getter is found that matches "get" + uppercase_first_letter(key), then that getter is used as the named parameter.

    Escaping interpolation

    You can escape the "$" character using two "$$".

    
     SimpleCodeWriter = new SimpleCodeWriter().write("$$L");
     System.out.println(writer.toString());
     // Outputs: "$L"
     

    Custom expression characters

    The character used to start a code block expression can be customized to make it easier to write code that makes heavy use of $. The default character used to start an expression is, $, but this can be changed for the current state of the AbstractCodeWriter by calling setExpressionStart(char). A custom start character can be escaped using two start characters in a row. For example, given a custom start character of #, # can be escaped using ##.

    
     SimpleCodeWriter = new SimpleCodeWriter();
     writer.setExpressionStart('#');
     writer.write("#L ##L $L", "hi");
     System.out.println(writer.toString());
     // Outputs: "hi #L $L"
     
    The start character cannot be set to ' ' or '\n'.

    Opening and closing blocks

    AbstractCodeWriter provides a short cut for opening code blocks that have an opening an closing delimiter (for example, "{" and "}") and that require indentation inside of the delimiters. Calling openBlock(java.lang.String, java.lang.Object...) and providing the opening statement will write and format a line followed by indenting one level. Calling closeBlock(java.lang.String, java.lang.Object...) will first dedent and then print a formatted statement.

    
     SimpleCodeWriter = new SimpleCodeWriter()
           .openBlock("if ($L) {", someValue)
           .write("System.out.println($S);", "Hello!")
           .closeBlock("}");
     

    The above example outputs (assuming someValue is equal to "foo"):

    
     if (foo) {
         System.out.println("Hello!");
     }
     

    Pushing and popping state

    AbstractCodeWriter can maintain a stack of transformation states, including the text used to indent, a prefix to add before each line, newline character, the number of times to indent, a map of context values, whether or not whitespace is trimmed from the end of newlines, whether or not the automatic insertion of newlines is disabled, the character used to start code expressions (defaults to $), and formatters. State can be pushed onto the stack using pushState() which copies the current state. Mutations can then be made to the top-most state of the AbstractCodeWriter and do not affect previous states. The previous transformation state of the AbstractCodeWriter can later be restored using popState().

    AbstractCodeWriter is stateful, and a prefix can be added before each line. This is useful for doing things like create Javadoc strings:

    
     SimpleCodeWriter = new SimpleCodeWriter();
     writer
           .pushState()
           .write("/**")
           .setNewlinePrefix(" * ")
           .write("This is some docs.")
           .write("And more docs.\n\n\n")
           .write("Foo.")
           .popState()
           .write(" *\/");
     

    The above example outputs:

    
     /**
      * This is some docs.
      * And more docs.
      *
      * Foo.
      *\/
    
       ^ Minus this escape character
     

    AbstractCodeWriter maintains some global state that is not affected by pushState() and popState():

    • The number of successive blank lines to trim.
    • Whether or not a trailing newline is inserted or removed from the result of converting the AbstractCodeWriter to a string.

    Limiting blank lines

    Many coding standards recommend limiting the number of successive blank lines. This can be handled automatically by AbstractCodeWriter by calling trimBlankLines. The removal of blank lines is handled when the AbstractCodeWriter is converted to a string. Lines that consist solely of spaces or tabs are considered blank. If the number of blank lines exceeds the allowed threshold, they are omitted from the result.

    Trimming trailing spaces

    Trailing spaces can be automatically trimmed from each line by calling trimTrailingSpaces().

    In the following example:

    
     SimpleCodeWriter = new SimpleCodeWriter();
     String result = writer.trimTrailingSpaces().write("hello  ").toString();
     

    The value of result contains "hello"

    Extending AbstractCodeWriter

    AbstractCodeWriter can be extended to add functionality for specific programming languages. For example, Java specific code generator could be implemented that makes it easier to write Javadocs.

    
     class JavaCodeWriter extends AbstractCodeWriter<JavaCodeWriter> {
         public JavaCodeWriter javadoc(Runnable runnable) {
             pushState()
             write("/**")
             setNewlinePrefix(" * ")
             runnable.run();
             popState()
             write(" *\/");
             return this;
         }
     }
    
     JavaCodeWriter writer = new JavaCodeWriter();
     writer.javadoc(() -> {
         writer.write("This is an example.");
     });
     

    Code sections

    Named sections can be marked in the code writer that can be intercepted and modified by section interceptors. This gives the AbstractCodeWriter a lightweight extension system for augmenting generated code.

    A section of code can be captured using a block state or an inline section. Section names must match the following regular expression: ^[a-z]+[a-zA-Z0-9_.#$]*$.

    Block states

    A block section is created by passing a string to pushState(). This string gives the state a name and captures all of the output written inside of this state to an internal buffer. This buffer is then passed to each registered interceptor for that name. These interceptors can choose to use the default contents of the section or emit entirely different content. Interceptors are expected to make calls to the AbstractCodeWriter in order to emit content. Interceptors need to have a reference to the AbstractCodeWriter as one is not provided to them when they are invoked. Interceptors are invoked in the order in which they are added to the CodeBuilder.

    
     SimpleCodeWriter = new SimpleCodeWriter();
     writer.onSection("example", text -> writer.write("Intercepted: " + text));
     writer.pushState("example");
     writer.write("Original contents");
     writer.popState();
     System.out.println(writer.toString());
     // Outputs: "Intercepted: Original contents\n"
     

    Inline sections

    An inline section is created using a special CodeWriter interpolation format that appends "@" followed by the section name. Inline sections are function just like block sections, but they can appear inline inside of other content passed in calls to write(java.lang.Object, java.lang.Object...). An inline section that makes no calls to write(java.lang.Object, java.lang.Object...) expands to an empty string.

    Inline sections are created in a format string inside of braced arguments after the formatter. For example, ${L@foo} is an inline section that uses the literal "L" value of a relative argument as the default value of the section and allows AbstractCodeWriter registered for the "foo" section to make calls to the CodeWriter to modify the section.

    
     SimpleCodeWriter = new SimpleCodeWriter();
     writer.onSection("example", text -> writer.write("Intercepted: " + text));
     writer.write("Leading text...${L@example}...Trailing text...", "foo");
     System.out.println(writer.toString());
     // Outputs: "Leading text...Intercepted: foo...Trailing text...\n"
     
    Inline sections are useful for composing sets or lists from any code with access to AbstractCodeWriter:
    
     SimpleCodeWriter = new SimpleCodeWriter();
     writer.onSection("example", text -> writer.write(text + "1, "));
     writer.onSection("example", text -> writer.write(text + "2, "));
     writer.onSection("example", text -> writer.write(text + "3"));
     writer.write("[${L@example}]", "");
     System.out.println(writer.toString());
     // Outputs: "[1, 2, 3]\n"
     

    Inline block alignment

    The long-form interpolation syntax allows for inline block alignment, which means that any newline emitted by the interpolation is automatically aligned with the column of where the interpolation occurs. Inline block indentation is defined by preceding the closing '}' character with '|' (e.g., ${L|}):

    
     SimpleCodeWriter = new SimpleCodeWriter();
     writer.write("$L: ${L|}", "Names", "Bob\nKaren\nLuis");
     System.out.println(writer.toString());
     // Outputs: "Names: Bob\n       Karen\n       Luis\n"
     

    Alignment occurs either statically or dynamically based on the characters that come before interpolation. If all of the characters in the literal template that come before interpolation are spaces and tabs, then those characters are used when indenting newlines. Otherwise, the number of characters written as the template result that come before interpolation are used when indenting (this takes into account any interpolation that may precede block interpolation).

    Block interpolation is particularly used when using text blocks in Java because it allows templates to more closely match their end result.

    
     // Assume handleNull, handleA, and handleB are Runnable.
     writer.write("""
         if (foo == null) {
             ${C|}
         } else if (foo == "a") {
             ${C|}
         } else if (foo == "b") {
             ${C|}
         }
         """,
         handleNull,
         handleA,
         handleB);
     

    Template conditionals and loops

    AbstractCodeWriter is a lightweight template engine that supports conditional blocks and loops.

    Conditional blocks

    Conditional blocks are defined using the following syntax:

    
     ${?foo}
     Foo is set: ${foo:L}
     ${/foo}
     

    Assuming foo is truthy and set to "hi", then the above template outputs: "Foo is set: hi"

    In the above example, "?" indicates that the expression is a conditional block to check if the named parameter "foo" is truthy. If it is, then the contents of the block up to the matching closing block, ${/foo}, are evaluated. If the condition is not satisfied, then contents of the block are skipped.

    You can check if a named property is falsey using "^":

    
     ${^foo}
     Foo is not set
     ${/foo}
     

    Assuming foo is set to "hi", then the above template outputs nothing. If foo is falsey, then the above template output "Foo is not set".

    Truthy and falsey

    The following values are considered falsey:

    • values that are not found
    • null values
    • false
    • empty String
    • empty Iterable
    • empty Map
    • empty Optional

    Values that are not falsey are considered truthy.

    Loops

    Loops can be created to repeat a section of a template for each value stored in a list or each each key value pair stored in a map. Loops are created using "#".

    The following template with a "foo" value of {"key1": "a", "key2": "b", "key3": "c"}:

    
     ${#foo}
     - ${key:L}: ${value:L} (first: ${key.first:L}, last: ${key.last:L})
     ${/foo}
     

    Evaluates to:

    
     - key1: a (first: true, last: false)
     - key2: b (first: false, last: false)
     - key3: c (first: false, last: true)
     

    Each iteration of the loop pushes a new state in the writer that sets the following context properties:

    • key: contains the current 0-based index of an iterator or the current key of a map entry
    • value: contains the current value of an iterator or current value of a map entry
    • key.first: set to true if the loop is on the first iteration
    • key.false: set to true if the loop is on the last iteration

    A custom variable name can be used in loops. For example:

    
     ${#foo as key1, value1}
         - ${key1:L}: ${value1:L} (first: ${key1.first:L}, last: ${key1.last:L})
     ${/foo}
     

    Whitespace control

    Conditional blocks that occur on lines that only contain whitespace are not written to the template output. For example, if the condition in the following template evaluates to falsey, then the template expands to an empty string:

    
     ${?foo}
     Foo is set: ${foo:L}
     ${/foo}
     

    Whitespace that comes before an expression can be removed by putting "~" at the beginning of an expression.

    Assuming that the first positional argument is "hi":

    
     Greeting:
         ${~L}
     

    Expands to:

    
     Greeting:hi
     

    Whitespace that comes after an expression can be removed by adding "~" to the end of the expression:

    
     ${L~}
    
     .
     

    Expands to:

    
     hi.
     

    Leading whitespace cannot be removed when using inline block alignment ('|'). The following is invalid:

    
     ${~C|}
     
    • Constructor Summary

      Constructors 
      Constructor Description
      AbstractCodeWriter()
      Creates a new SimpleCodeWriter that uses "\n" for a newline, four spaces for indentation, does not strip trailing whitespace, does not flatten multiple successive blank lines into a single blank line, and adds no trailing new line.
    • Method Summary

      All Methods Static Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      T call​(java.lang.Runnable task)
      Allows calling out to arbitrary code for things like looping or conditional writes without breaking method chaining.
      T closeBlock​(java.lang.String textAfterNewline, java.lang.Object... args)
      Closes a block of syntax by writing a newline, dedenting, then writing text.
      java.util.function.Consumer<T> consumer​(java.util.function.Consumer<T> consumer)
      A simple helper method that makes it easier to invoke the built-in C (call) formatter using a Consumer where T is the specific type of AbstractCodeWriter.
      void copySettingsFrom​(AbstractCodeWriter<T> other)
      Copies settings from the given AbstractCodeWriter into this AbstractCodeWriter.
      T dedent()
      Removes one level of indentation from all lines.
      T dedent​(int levels)
      Removes a specific number of indentations from all lines.
      T disableNewlines()
      Disables the automatic appending of newlines in the current state.
      T enableNewlines()
      Enables the automatic appending of newlines in the current state.
      T enableStackTraceComments​(boolean enableStackTraceComments)
      T ensureNewline()
      Ensures that the last text written to the writer was a newline as defined in the current state and inserts one if necessary.
      java.lang.String format​(java.lang.Object content, java.lang.Object... args)
      Creates a formatted string using formatter expressions and variadic arguments.
      static java.lang.String formatLiteral​(java.lang.Object value)
      Provides the default functionality for formatting literal values.
      protected java.lang.String formatWithStackTraceElement​(java.lang.String content, java.lang.StackTraceElement element, boolean inline)
      Formats content for the given stack frame.
      java.lang.Object getContext​(java.lang.String key)
      Gets a named contextual key-value pair from the current state or any parent states.
      <C> C getContext​(java.lang.String key, java.lang.Class<C> type)
      Gets a named context key-value pair from the current state and casts the value to the given type.
      CodeWriterDebugInfo getDebugInfo()
      Gets debug information about the current state of the AbstractCodeWriter, including the path to the current state as returned by getStateDebugPath(), and up to the last two lines of text written to the AbstractCodeWriter.
      CodeWriterDebugInfo getDebugInfo​(int numberOfContextLines)
      Gets debug information about the current state of the AbstractCodeWriter.
      char getExpressionStart()
      Get the expression start character of the current state.
      int getIndentLevel()
      Gets the indentation level of the current state.
      java.lang.String getIndentText()
      Gets the text used for indentation (defaults to four spaces).
      boolean getInsertTrailingNewline()
      Checks if the AbstractCodeWriter inserts a trailing newline (if necessary) when converted to a string.
      java.lang.String getNewline()
      Gets the character used to represent newlines in the current state.
      java.lang.String getNewlinePrefix()
      Gets the prefix to prepend to every line after a new line is added (except for an inserted trailing newline).
      int getTrimBlankLines()
      Returns the number of allowed consecutive newlines that are not trimmed by the AbstractCodeWriter when written to a string.
      boolean getTrimTrailingSpaces()
      Returns true if the trailing spaces in the current state are trimmed.
      T indent()
      Indents all text one level.
      T indent​(int levels)
      Indents all text a specific number of levels.
      T injectSection​(CodeSection section)
      Creates a section that contains no content used to allow CodeInterceptors to inject content at specific locations.
      T insertTrailingNewline()
      Configures the AbstractCodeWriter to always append a newline at the end of the text if one is not already present.
      T insertTrailingNewline​(boolean trailingNewline)
      Configures the AbstractCodeWriter to always append a newline at the end of the text if one is not already present.
      protected boolean isStackTraceRelevant​(java.lang.StackTraceElement e)
      Tests if the given StackTraceElement is relevant for a comment used when writing debug information before calls to write.
      T onSection​(java.lang.String sectionName, java.util.function.Consumer<java.lang.Object> interceptor)
      Registers a function that intercepts the contents of a section and writes to the AbstractCodeWriter with the updated contents.
      <S extends CodeSection>
      T
      onSection​(CodeInterceptor<S,​T> interceptor)
      Intercepts a section of code emitted for a strongly typed CodeSection.
      T openBlock​(java.lang.String textBeforeNewline, java.lang.Object... args)
      Opens a block of syntax by writing text, a newline, then indenting.
      T openBlock​(java.lang.String textBeforeNewline, java.lang.String textAfterNewline, java.lang.Object[] args, java.lang.Runnable f)
      Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
      T openBlock​(java.lang.String textBeforeNewline, java.lang.String textAfterNewline, java.lang.Object arg1, java.lang.Object arg2, java.lang.Object arg3, java.lang.Object arg4, java.lang.Object arg5, java.lang.Runnable f)
      Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
      T openBlock​(java.lang.String textBeforeNewline, java.lang.String textAfterNewline, java.lang.Object arg1, java.lang.Object arg2, java.lang.Object arg3, java.lang.Object arg4, java.lang.Runnable f)
      Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
      T openBlock​(java.lang.String textBeforeNewline, java.lang.String textAfterNewline, java.lang.Object arg1, java.lang.Object arg2, java.lang.Object arg3, java.lang.Runnable f)
      Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
      T openBlock​(java.lang.String textBeforeNewline, java.lang.String textAfterNewline, java.lang.Object arg1, java.lang.Object arg2, java.lang.Runnable f)
      Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
      T openBlock​(java.lang.String textBeforeNewline, java.lang.String textAfterNewline, java.lang.Object arg1, java.lang.Runnable f)
      Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
      T openBlock​(java.lang.String textBeforeNewline, java.lang.String textAfterNewline, java.lang.Runnable f)
      Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
      T popState()
      Pops the current AbstractCodeWriter state from the state stack.
      T pushFilteredState​(java.util.function.Function<java.lang.String,​java.lang.String> filter)
      Pushes an anonymous named state that is always passed through the given filter function before being written to the writer.
      T pushState()
      Copies and pushes the current state to the state stack.
      T pushState​(java.lang.String sectionName)
      Copies and pushes the current state to the state stack using a named state that can be intercepted by functions registered with onSection(CodeInterceptor).
      T pushState​(CodeSection section)
      Pushes a strongly typed section extension point.
      T putContext​(java.lang.String key, java.lang.Object value)
      Adds a named key-value pair to the context of the current state.
      T putContext​(java.util.Map<java.lang.String,​java.lang.Object> mappings)
      Adds a map of named key-value pair to the context of the current state.
      T putFormatter​(char identifier, java.util.function.BiFunction<java.lang.Object,​java.lang.String,​java.lang.String> formatFunction)
      Adds a custom formatter expression to the current state of the AbstractCodeWriter.
      T removeContext​(java.lang.String key)
      Removes a named key-value pair from the context of the current state.
      T setExpressionStart​(char expressionStart)
      T setIndentText​(java.lang.String indentText)
      Sets the text used for indentation (defaults to four spaces).
      T setNewline​(char newline)
      Sets the character used to represent newlines in the current state ("\n" is the default).
      T setNewline​(java.lang.String newline)
      Sets the character used to represent newlines in the current state ("\n" is the default).
      T setNewlinePrefix​(java.lang.String newlinePrefix)
      Sets a prefix to prepend to every line after a new line is added (except for an inserted trailing newline).
      java.lang.String toString()
      Gets the contents of the generated code.
      T trimBlankLines()
      Ensures that no more than one blank line occurs in succession.
      T trimBlankLines​(int trimBlankLines)
      Ensures that no more than the given number of newlines can occur in succession, removing consecutive newlines that exceed the given threshold.
      T trimTrailingSpaces()
      Enables the trimming of trailing spaces on a line.
      T trimTrailingSpaces​(boolean trimTrailingSpaces)
      Configures if trailing spaces on a line are removed.
      T unwrite​(java.lang.Object content, java.lang.Object... args)
      Remove the most recent text written to the AbstractCodeWriter if and only if the last written text is exactly equal to the given expanded content string.
      T write​(java.lang.Object content, java.lang.Object... args)
      Writes text to the AbstractCodeWriter and appends a newline.
      T writeInline​(java.lang.Object content, java.lang.Object... args)
      Writes text to the AbstractCodeWriter without appending a newline.
      T writeInlineWithNoFormatting​(java.lang.Object content)
      Writes inline text to the AbstractCodeWriter with no formatting.
      T writeOptional​(java.lang.Object content)
      Optionally writes text to the AbstractCodeWriter and appends a newline if a value is present.
      T writeWithNoFormatting​(java.lang.Object content)
      Writes text to the AbstractCodeWriter and appends a newline.
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    • Constructor Detail

      • AbstractCodeWriter

        public AbstractCodeWriter()
        Creates a new SimpleCodeWriter that uses "\n" for a newline, four spaces for indentation, does not strip trailing whitespace, does not flatten multiple successive blank lines into a single blank line, and adds no trailing new line.
    • Method Detail

      • copySettingsFrom

        public void copySettingsFrom​(AbstractCodeWriter<T> other)
        Copies settings from the given AbstractCodeWriter into this AbstractCodeWriter.

        The settings of the other AbstractCodeWriter will overwrite both global and state-based settings of this AbstractCodeWriter.

        Stateful settings of the other AbstractCodeWriter like formatters, interceptors, and context are flattened and then copied into the current state of this AbstractCodeWriter. Any conflicts between formatters, interceptors, or context of the current writer are overwritten by the other writer. The stack of states and the contents written to other are not copied.

        
         SimpleCodeWriter a = new SimpleCodeWriter();
         a.setExpressionStart('#');
        
         SimpleCodeWriter b = new SimpleCodeWriter();
         b.copySettingsFrom(a);
        
         assert(b.getExpressionStart() == '#');
         
        Parameters:
        other - CodeWriter to copy settings from.
      • formatLiteral

        public static java.lang.String formatLiteral​(java.lang.Object value)
        Provides the default functionality for formatting literal values.

        This formatter is registered by default as the literal "L" formatter, and is called in the default string "S" formatter before escaping any characters in the string.

        • null: Formatted as an empty string.
        • Empty Optional: Formatted as an empty string.
        • Optional with value: Formatted as the formatted value in the optional.
        • Everything else: Formatted as the result of String.valueOf(java.lang.Object).
        Parameters:
        value - Value to format.
        Returns:
        Returns the formatted value.
      • putFormatter

        public T putFormatter​(char identifier,
                              java.util.function.BiFunction<java.lang.Object,​java.lang.String,​java.lang.String> formatFunction)
        Adds a custom formatter expression to the current state of the AbstractCodeWriter.

        The provided identifier string must match the following ABNF:

         %x21-23    ; ( '!' - '#' )
         / %x25-2F  ; ( '%' - '/' )
         / %x3A-60  ; ( ':' - '`' )
         / %x7B-7E  ; ( '{' - '~' )
         
        Parameters:
        identifier - Formatter identifier to associate with this formatter.
        formatFunction - Formatter function that formats the given object as a String. The formatter is give the value to format as an object (use .toString to access the string contents) and the current indentation string of the AbstractCodeWriter.
        Returns:
        Returns self.
      • setExpressionStart

        public T setExpressionStart​(char expressionStart)
        Sets the character used to start expressions in the current state when calling write(java.lang.Object, java.lang.Object...), writeInline(java.lang.Object, java.lang.Object...), openBlock(java.lang.String, java.lang.Object...), etc.

        By default, $ is used to start expressions (for example $L. However, some programming languages frequently give syntactic meaning to $, making this an inconvenient syntactic character for the AbstractCodeWriter. In these cases, the character used to start a AbstractCodeWriter expression can be changed. Just like $, the custom start character can be escaped using two subsequent start characters (e.g., $$).

        Parameters:
        expressionStart - Character to use to start expressions.
        Returns:
        Returns self.
      • getExpressionStart

        public char getExpressionStart()
        Get the expression start character of the current state.

        This value should not be cached and reused across pushed and popped states. This value is "$" by default, but it can be changed using setExpressionStart(char).

        Returns:
        Returns the expression start char of the current state.
      • toString

        public java.lang.String toString()
        Gets the contents of the generated code.

        The result will have an appended newline if the AbstractCodeWriter is configured to always append a newline. A newline is only appended in these cases if the result does not already end with a newline.

        Overrides:
        toString in class java.lang.Object
        Returns:
        Returns the generated code.
      • pushState

        public T pushState()
        Copies and pushes the current state to the state stack.

        This method is used to prepare for a corresponding popState() operation later. It stores the current state of the AbstractCodeWriter into a stack and keeps it active. After pushing, mutations can be made to the state of the AbstractCodeWriter without affecting the previous state on the stack. Changes to the state of the AbstractCodeWriter can be undone by using popState(), which Returns self state to the state it was in before calling pushState.

        Returns:
        Returns the code writer.
      • pushState

        public T pushState​(java.lang.String sectionName)
        Copies and pushes the current state to the state stack using a named state that can be intercepted by functions registered with onSection(CodeInterceptor).

        The text written while in this state is buffered and passed to each state interceptor. If no text is written by the section or an interceptor, nothing is changed on the AbstractCodeWriter. This behavior allows for placeholder sections to be added into AbstractCodeWriter generators in order to provide extension points that can be otherwise empty.

        Parameters:
        sectionName - Name of the section to set on the state.
        Returns:
        Returns the code writer.
      • pushState

        public T pushState​(CodeSection section)
        Pushes a strongly typed section extension point.

        Interceptors can be registered to intercept this specific type of CodeSection using a CodeInterceptor and providing a class for which section is an instance.

        Parameters:
        section - The section value to push.
        Returns:
        Returns self.
        See Also:
        onSection(CodeInterceptor)
      • injectSection

        public T injectSection​(CodeSection section)
        Creates a section that contains no content used to allow CodeInterceptors to inject content at specific locations.
        Parameters:
        section - The code section to register that can be intercepted by type.
        Returns:
        Returns self.
      • getDebugInfo

        public final CodeWriterDebugInfo getDebugInfo()
        Gets debug information about the current state of the AbstractCodeWriter, including the path to the current state as returned by getStateDebugPath(), and up to the last two lines of text written to the AbstractCodeWriter.

        This debug information is used in most exceptions thrown by AbstractCodeWriter to provide additional context when something goes wrong. It can also be used by subclasses and collaborators to aid in debugging codegen issues.

        Returns:
        Returns debug info as a string.
        See Also:
        getDebugInfo(int)
      • getDebugInfo

        public CodeWriterDebugInfo getDebugInfo​(int numberOfContextLines)
        Gets debug information about the current state of the AbstractCodeWriter.

        This method can be overridden in order to add more metadata to the created debug info object.

        Parameters:
        numberOfContextLines - Include the last N lines in the output. Set to 0 to omit lines.
        Returns:
        Returns debug info as a string.
        See Also:
        getDebugInfo()
      • pushFilteredState

        public T pushFilteredState​(java.util.function.Function<java.lang.String,​java.lang.String> filter)
        Pushes an anonymous named state that is always passed through the given filter function before being written to the writer.
        Parameters:
        filter - Function that maps over the entire section when popped.
        Returns:
        Returns the code writer.
      • popState

        public T popState()
        Pops the current AbstractCodeWriter state from the state stack.

        This method is used to reverse a previous pushState() operation. It configures the current AbstractCodeWriter state to what it was before the last preceding pushState call.

        Returns:
        Returns self.
        Throws:
        java.lang.IllegalStateException - if there a no states to pop.
      • onSection

        public T onSection​(java.lang.String sectionName,
                           java.util.function.Consumer<java.lang.Object> interceptor)
        Registers a function that intercepts the contents of a section and writes to the AbstractCodeWriter with the updated contents.

        The interceptor function is expected to have a reference to the AbstractCodeWriter and to mutate it when they are invoked. Each interceptor is invoked in their own isolated pushed/popped states.

        The text provided to interceptor does not contain a trailing new line. A trailing new line is expected to be injected automatically when the results of intercepting the contents are written to the AbstractCodeWriter. A result is only written if the interceptors write a non-null, non-empty string, allowing for empty placeholders to be added that don't affect the resulting layout of the code.

        
         SimpleCodeWriter = new SimpleCodeWriter();
        
         // Prepend text to a section named "foo".
         writer.onSectionPrepend("foo", () -> writer.write("A"));
        
         // Write text to a section, and ensure that the original
         // text is written too.
         writer.onSection("foo", text -> {
             // Write before the original text.
             writer.write("A");
             // Write the original text of the section.
             writer.writeWithNoFormatting(text);
             // Write more text to the section.
             writer.write("C");
         });
        
         // Create the section, write to it, then close the section.
         writer.pushState("foo").write("B").popState();
        
         assert(writer.toString().equals("A\nB\nC\n"));
         

        Newline handling

        This method is a wrapper around onSection(CodeInterceptor) that has several limitations:

        • The provided interceptor is expected to have a reference to an AbstractCodeWriter so that write calls can be made.
        • The handling of newlines is much less precise. If you want to give interceptors full control over how newlines are injected, then onSection(CodeInterceptor) must be used directly and careful use of writeInlineWithNoFormatting(Object) is required when writing the previous contents to the interceptor.
        • Interceptors do not have access to strongly typed event data like CodeInterceptors do.

        The newline handling functionality provided by this method can be reproduced using a CodeInterceptor by removing trailing newlines using removeTrailingNewline(String).

        
         SimpleCodeWriter = new SimpleCodeWriter();
        
         CodeInterceptor<CodeSection, SimpleCodeWriter> interceptor = CodeInterceptor.forName(sectionName, (w, p) -> {
             String trimmedContent = removeTrailingNewline(p);
             interceptor.accept(trimmedContent);
         })
        
         writer.onSection(interceptor);
         
        Parameters:
        sectionName - The name of the section to intercept.
        interceptor - The function to intercept with.
        Returns:
        Returns self.
      • onSection

        public <S extends CodeSectionT onSection​(CodeInterceptor<S,​T> interceptor)
        Intercepts a section of code emitted for a strongly typed CodeSection.

        These section interceptors provide a kind of event-based hook system for AbstractCodeWriters that add extension points when generating code. The function has the ability to completely ignore the original contents of the section, to prepend text to it, and append text to it. Intercepting functions are expected to have a reference to the AbstractCodeWriter and to mutate it when they are invoked. Each interceptor is invoked in their own isolated pushed/popped states.

        Interceptors are registered on the current state of the AbstractCodeWriter. When the state to which an interceptor is registered is popped, the interceptor is no longer in scope.

        Type Parameters:
        S - The type of section being intercepted.
        Parameters:
        interceptor - A consumer that takes the writer and strongly typed section.
        Returns:
        Returns self.
      • enableNewlines

        public T enableNewlines()
        Enables the automatic appending of newlines in the current state.
        Returns:
        Returns self.
      • setNewline

        public T setNewline​(java.lang.String newline)
        Sets the character used to represent newlines in the current state ("\n" is the default).

        When the provided string is empty (""), then newlines are disabled in the current state. This is exactly equivalent to calling disableNewlines(), and does not actually change the newline character of the current state.

        Setting the newline character to a non-empty string implicitly enables newlines in the current state.

        Parameters:
        newline - Newline character to use.
        Returns:
        Returns self.
      • setNewline

        public T setNewline​(char newline)
        Sets the character used to represent newlines in the current state ("\n" is the default).

        This call also enables newlines in the current state by calling enableNewlines().

        Parameters:
        newline - Newline character to use.
        Returns:
        Returns self.
      • getNewline

        public java.lang.String getNewline()
        Gets the character used to represent newlines in the current state.
        Returns:
        Returns the newline string.
      • setIndentText

        public T setIndentText​(java.lang.String indentText)
        Sets the text used for indentation (defaults to four spaces).
        Parameters:
        indentText - Indentation text.
        Returns:
        Returns self.
      • getIndentText

        public final java.lang.String getIndentText()
        Gets the text used for indentation (defaults to four spaces).
        Returns:
        Returns the indentation string.
      • trimTrailingSpaces

        public T trimTrailingSpaces()
        Enables the trimming of trailing spaces on a line.
        Returns:
        Returns self.
      • trimTrailingSpaces

        public T trimTrailingSpaces​(boolean trimTrailingSpaces)
        Configures if trailing spaces on a line are removed.
        Parameters:
        trimTrailingSpaces - Set to true to trim trailing spaces.
        Returns:
        Returns self.
      • getTrimTrailingSpaces

        public boolean getTrimTrailingSpaces()
        Returns true if the trailing spaces in the current state are trimmed.
        Returns:
        Returns the trailing spaces setting of the current state.
      • trimBlankLines

        public T trimBlankLines()
        Ensures that no more than one blank line occurs in succession.
        Returns:
        Returns self.
      • trimBlankLines

        public T trimBlankLines​(int trimBlankLines)
        Ensures that no more than the given number of newlines can occur in succession, removing consecutive newlines that exceed the given threshold.
        Parameters:
        trimBlankLines - Number of allowed consecutive newlines. Set to -1 to perform no trimming. Set to 0 to allow no blank lines. Set to 1 or more to allow for no more than N consecutive blank lines.
        Returns:
        Returns self.
      • getTrimBlankLines

        public int getTrimBlankLines()
        Returns the number of allowed consecutive newlines that are not trimmed by the AbstractCodeWriter when written to a string.
        Returns:
        Returns the number of allowed consecutive newlines. -1 means that no newlines are trimmed. 0 allows no blank lines. 1 or more allows for no more than N consecutive blank lines.
      • insertTrailingNewline

        public T insertTrailingNewline()
        Configures the AbstractCodeWriter to always append a newline at the end of the text if one is not already present.

        This setting is not captured as part of push/popState.

        Returns:
        Returns self.
      • insertTrailingNewline

        public T insertTrailingNewline​(boolean trailingNewline)
        Configures the AbstractCodeWriter to always append a newline at the end of the text if one is not already present.

        This setting is not captured as part of push/popState.

        Parameters:
        trailingNewline - True if a newline is added.
        Returns:
        Returns self.
      • getInsertTrailingNewline

        public boolean getInsertTrailingNewline()
        Checks if the AbstractCodeWriter inserts a trailing newline (if necessary) when converted to a string.
        Returns:
        The newline behavior (true to insert a trailing newline).
      • setNewlinePrefix

        public T setNewlinePrefix​(java.lang.String newlinePrefix)
        Sets a prefix to prepend to every line after a new line is added (except for an inserted trailing newline).
        Parameters:
        newlinePrefix - Newline prefix to use.
        Returns:
        Returns self.
      • getNewlinePrefix

        public java.lang.String getNewlinePrefix()
        Gets the prefix to prepend to every line after a new line is added (except for an inserted trailing newline).
        Returns:
        Returns the newline prefix string.
      • indent

        public T indent()
        Indents all text one level.
        Returns:
        Returns self.
      • indent

        public T indent​(int levels)
        Indents all text a specific number of levels.
        Parameters:
        levels - Number of levels to indent.
        Returns:
        Returns self.
      • getIndentLevel

        public int getIndentLevel()
        Gets the indentation level of the current state.
        Returns:
        Returns the indentation level of the current state.
      • dedent

        public T dedent()
        Removes one level of indentation from all lines.
        Returns:
        Returns self.
      • dedent

        public T dedent​(int levels)
        Removes a specific number of indentations from all lines.

        Set to -1 to dedent back to 0 (root).

        Parameters:
        levels - Number of levels to remove.
        Returns:
        Returns self.
        Throws:
        java.lang.IllegalStateException - when trying to dedent too far.
      • openBlock

        public T openBlock​(java.lang.String textBeforeNewline,
                           java.lang.Object... args)
        Opens a block of syntax by writing text, a newline, then indenting.
         
         String result = new SimpleCodeWriter()
                 .openBlock("public final class $L {", "Foo")
                     .openBlock("public void main(String[] args) {")
                         .write("System.out.println(args[0]);")
                     .closeBlock("}")
                 .closeBlock("}")
                 .toString();
         
         
        Parameters:
        textBeforeNewline - Text to write before writing a newline and indenting.
        args - String arguments to use for formatting.
        Returns:
        Returns this.
      • openBlock

        public T openBlock​(java.lang.String textBeforeNewline,
                           java.lang.String textAfterNewline,
                           java.lang.Runnable f)
        Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
        
         SimpleCodeWriter = new SimpleCodeWriter();
         writer.openBlock("public final class $L {", "}", "Foo", () -> {
             writer.openBlock("public void main(String[] args) {", "}", () -> {
                 writer.write("System.out.println(args[0]);");
             })
         });
         
        Parameters:
        textBeforeNewline - Text to write before writing a newline and indenting.
        textAfterNewline - Text to write after writing a newline and indenting.
        f - Runnable function to execute inside of the block.
        Returns:
        Returns this.
      • openBlock

        public T openBlock​(java.lang.String textBeforeNewline,
                           java.lang.String textAfterNewline,
                           java.lang.Object arg1,
                           java.lang.Runnable f)
        Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
        Parameters:
        textBeforeNewline - Text to write before writing a newline and indenting.
        textAfterNewline - Text to write after writing a newline and indenting.
        arg1 - First positional argument to substitute into textBeforeNewline.
        f - Runnable function to execute inside of the block.
        Returns:
        Returns this.
      • openBlock

        public T openBlock​(java.lang.String textBeforeNewline,
                           java.lang.String textAfterNewline,
                           java.lang.Object arg1,
                           java.lang.Object arg2,
                           java.lang.Runnable f)
        Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
        Parameters:
        textBeforeNewline - Text to write before writing a newline and indenting.
        textAfterNewline - Text to write after writing a newline and indenting.
        arg1 - First positional argument to substitute into textBeforeNewline.
        arg2 - Second positional argument to substitute into textBeforeNewline.
        f - Runnable function to execute inside of the block.
        Returns:
        Returns this.
      • openBlock

        public T openBlock​(java.lang.String textBeforeNewline,
                           java.lang.String textAfterNewline,
                           java.lang.Object arg1,
                           java.lang.Object arg2,
                           java.lang.Object arg3,
                           java.lang.Runnable f)
        Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
        Parameters:
        textBeforeNewline - Text to write before writing a newline and indenting.
        textAfterNewline - Text to write after writing a newline and indenting.
        arg1 - First positional argument to substitute into textBeforeNewline.
        arg2 - Second positional argument to substitute into textBeforeNewline.
        arg3 - Third positional argument to substitute into textBeforeNewline.
        f - Runnable function to execute inside of the block.
        Returns:
        Returns this.
      • openBlock

        public T openBlock​(java.lang.String textBeforeNewline,
                           java.lang.String textAfterNewline,
                           java.lang.Object arg1,
                           java.lang.Object arg2,
                           java.lang.Object arg3,
                           java.lang.Object arg4,
                           java.lang.Runnable f)
        Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
        Parameters:
        textBeforeNewline - Text to write before writing a newline and indenting.
        textAfterNewline - Text to write after writing a newline and indenting.
        arg1 - First positional argument to substitute into textBeforeNewline.
        arg2 - Second positional argument to substitute into textBeforeNewline.
        arg3 - Third positional argument to substitute into textBeforeNewline.
        arg4 - Fourth positional argument to substitute into textBeforeNewline.
        f - Runnable function to execute inside of the block.
        Returns:
        Returns this.
      • openBlock

        public T openBlock​(java.lang.String textBeforeNewline,
                           java.lang.String textAfterNewline,
                           java.lang.Object arg1,
                           java.lang.Object arg2,
                           java.lang.Object arg3,
                           java.lang.Object arg4,
                           java.lang.Object arg5,
                           java.lang.Runnable f)
        Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
        Parameters:
        textBeforeNewline - Text to write before writing a newline and indenting.
        textAfterNewline - Text to write after writing a newline and indenting.
        arg1 - First positional argument to substitute into textBeforeNewline.
        arg2 - Second positional argument to substitute into textBeforeNewline.
        arg3 - Third positional argument to substitute into textBeforeNewline.
        arg4 - Fourth positional argument to substitute into textBeforeNewline.
        arg5 - Fifth positional argument to substitute into textBeforeNewline.
        f - Runnable function to execute inside of the block.
        Returns:
        Returns this.
      • openBlock

        public T openBlock​(java.lang.String textBeforeNewline,
                           java.lang.String textAfterNewline,
                           java.lang.Object[] args,
                           java.lang.Runnable f)
        Opens a block of syntax by writing textBeforeNewline, a newline, then indenting, then executes the given Runnable, then closes the block of syntax by writing a newline, dedenting, then writing textAfterNewline.
        Parameters:
        textBeforeNewline - Text to write before writing a newline and indenting.
        textAfterNewline - Text to write after writing a newline and indenting.
        args - Arguments to substitute into textBeforeNewline.
        f - Runnable function to execute inside of the block.
        Returns:
        Returns this.
      • closeBlock

        public T closeBlock​(java.lang.String textAfterNewline,
                            java.lang.Object... args)
        Closes a block of syntax by writing a newline, dedenting, then writing text.
        Parameters:
        textAfterNewline - Text to write after writing a newline and dedenting.
        args - String arguments to use for formatting.
        Returns:
        Returns this.
      • writeWithNoFormatting

        public T writeWithNoFormatting​(java.lang.Object content)
        Writes text to the AbstractCodeWriter and appends a newline.

        The provided text does not use any kind of expression formatting.

        Indentation and the newline prefix is only prepended if the writer's cursor is at the beginning of a newline.

        Stack trace comments are written along with the given content if enableStackTraceComments(boolean) was called with true.

        Parameters:
        content - Content to write.
        Returns:
        Returns self.
      • isStackTraceRelevant

        protected boolean isStackTraceRelevant​(java.lang.StackTraceElement e)
        Tests if the given StackTraceElement is relevant for a comment used when writing debug information before calls to write.

        The default implementation filters out all methods in "java.*", AbstractCodeWriter, software.amazon.smithy.utils.SymbolWriter, SimpleCodeWriter, and methods of the implementing subclass of AbstractCodeWriter. This method can be overridden to further filter stack frames as needed.

        Parameters:
        e - StackTraceElement to test.
        Returns:
        Returns true if this element should be in a comment.
      • formatWithStackTraceElement

        protected java.lang.String formatWithStackTraceElement​(java.lang.String content,
                                                               java.lang.StackTraceElement element,
                                                               boolean inline)
        Formats content for the given stack frame.

        Subclasses are expected to override this method as needed to handle language-specific comment requirements. By default, this class will use C/Java style "traditional" comments that come on the same line before both calls to writeInline and calls to write with a newline {@see https://docs.oracle.com/javase/specs/jls/se18/html/jls-3.html#jls-3.7}.

        Programming languages that do not support inline comments should return the given content string as-is when writingInline is set to true.

        Parameters:
        content - The content about to be written.
        element - The StackFrameElement to format.
        inline - Set to true when this is a comment intended to appear before inline content.
        Returns:
        Returns the formatted content that includes a leading comment.
      • writeInlineWithNoFormatting

        public final T writeInlineWithNoFormatting​(java.lang.Object content)
        Writes inline text to the AbstractCodeWriter with no formatting.

        The provided text does not use any kind of expression formatting. Indentation and the newline prefix is only prepended if the writer's cursor is at the beginning of a newline.

        Stack trace comments are written along with the given content if enableStackTraceComments(boolean) was called with true.

        Parameters:
        content - Inline content to write.
        Returns:
        Returns self.
      • format

        public final java.lang.String format​(java.lang.Object content,
                                             java.lang.Object... args)
        Creates a formatted string using formatter expressions and variadic arguments.

        Important: if the formatters that are executed while formatting the given content string mutate the AbstractCodeWriter, it could leave the SimpleCodeWriter in an inconsistent state. For example, some AbstractCodeWriter implementations manage imports and dependencies automatically based on code that is referenced by formatters. If such an expression is used with this format method but the returned String is never written to the AbstractCodeWriter, then the AbstractCodeWriter might be mutated to track dependencies that aren't actually necessary.

        
         SimpleCodeWriter = new SimpleCodeWriter();
         String name = "Person";
         String formatted = writer.format("Hello, $L", name);
         assert(formatted.equals("Hello, Person"));
         
        Parameters:
        content - Content to format.
        args - String arguments to use for formatting.
        Returns:
        Returns the formatted string.
        See Also:
        write(java.lang.Object, java.lang.Object...), putFormatter(char, java.util.function.BiFunction<java.lang.Object, java.lang.String, java.lang.String>)
      • consumer

        public java.util.function.Consumer<T> consumer​(java.util.function.Consumer<T> consumer)
        A simple helper method that makes it easier to invoke the built-in C (call) formatter using a Consumer where T is the specific type of AbstractCodeWriter.

        Instead of having to type this:

        
         writer.write("$C", (Consumer<MyWriter>) (w) -> w.write("Hi"));
         

        You can write:

        
         writer.write("$C", writer.consumer(w -> w.write("Hi"));
         
        Parameters:
        consumer - The consumer to call.
        Returns:
        Returns the consumer as-is, but cast as the appropriate Java type.
      • call

        public T call​(java.lang.Runnable task)
        Allows calling out to arbitrary code for things like looping or conditional writes without breaking method chaining.
        Parameters:
        task - Method to invoke.
        Returns:
        Returns this.
      • write

        public T write​(java.lang.Object content,
                       java.lang.Object... args)
        Writes text to the AbstractCodeWriter and appends a newline.

        The provided text is automatically formatted using variadic arguments.

        Indentation and the newline prefix is only prepended if the writer's cursor is at the beginning of a newline.

        If a subclass overrides this method, it should first perform formatting and then delegate to writeWithNoFormatting(java.lang.Object) to perform the actual write.

        Parameters:
        content - Content to write.
        args - String arguments to use for formatting.
        Returns:
        Returns self.
      • writeInline

        public T writeInline​(java.lang.Object content,
                             java.lang.Object... args)
        Writes text to the AbstractCodeWriter without appending a newline.

        The provided text is automatically formatted using variadic arguments.

        Indentation and the newline prefix is only prepended if the writer's cursor is at the beginning of a newline.

        If newlines are present in the given string, each of those lines will receive proper indentation.

        If a subclass overrides this method, it should first perform formatting and then delegate to writeInlineWithNoFormatting(java.lang.Object) to perform the actual write.

        Parameters:
        content - Content to write.
        args - String arguments to use for formatting.
        Returns:
        Returns self.
      • ensureNewline

        public T ensureNewline()
        Ensures that the last text written to the writer was a newline as defined in the current state and inserts one if necessary.
        Returns:
        Returns self.
      • writeOptional

        public T writeOptional​(java.lang.Object content)
        Optionally writes text to the AbstractCodeWriter and appends a newline if a value is present.

        If the provided content value is null, nothing is written. If the provided content value is an empty Optional, nothing is written. If the result of calling toString on content results in an empty string, nothing is written. Finally, if the value is a non-empty string, the content is written to the AbstractCodeWriter at the current level of indentation, and a newline is appended.

        Parameters:
        content - Content to write if present.
        Returns:
        Returns self.
      • unwrite

        public T unwrite​(java.lang.Object content,
                         java.lang.Object... args)
        Remove the most recent text written to the AbstractCodeWriter if and only if the last written text is exactly equal to the given expanded content string.

        This can be useful, for example, for use cases like removing trailing commas from lists of values.

        For example, the following will remove ", there." from the end of the AbstractCodeWriter:

        
         SimpleCodeWriter = new SimpleCodeWriter();
         writer.writeInline("Hello, there.");
         writer.unwrite(", there.");
         assert(writer.toString().equals("Hello\n"));
         

        However, the following call to unwrite will do nothing because the last text written to the AbstractCodeWriter does not match:

        
         SimpleCodeWriter = new SimpleCodeWriter();
         writer.writeInline("Hello.");
         writer.unwrite("there.");
         assert(writer.toString().equals("Hello.\n"));
         
        Parameters:
        content - Content to write.
        args - String arguments to use for formatting.
        Returns:
        Returns self.
      • putContext

        public T putContext​(java.lang.String key,
                            java.lang.Object value)
        Adds a named key-value pair to the context of the current state.

        These context values can be referenced by named interpolated parameters.

        Parameters:
        key - Key to add to the context.
        value - Value to associate with the key.
        Returns:
        Returns self.
      • putContext

        public T putContext​(java.util.Map<java.lang.String,​java.lang.Object> mappings)
        Adds a map of named key-value pair to the context of the current state.

        These context values can be referenced by named interpolated parameters.

        Parameters:
        mappings - Key value pairs to add.
        Returns:
        Returns self.
      • removeContext

        public T removeContext​(java.lang.String key)
        Removes a named key-value pair from the context of the current state.

        This method has no effect if the parent state defines the context key value pair.

        Parameters:
        key - Key to add to remove from the current context.
        Returns:
        Returns self.
      • getContext

        public java.lang.Object getContext​(java.lang.String key)
        Gets a named contextual key-value pair from the current state or any parent states.
        Parameters:
        key - Key to retrieve.
        Returns:
        Returns the associated value or null if not present.
      • getContext

        public <C> C getContext​(java.lang.String key,
                                java.lang.Class<C> type)
        Gets a named context key-value pair from the current state and casts the value to the given type.
        Parameters:
        key - Key to retrieve.
        type - The type of value expected.
        Returns:
        Returns the associated value or null if not present.
        Throws:
        java.lang.ClassCastException - if the stored value is not null and does not match type.