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.exceptions.JSilverBadSyntaxException;
2056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.analysis.AnalysisAdapter;
2156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter;
2256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ADataCommand;
2356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AInlineCommand;
2456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ANoopCommand;
2556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.PCommand;
2656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.TData;
2756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
2856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/**
2956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Rewrites the AST to replace all 'inline' commands with their associated inner
3056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * command sub-tree, where all whitespace data commands have been removed.
3156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
3256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p>The following template:
3356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <pre>
3456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs inline?>
3556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs if:x.flag?>
3656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *   <?cs var:">> " + x.foo + " <<"?>
3756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs /if?>
3856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs /inline?>
3956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * </pre>
4056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
4156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p>will render as if it had been written:
4256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <pre>
4356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs if:x.flag?><?cs var:">> " + x.foo + " <<"?><?cs /if?>
4456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * </pre>
4556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
4656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p>The inline command is intended only to allow neater template authoring.
4756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * As such there is a restriction that any data commands (ie, bare literal text)
4856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * inside an inline command can consist only of whitespace characters. This
4956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * limits the risk of accidentally modifying the template's output in an
5056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * unexpected way when using the inline command. Literal text may still be
5156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * rendered in an inlined section if it is part of a var command.
5256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
5356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <p>Data commands containing only whitespace are effectively removed by
5456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * replacing them with noop commands. These can be removed (if needed) by a
5556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * later optimization step but shouldn't cause any issues.
5656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */
5756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpublic class InlineRewriter extends DepthFirstAdapter {
5856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
5956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
6056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Inner visitor class to recursively replace data commands with noops.
6156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
6256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static AnalysisAdapter WHITESPACE_STRIPPER = new DepthFirstAdapter() {
6356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    @Override
6456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    public void caseADataCommand(ADataCommand node) {
6556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      TData data = node.getData();
6656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (isAllWhitespace(data.getText())) {
6756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        node.replaceBy(new ANoopCommand());
6856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        return;
6956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
7056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // TODO: Add more information here (line numbers etc...)
7156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverBadSyntaxException(
7256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          "literal text in an inline block may only contain whitespace", data.getText(), null, data
7356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson              .getLine(), data.getPos(), null);
7456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
7556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
7656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    @Override
7756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    public void caseAInlineCommand(AInlineCommand node) {
7856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      // Once in an inline block, just remove any more we encounter.
7956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      PCommand command = node.getCommand();
8056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      node.replaceBy(command);
8156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      command.apply(this);
8256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
8356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  };
8456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
8556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  private static boolean isAllWhitespace(String s) {
8656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    for (int i = 0; i < s.length(); i++) {
8756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (!Character.isWhitespace(s.charAt(i))) {
8856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        return false;
8956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
9056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
9156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return true;
9256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
9356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
9456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
9556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Removes data commands within an inline command.
9656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *
9756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * @throws JSilverBadSyntaxException if any data commands within the inline block contain
9856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *         non-whitespace text.
9956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
10056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
10156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void caseAInlineCommand(AInlineCommand node) {
10256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    node.getCommand().apply(WHITESPACE_STRIPPER);
10356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    node.replaceBy(node.getCommand());
10456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
10556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson}
106