• 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.syntax.analysis.DepthFirstAdapter;
20 import com.google.clearsilver.jsilver.syntax.node.AAltCommand;
21 import com.google.clearsilver.jsilver.syntax.node.ACallCommand;
22 import com.google.clearsilver.jsilver.syntax.node.ADataCommand;
23 import com.google.clearsilver.jsilver.syntax.node.ADefCommand;
24 import com.google.clearsilver.jsilver.syntax.node.AEachCommand;
25 import com.google.clearsilver.jsilver.syntax.node.AEvarCommand;
26 import com.google.clearsilver.jsilver.syntax.node.AHardIncludeCommand;
27 import com.google.clearsilver.jsilver.syntax.node.AHardLincludeCommand;
28 import com.google.clearsilver.jsilver.syntax.node.AIfCommand;
29 import com.google.clearsilver.jsilver.syntax.node.AIncludeCommand;
30 import com.google.clearsilver.jsilver.syntax.node.ALincludeCommand;
31 import com.google.clearsilver.jsilver.syntax.node.ALoopCommand;
32 import com.google.clearsilver.jsilver.syntax.node.ALoopIncCommand;
33 import com.google.clearsilver.jsilver.syntax.node.ALoopToCommand;
34 import com.google.clearsilver.jsilver.syntax.node.ALvarCommand;
35 import com.google.clearsilver.jsilver.syntax.node.ANameCommand;
36 import com.google.clearsilver.jsilver.syntax.node.AUvarCommand;
37 import com.google.clearsilver.jsilver.syntax.node.AVarCommand;
38 import com.google.clearsilver.jsilver.syntax.node.AWithCommand;
39 import com.google.clearsilver.jsilver.syntax.node.EOF;
40 import com.google.clearsilver.jsilver.syntax.node.TData;
41 
42 import java.util.ArrayList;
43 import java.util.List;
44 
45 /**
46  * Consolidates runs of (unescaped literal output) data commands, deferring output until another
47  * output command (var, call, etc) is encountered.
48  */
49 public class DataCommandConsolidator extends DepthFirstAdapter {
50   /**
51    * The current block nesting level. This is incremented whenever a conditional command is
52    * encountered.
53    */
54   private int currentBlockNestingLevel = 0;
55   /**
56    * A list of the data commands we're currently considering for consolidation.
57    */
58   private final List<ADataCommand> datas = new ArrayList<ADataCommand>();
59   /** The block nesting level of the data commands above. */
60   private int datasBlockNestingLevel = -1;
61 
62   /**
63    * Data consolidation barrier: consolidates all data contents into the last data command in the
64    * datas list, replacing all but the last node with no-ops.
65    */
barrier()66   private void barrier() {
67     if (datas.size() > 1) {
68       // Put aside the last data command for later, then remove all the other
69       // data commands, coalescing their contents into the last command.
70       ADataCommand last = datas.remove(datas.size() - 1);
71 
72       StringBuilder sb = new StringBuilder();
73       for (ADataCommand data : datas) {
74         sb.append(data.getData().getText());
75         data.replaceBy(null); // removes the node
76       }
77 
78       sb.append(last.getData().getText());
79       last.replaceBy(new ADataCommand(new TData(sb.toString())));
80     }
81     datas.clear();
82     datasBlockNestingLevel = -1;
83   }
84 
85   /** Block entry: just increments the current block nesting level. */
blockEntry()86   private void blockEntry() {
87     assert datasBlockNestingLevel <= currentBlockNestingLevel;
88     ++currentBlockNestingLevel;
89   }
90 
91   /**
92    * Block exit: acts as a conditional barrier only to data contained within the block.
93    */
blockExit()94   private void blockExit() {
95     assert datasBlockNestingLevel <= currentBlockNestingLevel;
96     if (datasBlockNestingLevel == currentBlockNestingLevel) {
97       barrier();
98     }
99     --currentBlockNestingLevel;
100   }
101 
102   // data commands: continue to accumulate as long as the block nesting level
103   // is unchanged.
104 
105   @Override
caseADataCommand(ADataCommand node)106   public void caseADataCommand(ADataCommand node) {
107     assert datasBlockNestingLevel <= currentBlockNestingLevel;
108     if (currentBlockNestingLevel != datasBlockNestingLevel) {
109       barrier();
110     }
111     datas.add(node);
112     datasBlockNestingLevel = currentBlockNestingLevel;
113   }
114 
115   // var, lvar, evar, uvar, name: all unconditional barriers.
116 
117   @Override
inAVarCommand(AVarCommand node)118   public void inAVarCommand(AVarCommand node) {
119     barrier();
120   }
121 
122   @Override
inALvarCommand(ALvarCommand node)123   public void inALvarCommand(ALvarCommand node) {
124     barrier();
125   }
126 
127   @Override
inAUvarCommand(AUvarCommand node)128   public void inAUvarCommand(AUvarCommand node) {
129     barrier();
130   }
131 
132   @Override
inAEvarCommand(AEvarCommand node)133   public void inAEvarCommand(AEvarCommand node) {
134     barrier();
135   }
136 
137   @Override
inANameCommand(ANameCommand node)138   public void inANameCommand(ANameCommand node) {
139     barrier();
140   }
141 
142   // loop, each: block barriers.
143 
144   @Override
inALoopCommand(ALoopCommand node)145   public void inALoopCommand(ALoopCommand node) {
146     blockEntry();
147   }
148 
149   @Override
inALoopIncCommand(ALoopIncCommand node)150   public void inALoopIncCommand(ALoopIncCommand node) {
151     blockEntry();
152   }
153 
154   @Override
inALoopToCommand(ALoopToCommand node)155   public void inALoopToCommand(ALoopToCommand node) {
156     blockEntry();
157   }
158 
159   @Override
inAEachCommand(AEachCommand node)160   public void inAEachCommand(AEachCommand node) {
161     blockEntry();
162   }
163 
164   @Override
inAWithCommand(AWithCommand node)165   public void inAWithCommand(AWithCommand node) {
166     blockEntry();
167   }
168 
169   @Override
outALoopCommand(ALoopCommand node)170   public void outALoopCommand(ALoopCommand node) {
171     blockExit();
172   }
173 
174   @Override
outALoopIncCommand(ALoopIncCommand node)175   public void outALoopIncCommand(ALoopIncCommand node) {
176     blockExit();
177   }
178 
179   @Override
outALoopToCommand(ALoopToCommand node)180   public void outALoopToCommand(ALoopToCommand node) {
181     blockExit();
182   }
183 
184   @Override
outAEachCommand(AEachCommand node)185   public void outAEachCommand(AEachCommand node) {
186     blockExit();
187   }
188 
189   @Override
outAWithCommand(AWithCommand node)190   public void outAWithCommand(AWithCommand node) {
191     blockExit();
192   }
193 
194   // def: special case: run another instance of this optimizer on the contained
195   // commands. def produces no output, so it should not act as a barrier to
196   // any accumulated data nodes; however, it contains data nodes, so we can
197   // (and should) consolidate them.
198 
199   @Override
caseADefCommand(ADefCommand node)200   public void caseADefCommand(ADefCommand node) {
201     DataCommandConsolidator consolidator = new DataCommandConsolidator();
202     node.getCommand().apply(consolidator);
203     consolidator.barrier(); // Force final consolidation, just like EOF would.
204   }
205 
206   // call: unconditional barrier.
207 
208   @Override
inACallCommand(ACallCommand node)209   public void inACallCommand(ACallCommand node) {
210     barrier();
211   }
212 
213   // if: special case: each branch is a block barrier.
214 
215   @Override
caseAIfCommand(AIfCommand node)216   public void caseAIfCommand(AIfCommand node) {
217     if (node.getBlock() != null) {
218       blockEntry();
219       node.getBlock().apply(this);
220       blockExit();
221     }
222     if (node.getOtherwise() != null) {
223       blockEntry();
224       node.getOtherwise().apply(this);
225       blockExit();
226     }
227   }
228 
229   // alt: block barrier.
230 
231   @Override
inAAltCommand(AAltCommand node)232   public void inAAltCommand(AAltCommand node) {
233     blockEntry();
234   }
235 
236   @Override
outAAltCommand(AAltCommand node)237   public void outAAltCommand(AAltCommand node) {
238     blockExit();
239   }
240 
241   // include, hard include, linclude, hard linclude unconditional barriers.
242 
243   @Override
caseAIncludeCommand(AIncludeCommand node)244   public void caseAIncludeCommand(AIncludeCommand node) {
245     barrier();
246   }
247 
248   @Override
caseAHardIncludeCommand(AHardIncludeCommand node)249   public void caseAHardIncludeCommand(AHardIncludeCommand node) {
250     barrier();
251   }
252 
253   @Override
caseALincludeCommand(ALincludeCommand node)254   public void caseALincludeCommand(ALincludeCommand node) {
255     barrier();
256   }
257 
258   @Override
caseAHardLincludeCommand(AHardLincludeCommand node)259   public void caseAHardLincludeCommand(AHardLincludeCommand node) {
260     barrier();
261   }
262 
263   // EOF: unconditional barrier.
264 
265   @Override
caseEOF(EOF node)266   public void caseEOF(EOF node) {
267     barrier();
268   }
269 }
270