Home | History | Annotate | Download | only in syntax
      1 /*
      2  * Copyright (C) 2010 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.clearsilver.jsilver.syntax;
     18 
     19 import com.google.clearsilver.jsilver.exceptions.JSilverBadSyntaxException;
     20 import com.google.clearsilver.jsilver.syntax.analysis.AnalysisAdapter;
     21 import com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter;
     22 import com.google.clearsilver.jsilver.syntax.node.ADataCommand;
     23 import com.google.clearsilver.jsilver.syntax.node.AInlineCommand;
     24 import com.google.clearsilver.jsilver.syntax.node.ANoopCommand;
     25 import com.google.clearsilver.jsilver.syntax.node.PCommand;
     26 import com.google.clearsilver.jsilver.syntax.node.TData;
     27 
     28 /**
     29  * Rewrites the AST to replace all 'inline' commands with their associated inner
     30  * command sub-tree, where all whitespace data commands have been removed.
     31  *
     32  * <p>The following template:
     33  * <pre>
     34  * <?cs inline?>
     35  * <?cs if:x.flag?>
     36  *   <?cs var:">> " + x.foo + " <<"?>
     37  * <?cs /if?>
     38  * <?cs /inline?>
     39  * </pre>
     40  *
     41  * <p>will render as if it had been written:
     42  * <pre>
     43  * <?cs if:x.flag?><?cs var:">> " + x.foo + " <<"?><?cs /if?>
     44  * </pre>
     45  *
     46  * <p>The inline command is intended only to allow neater template authoring.
     47  * As such there is a restriction that any data commands (ie, bare literal text)
     48  * inside an inline command can consist only of whitespace characters. This
     49  * limits the risk of accidentally modifying the template's output in an
     50  * unexpected way when using the inline command. Literal text may still be
     51  * rendered in an inlined section if it is part of a var command.
     52  *
     53  * <p>Data commands containing only whitespace are effectively removed by
     54  * replacing them with noop commands. These can be removed (if needed) by a
     55  * later optimization step but shouldn't cause any issues.
     56  */
     57 public class InlineRewriter extends DepthFirstAdapter {
     58 
     59   /**
     60    * Inner visitor class to recursively replace data commands with noops.
     61    */
     62   private static AnalysisAdapter WHITESPACE_STRIPPER = new DepthFirstAdapter() {
     63     @Override
     64     public void caseADataCommand(ADataCommand node) {
     65       TData data = node.getData();
     66       if (isAllWhitespace(data.getText())) {
     67         node.replaceBy(new ANoopCommand());
     68         return;
     69       }
     70       // TODO: Add more information here (line numbers etc...)
     71       throw new JSilverBadSyntaxException(
     72           "literal text in an inline block may only contain whitespace", data.getText(), null, data
     73               .getLine(), data.getPos(), null);
     74     }
     75 
     76     @Override
     77     public void caseAInlineCommand(AInlineCommand node) {
     78       // Once in an inline block, just remove any more we encounter.
     79       PCommand command = node.getCommand();
     80       node.replaceBy(command);
     81       command.apply(this);
     82     }
     83   };
     84 
     85   private static boolean isAllWhitespace(String s) {
     86     for (int i = 0; i < s.length(); i++) {
     87       if (!Character.isWhitespace(s.charAt(i))) {
     88         return false;
     89       }
     90     }
     91     return true;
     92   }
     93 
     94   /**
     95    * Removes data commands within an inline command.
     96    *
     97    * @throws JSilverBadSyntaxException if any data commands within the inline block contain
     98    *         non-whitespace text.
     99    */
    100   @Override
    101   public void caseAInlineCommand(AInlineCommand node) {
    102     node.getCommand().apply(WHITESPACE_STRIPPER);
    103     node.replaceBy(node.getCommand());
    104   }
    105 }
    106