156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/*
256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Copyright (C) 2010 Google Inc.
356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Licensed under the Apache License, Version 2.0 (the "License");
556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * you may not use this file except in compliance with the License.
656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * You may obtain a copy of the License at
756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * http://www.apache.org/licenses/LICENSE-2.0
956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
1056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Unless required by applicable law or agreed to in writing, software
1156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * distributed under the License is distributed on an "AS IS" BASIS,
1256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * See the License for the specific language governing permissions and
1456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * limitations under the License.
1556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */
1656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
1756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpackage com.google.clearsilver.jsilver.syntax;
1856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
1956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter;
2056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AAltCommand;
2156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ACallCommand;
2256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ADataCommand;
2356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ADefCommand;
2456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AEachCommand;
2556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AEscapeCommand;
2656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AEvarCommand;
2756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AIfCommand;
2856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALoopCommand;
2956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALoopIncCommand;
3056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALoopToCommand;
3156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALvarCommand;
3256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ANameCommand;
3356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ANoopCommand;
3456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ASetCommand;
3556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AUvarCommand;
3656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AVarCommand;
3756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AWithCommand;
3856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.Start;
3956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.TData;
4056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
4156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.ArrayList;
4256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.List;
4356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.regex.Matcher;
4456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.regex.Pattern;
4556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
4656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/**
4756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Detects sequences of commands corresponding to a line in the template containing only structural
4856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * commands, comments or whitespace and rewrites the syntax tree to effectively remove any data
4956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * (text) associated with that line (including the trailing whitespace).
5056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p>
5156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * A structural command is any command that never emits any output. These come in three types:
5256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <ul>
5356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <li>Commands that can contain other commands (eg, "alt", "each", "escape", "if", "loop", "with",
5456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * etc...).
5556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <li>Commands that operate on the template itself (eg, "include", "autoescape", etc...).
5656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <li>Comments.
5756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * </ul>
5856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p>
5956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * This makes it much easier to write human readable templates in cases where the output format is
6056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * whitespace sensitive.
6156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p>
6256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Thus the input:
6356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
6456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <pre>
6556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * {@literal
6656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * ----------------
6756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Value is:
6856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs if:x>0 ?>
6956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *   positive
7056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs elif:x<0 ?>
7156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *   negative
7256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs else ?>
7356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *   zero
7456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs /if ?>.
7556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * ----------------
7656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * }
7756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * </pre>
7856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * is equivalent to:
7956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
8056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <pre>
8156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * {@literal
8256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * ----------------
8356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Value is:
8456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs if:x>0 ?>  positive
8556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs elif:x<0 ?>  negative
8656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs else ?>  zero
8756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs /if ?>.
8856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * ----------------
8956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * }
9056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * </pre>
9156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * but is much easier to read.
9256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p>
9356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Where data commands become empty they are replaced with Noop commands, which effectively removes
9456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * them from the tree. These can be removed (if needed) by a later optimization step but shouldn't
9556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * cause any issues.
9656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */
9756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpublic class StructuralWhitespaceStripper extends DepthFirstAdapter {
9856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
9956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * A regex snippet to match sequences of inline whitespace. The easiest way to define this is as
10056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * "not (non-space or newline)".
10156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
10256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static final String IWS = "[^\\S\\n]*";
10356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
10456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /** Pattern to match strings that consist only of inline whitespace. */
10556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static final Pattern INLINE_WHITESPACE = Pattern.compile(IWS);
10656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
10756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
10856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Pattern to match strings that start with arbitrary (inline) whitespace, followed by a newline.
10956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
11056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static final Pattern STARTS_WITH_NEWLINE = Pattern.compile("^" + IWS + "\\n");
11156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
11256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
11356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Pattern to match strings that end with a newline, followed by trailing (inline) whitespace.
11456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
11556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static final Pattern ENDS_WITH_NEWLINE = Pattern.compile("\\n" + IWS + "$");
11656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
11756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
11856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Pattern to capture the content of a string after a leading newline. Only ever used on input
11956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * that previously matched STARTS_WITH_NEWLINE.
12056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
12156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static final Pattern LEADING_WHITESPACE_AND_NEWLINE =
12256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      Pattern.compile("^" + IWS + "\\n(.*)$", Pattern.DOTALL);
12356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
12456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
12556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Pattern to capture the content of a string before a trailing newline. Note that this may have
12656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * to match text that has already had the final newline removed so we must greedily match the
12756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * whitespace rather than the content.
12856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
12956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static final Pattern TRAILING_WHITESPACE =
13056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      Pattern.compile("^(.*?)" + IWS + "$", Pattern.DOTALL);
13156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
13256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
13356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Flag to tell us if we are in whitespace chomping mode. By default we start in this mode because
13456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * the content of the first line in a template is not preceded by a newline (but should behave as
13556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * if it was). Once this flag has been set to false, it remains unset until a new line is
13656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * encountered.
13756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <p>
13856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Note that we only actually remove whitespace when we find the terminating condition rather than
13956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * when as visit the nodes (ie, this mode can be aborted and any visited whitespace will be left
14056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * untouched).
14156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
14256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private boolean maybeChompWhitespace = true;
14356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
14456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
14556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Flag to tell us if the line we are processing has an inline command in it.
14656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <p>
14756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * An inline command is a complex command (eg. 'if', 'loop') where both the start and end of the
14856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * command exists on the same line. Non-complex commands (eg. 'var', 'name') cannot be considered
14956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * inline.
15056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <p>
15156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * This flag is set when we process the start of a complex command and unset when we finish
15256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * processing a line. Thus if the flag is still true when we encounter the end of a complex
15356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * command, it tells us that (at least one) complex command was entirely contained within the
15456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * current line and that we should stop chomping whitespace for the current line.
15556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <p>
15656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * This means we can detect input such as:
15756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *
15856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <pre>
15956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * {@literal <?cs if:x?>   <?cs /if?>}
16056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * </pre>
16156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * for which the trailing newline and surrounding whitespace should not be removed, as opposed to:
16256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *
16356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <pre>
16456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * {@literal <?cs if:x?>
16556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *    something
16656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *  <?cs /if?>
16756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * }
16856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * </pre>
16956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * where the trailing newlines for both the opening and closing of the 'if' command should be
17056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * removed.
17156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
17256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private boolean currentLineContainsInlineComplexCommand = false;
17356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
17456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
17556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * First data command we saw when we started 'chomping' whitespace (note that this can be null if
17656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * we are at the beginning of a file or when we have chomped a previous data command down to
17756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * nothing).
17856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
17956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private ADataCommand firstChompedData = null;
18056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
18156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
18256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Intermediate whitespace-only data commands that we may need to remove.
18356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <p>
18456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * This list is built up as we visit commands and is either processed when we need to remove
18556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * structural whitespace or cleared if we encounter situations that prohibit whitespace removal.
18656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
18756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private List<ADataCommand> whitespaceData = new ArrayList<ADataCommand>();
18856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
18956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static boolean isInlineWhitespace(String text) {
19056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return INLINE_WHITESPACE.matcher(text).matches();
19156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
19256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
19356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static boolean startsWithNewline(String text) {
19456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return STARTS_WITH_NEWLINE.matcher(text).find();
19556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
19656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
19756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static boolean endsWithNewline(String text) {
19856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return ENDS_WITH_NEWLINE.matcher(text).find();
19956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
20056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
20156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
20256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Removes leading whitespace (including first newline) from the given string. The text must start
20356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * with optional whitespace followed by a newline.
20456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
20556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static String stripLeadingWhitespaceAndNewline(String text) {
20656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Matcher matcher = LEADING_WHITESPACE_AND_NEWLINE.matcher(text);
20756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (!matcher.matches()) {
20856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new IllegalStateException("Text '" + text + "' should have leading whitespace/newline.");
20956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
21056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return matcher.group(1);
21156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
21256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
21356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
21456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Removes trailing whitespace (if present) from the given string.
21556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
21656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static String stripTrailingWhitespace(String text) {
21756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Matcher matcher = TRAILING_WHITESPACE.matcher(text);
21856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (!matcher.matches()) {
21956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // The trailing whitespace regex should never fail to match a string.
22056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new AssertionError("Error in regular expression");
22156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
22256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return matcher.group(1);
22356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
22456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
22556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
22656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Remove whitespace (including first newline) from the start of the given data command (replacing
22756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * it with a Noop command if it becomes empty). Returns a modified data command, or null if all
22856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * text was removed.
22956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * <p>
23056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * The given command can be null at the beginning of the file or if the original data command was
23156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * entirely consumed by a previous strip operation (remember that data commands can be processed
23256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * twice, at both the start and end of a whitespace sequence).
23356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
23456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static ADataCommand stripLeadingWhitespaceAndNewline(ADataCommand data) {
23556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (data != null) {
23656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      String text = stripLeadingWhitespaceAndNewline(data.getData().getText());
23756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (text.isEmpty()) {
23856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        data.replaceBy(new ANoopCommand());
23956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // Returning null just means we have chomped the whitespace to nothing.
24056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        data = null;
24156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      } else {
24256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        data.setData(new TData(text));
24356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
24456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
24556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return data;
24656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
24756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
24856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
24956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Removes whitespace from the end of the given data command (replacing it with a Noop command if
25056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * it becomes empty).
25156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
25256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static void stripTrailingWhitespace(ADataCommand data) {
25356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (data != null) {
25456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      String text = stripTrailingWhitespace(data.getData().getText());
25556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (text.isEmpty()) {
25656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        data.replaceBy(new ANoopCommand());
25756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      } else {
25856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        data.setData(new TData(text));
25956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
26056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
26156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
26256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
26356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
26456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Removes all data commands collected while chomping the current line and clears the given list.
26556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
26656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static void removeWhitespace(List<ADataCommand> whitespaceData) {
26756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    for (ADataCommand data : whitespaceData) {
26856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      data.replaceBy(new ANoopCommand());
26956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
27056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    whitespaceData.clear();
27156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
27256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
27356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
27456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseStart(Start node) {
27556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Process the hierarchy.
27656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    super.caseStart(node);
27756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // We might end after processing a non-data node, so deal with any
27856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // unprocessed whitespace before we exit.
27956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (maybeChompWhitespace) {
28056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      stripTrailingWhitespace(firstChompedData);
28156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      removeWhitespace(whitespaceData);
28256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      firstChompedData = null;
28356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
28456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Verify we have consumed (and cleared) any object references.
28556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (firstChompedData != null) {
28656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new IllegalStateException("Unexpected first data node.");
28756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
28856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (!whitespaceData.isEmpty()) {
28956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new IllegalStateException("Unexpected data nodes.");
29056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
29156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
29256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
29356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
29456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseADataCommand(ADataCommand data) {
29556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    final String originalText = data.getData().getText();
29656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (maybeChompWhitespace) {
29756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (isInlineWhitespace(originalText)) {
29856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // This data command is whitespace between two commands on the same
29956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // line, simply chomp it and continue ("Om-nom-nom").
30056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        whitespaceData.add(data);
30156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        return;
30256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
30356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (startsWithNewline(originalText)) {
30456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // This data command is at the end of a line that contains only
30556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // structural commands and whitespace. We remove all whitespace
30656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // associated with this line by:
30756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // * Stripping whitespace from the end of the data command at the start
30856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // of this line.
30956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // * Removing all intermediate (whitespace only) data commands.
31056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // * Stripping whitespace from the start of the current data command.
31156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        stripTrailingWhitespace(firstChompedData);
31256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        removeWhitespace(whitespaceData);
31356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        data = stripLeadingWhitespaceAndNewline(data);
31456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        currentLineContainsInlineComplexCommand = false;
31556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      } else {
31656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // This data command contains some non-whitespace text so we must abort
31756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        // the chomping of this line and output it normally.
31856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        abortWhitespaceChompingForCurrentLine();
31956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
32056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
32156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Test to see if we should start chomping on the next line.
32256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    maybeChompWhitespace = endsWithNewline(originalText);
32356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Note that data can be null here if we stripped all the whitespace from
32456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // it (which means that firstChompedData can be null next time around).
32556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    firstChompedData = maybeChompWhitespace ? data : null;
32656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
32756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
32856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
32956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Helper method to abort whitespace processing for the current line. This method is idempotent on
33056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * a per line basis, and once it has been called the state is only reset at the start of the next
33156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * line.
33256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
33356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private void abortWhitespaceChompingForCurrentLine() {
33456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    maybeChompWhitespace = false;
33556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    currentLineContainsInlineComplexCommand = false;
33656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    whitespaceData.clear();
33756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
33856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
33956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // ---- Inline commands that prohibit whitespace removal. ----
34056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
34156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
34256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void inAAltCommand(AAltCommand node) {
34356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    abortWhitespaceChompingForCurrentLine();
34456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
34556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
34656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
34756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void inACallCommand(ACallCommand node) {
34856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    abortWhitespaceChompingForCurrentLine();
34956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
35056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
35156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
35256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void inAEvarCommand(AEvarCommand node) {
35356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    abortWhitespaceChompingForCurrentLine();
35456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
35556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
35656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
35756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void inALvarCommand(ALvarCommand node) {
35856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    abortWhitespaceChompingForCurrentLine();
35956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
36056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
36156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
36256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void inANameCommand(ANameCommand node) {
36356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    abortWhitespaceChompingForCurrentLine();
36456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
36556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
36656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
36756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void inASetCommand(ASetCommand node) {
36856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    abortWhitespaceChompingForCurrentLine();
36956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
37056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
37156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
37256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void inAUvarCommand(AUvarCommand node) {
37356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    abortWhitespaceChompingForCurrentLine();
37456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
37556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
37656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
37756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void inAVarCommand(AVarCommand node) {
37856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    abortWhitespaceChompingForCurrentLine();
37956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
38056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
38156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  // ---- Two part (open/close) commands that can have child commands. ----
38256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
38356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void enterComplexCommand() {
38456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    currentLineContainsInlineComplexCommand = true;
38556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
38656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
38756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void exitComplexCommand() {
38856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (currentLineContainsInlineComplexCommand) {
38956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      abortWhitespaceChompingForCurrentLine();
39056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
39156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
39256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
39356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
39456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAAltCommand(AAltCommand node) {
39556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    enterComplexCommand();
39656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    super.caseAAltCommand(node);
39756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    exitComplexCommand();
39856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
39956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
40056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
40156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseADefCommand(ADefCommand node) {
40256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    enterComplexCommand();
40356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    super.caseADefCommand(node);
40456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    exitComplexCommand();
40556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
40656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
40756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
40856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAEachCommand(AEachCommand node) {
40956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    enterComplexCommand();
41056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    super.caseAEachCommand(node);
41156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    exitComplexCommand();
41256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
41356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
41456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
41556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAEscapeCommand(AEscapeCommand node) {
41656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    enterComplexCommand();
41756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    super.caseAEscapeCommand(node);
41856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    exitComplexCommand();
41956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
42056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
42156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
42256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAIfCommand(AIfCommand node) {
42356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    enterComplexCommand();
42456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    super.caseAIfCommand(node);
42556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    exitComplexCommand();
42656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
42756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
42856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
42956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseALoopCommand(ALoopCommand node) {
43056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    enterComplexCommand();
43156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    super.caseALoopCommand(node);
43256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    exitComplexCommand();
43356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
43456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
43556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
43656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseALoopIncCommand(ALoopIncCommand node) {
43756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    enterComplexCommand();
43856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    super.caseALoopIncCommand(node);
43956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    exitComplexCommand();
44056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
44156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
44256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
44356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseALoopToCommand(ALoopToCommand node) {
44456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    enterComplexCommand();
44556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    super.caseALoopToCommand(node);
44656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    exitComplexCommand();
44756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
44856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
44956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
45056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAWithCommand(AWithCommand node) {
45156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    enterComplexCommand();
45256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    super.caseAWithCommand(node);
45356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    exitComplexCommand();
45456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
45556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson}
456