• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
isAllWhitespace(String s)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
caseAInlineCommand(AInlineCommand node)101   public void caseAInlineCommand(AInlineCommand node) {
102     node.getCommand().apply(WHITESPACE_STRIPPER);
103     node.replaceBy(node.getCommand());
104   }
105 }
106