Class CodeWriter

java.lang.Object
software.amazon.smithy.utils.CodeWriter
Direct Known Subclasses:
CodegenWriter

public class CodeWriter extends Object
Helper class for generating code.

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

The following example generates some Python code:


 CodeWriter writer = new CodeWriter();
 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:


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

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

A CodeWriter 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. The CodeWriter registers two built-in formatters:

  • L: 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).
  • S: 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 CodeWriter allows the formatter to be used in any state.

The identifier given to a formatter must match the following ABNF:

 %x21-23    ; ( '!' - '#' )
 / %x25-2F  ; ( '%' - '/' )
 / %x3A-60  ; ( ':' - '`' )
 / %x7B-7E  ; ( '{' - '~' )
 

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.


 CodeWriter writer = new CodeWriter();
 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.


 CodeWriter writer = new CodeWriter();
 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 of 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.


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

Escaping interpolation

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


 CodeWriter writer = new CodeWriter().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 CodeWriter 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 ##.


 CodeWriter writer = new CodeWriter();
 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

CodeWriter 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.


 CodeWriter writer = new CodeWriter()
       .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

The CodeWriter 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 CodeWriter and do not affect previous states. The previous transformation state of the CodeWriter can later be restored using popState().

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


 CodeWriter writer = new CodeWriter();
 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
 

The CodeWriter 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 CodeWriter to a string.

Limiting blank lines

Many coding standards recommend limiting the number of successive blank lines. This can be handled automatically by CodeWriter by calling trimBlankLines. The removal of blank lines is handled when the CodeWriter 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:


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

The value of result contains "hello"

Extending CodeWriter

CodeWriter 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 CodeWriter {
     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 CodeWriter 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 CodeWriter in order to emit content. Interceptors need to have a reference to the CodeWriter 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.


 CodeWriter writer = new CodeWriter();
 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 interceptors registered for the "foo" section to make calls to the CodeWriter to modify the section.


 CodeWriter writer = new CodeWriter();
 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 CodeWriter:

 CodeWriter writer = new CodeWriter();
 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|}):


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

    • CodeWriter

      public CodeWriter()
      Creates a new CodeWriter 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 Details

    • createDefault

      public static CodeWriter createDefault()
      Creates a default instance of a CodeWriter that uses "\n" for newlines, flattens multiple successive blank lines into a single blank line, and adds a trailing new line if needed when converting the CodeWriter to a string.
      Returns:
      Returns the created and configured CodeWriter.
    • copySettingsFrom

      public void copySettingsFrom(CodeWriter other)
      Copies settings from the given CodeWriter into this CodeWriter.

      The settings of the other CodeWriter will overwrite both global and state-based settings of this CodeWriter. Formatters of the other CodeWriter will be merged with the formatters of this CodeWriter, and in the case of conflicts, the formatters of the other will take precedence.

      Stateful settings of the other CodeWriter are copied into the current state of this CodeWriter. Only the settings of the top-most state is copied. Other states, and the contents of the top-most state are not copied.

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

      public static String formatLiteral(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 final CodeWriter putFormatter(char identifier, BiFunction<Object,String,String> formatFunction)
      Adds a custom formatter expression to the current state of the CodeWriter.

      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 CodeWriter.
      Returns:
      Returns the CodeWriter.
    • setExpressionStart

      public CodeWriter 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 CodeWriter. In these cases, the character used to start a CodeWriter 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 the CodeWriter.
    • 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 String toString()
      Gets the contents of the generated code.

      The result will have an appended newline if the CodeWriter 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 Object
      Returns:
      Returns the generated code.
    • pushState

      public final CodeWriter 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 CodeWriter into a stack and keeps it active. After pushing, mutations can be made to the state of the CodeWriter without affecting the previous state on the stack. Changes to the state of the CodeWriter can be undone by using popState(), which returns the CodeWriter state to the state it was in before calling pushState.

      Returns:
      Returns the code writer.
    • pushState

      public CodeWriter pushState(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(java.lang.String, java.util.function.Consumer<java.lang.Object>).

      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 CodeWriter. This behavior allows for placeholder sections to be added into CodeWriter 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.
    • pushFilteredState

      public CodeWriter pushFilteredState(Function<String,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 CodeWriter popState()
      Pops the current CodeWriter state from the state stack.

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

      Returns:
      Returns the CodeWriter.
      Throws:
      IllegalStateException - if there a no states to pop.
    • onSection

      public CodeWriter onSection(String sectionName, Consumer<Object> interceptor)
      Registers a function that intercepts the contents of a section and the current context map and writes to the CodeWriter with the updated contents.

      These section interceptors provide a simple hook system for CodeWriters 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 CodeWriter 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 CodeWriter. When the state to which an interceptor is registered is popped, the interceptor is no longer in scope.

      The text provided to the intercepting function does not contain a trailing new line. A trailing new line will be injected automatically when the results of intercepting the contents are written to the CodeWriter. 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.

      
       CodeWriter writer = new CodeWriter();
      
       // 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 the original text of the section.
           writer.write(text);
           // Write more text to the section.
           writer.write("C");
       });
      
       // Append text to a section.
       writer.onSectionAppend("foo", () -> writer.write("D"));
      
       // Create the section, write to it, then close the section.
       writer.pushState("foo").write("B").popState();
      
       assert(writer.toString().equals("A\nB\nC\nD\n"));
       
      Parameters:
      sectionName - The name of the section to intercept.
      interceptor - The function to intercept with.
      Returns:
      Returns the CodeWriter.
    • onSectionPrepend

      public final CodeWriter onSectionPrepend(String sectionName, Runnable writeBefore)
      Prepends to the contents of a named section.
      
       writer.onSectionPrepend("foo", () -> {
           writer.write("This text is added before the rest of the section.");
       });
       
      Parameters:
      sectionName - The name of the section to intercept.
      writeBefore - A runnable that prepends to a section by mutating the writer.
      Returns:
      Returns the CodeWriter.
      See Also:
    • onSectionAppend

      public final CodeWriter onSectionAppend(String sectionName, Runnable writeAfter)
      Appends to the contents of a named section.
      
       writer.onSectionAppend("foo", () -> {
           writer.write("This text is added after the rest of the section.");
       });
       
      Parameters:
      sectionName - The name of the section to intercept.
      writeAfter - A runnable that appends to a section by mutating the writer.
      Returns:
      Returns the CodeWriter.
      See Also:
    • disableNewlines

      public CodeWriter disableNewlines()
      Disables the automatic appending of newlines in the current state.

      Methods like write(java.lang.Object, java.lang.Object...), openBlock(java.lang.String, java.lang.Object...), and closeBlock(java.lang.String, java.lang.Object...) will not automatically append newlines when a state has this flag set.

      Returns:
      Returns the CodeWriter.
    • enableNewlines

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

      public final CodeWriter setNewline(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 the CodeWriter.
    • setNewline

      public final CodeWriter 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 the CodeWriter.
    • getNewline

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

      public final CodeWriter setIndentText(String indentText)
      Sets the text used for indentation (defaults to four spaces).
      Parameters:
      indentText - Indentation text.
      Returns:
      Returns the CodeWriter.
    • getIndentText

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

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

      public final CodeWriter trimTrailingSpaces(boolean trimTrailingSpaces)
      Configures if trailing spaces on a line are removed.
      Parameters:
      trimTrailingSpaces - Set to true to trim trailing spaces.
      Returns:
      Returns the CodeWriter.
    • 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 final CodeWriter trimBlankLines()
      Ensures that no more than one blank line occurs in succession.
      Returns:
      Returns the CodeWriter.
    • trimBlankLines

      public final CodeWriter 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 the CodeWriter.
    • getTrimBlankLines

      public int getTrimBlankLines()
      Returns the number of allowed consecutive newlines that are not trimmed by the CodeWriter 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 final CodeWriter insertTrailingNewline()
      Configures the CodeWriter 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 the CodeWriter.
    • insertTrailingNewline

      public final CodeWriter insertTrailingNewline(boolean trailingNewline)
      Configures the CodeWriter 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 the CodeWriter.
    • getInsertTrailingNewline

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

      public final CodeWriter setNewlinePrefix(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 the CodeWriter.
    • getNewlinePrefix

      public 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 final CodeWriter indent()
      Indents all text one level.
      Returns:
      Returns the CodeWriter.
    • indent

      public final CodeWriter indent(int levels)
      Indents all text a specific number of levels.
      Parameters:
      levels - Number of levels to indent.
      Returns:
      Returns the CodeWriter.
    • getIndentLevel

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

      public final CodeWriter dedent()
      Removes one level of indentation from all lines.
      Returns:
      Returns the CodeWriter.
    • dedent

      public final CodeWriter 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 the CodeWriter.
      Throws:
      IllegalStateException - when trying to dedent too far.
    • openBlock

      public final CodeWriter openBlock(String textBeforeNewline, Object... args)
      Opens a block of syntax by writing text, a newline, then indenting.
       
       String result = new CodeWriter()
               .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 the CodeWriter.
    • openBlock

      public final CodeWriter openBlock(String textBeforeNewline, String textAfterNewline, 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.
      
       CodeWriter writer = new CodeWriter();
       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 the CodeWriter.
    • openBlock

      public final CodeWriter openBlock(String textBeforeNewline, String textAfterNewline, Object arg1, 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 the CodeWriter.
    • openBlock

      public final CodeWriter openBlock(String textBeforeNewline, String textAfterNewline, Object arg1, Object arg2, 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 the CodeWriter.
    • openBlock

      public final CodeWriter openBlock(String textBeforeNewline, String textAfterNewline, Object arg1, Object arg2, Object arg3, 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 the CodeWriter.
    • openBlock

      public final CodeWriter openBlock(String textBeforeNewline, String textAfterNewline, Object arg1, Object arg2, Object arg3, Object arg4, 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 the CodeWriter.
    • openBlock

      public final CodeWriter openBlock(String textBeforeNewline, String textAfterNewline, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, 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 the CodeWriter.
    • openBlock

      public final CodeWriter openBlock(String textBeforeNewline, String textAfterNewline, Object[] args, 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 the CodeWriter.
    • closeBlock

      public final CodeWriter closeBlock(String textAfterNewline, 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 the CodeWriter.
    • writeWithNoFormatting

      public final CodeWriter writeWithNoFormatting(Object content)
      Writes text to the CodeWriter 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.

      Parameters:
      content - Content to write.
      Returns:
      Returns the CodeWriter.
    • format

      public final String format(Object content, 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 CodeWriter, it could leave the CodeWriter in an inconsistent state. For example, some CodeWriter 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 CodeWriter, then the CodeWriter might be mutated to track dependencies that aren't actually necessary.

      
       CodeWriter writer = new CodeWriter();
       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

      public final CodeWriter write(Object content, Object... args)
      Writes text to the CodeWriter 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.

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

      public final CodeWriter writeInline(Object content, Object... args)
      Writes text to the CodeWriter 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.

      Parameters:
      content - Content to write.
      args - String arguments to use for formatting.
      Returns:
      Returns the CodeWriter.
    • writeOptional

      public final CodeWriter writeOptional(Object content)
      Optionally writes text to the CodeWriter 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 CodeWriter at the current level of indentation, and a newline is appended.

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

      public final CodeWriter unwrite(Object content, Object... args)
      Remove the most recent text written to the CodeWriter 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 CodeWriter:

      
       CodeWriter writer = new CodeWriter();
       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 CodeWriter does not match:

      
       CodeWriter writer = new CodeWriter();
       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 the CodeWriter.
    • call

      public final CodeWriter call(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 the CodeWriter.
    • putContext

      public CodeWriter putContext(String key, 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 the CodeWriter.
    • putContext

      public final CodeWriter putContext(Map<String,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 the CodeWriter.
    • removeContext

      public CodeWriter removeContext(String key)
      Removes a named key-value pair from the context of the current state.
      Parameters:
      key - Key to add to remove from the current context.
      Returns:
      Returns the CodeWriter.
    • getContext

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