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
17package com.google.clearsilver.jsilver.syntax;
18
19import com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter;
20import com.google.clearsilver.jsilver.syntax.node.AAltCommand;
21import com.google.clearsilver.jsilver.syntax.node.ACallCommand;
22import com.google.clearsilver.jsilver.syntax.node.ADataCommand;
23import com.google.clearsilver.jsilver.syntax.node.ADefCommand;
24import com.google.clearsilver.jsilver.syntax.node.AEachCommand;
25import com.google.clearsilver.jsilver.syntax.node.AEvarCommand;
26import com.google.clearsilver.jsilver.syntax.node.AHardIncludeCommand;
27import com.google.clearsilver.jsilver.syntax.node.AHardLincludeCommand;
28import com.google.clearsilver.jsilver.syntax.node.AIfCommand;
29import com.google.clearsilver.jsilver.syntax.node.AIncludeCommand;
30import com.google.clearsilver.jsilver.syntax.node.ALincludeCommand;
31import com.google.clearsilver.jsilver.syntax.node.ALoopCommand;
32import com.google.clearsilver.jsilver.syntax.node.ALoopIncCommand;
33import com.google.clearsilver.jsilver.syntax.node.ALoopToCommand;
34import com.google.clearsilver.jsilver.syntax.node.ALvarCommand;
35import com.google.clearsilver.jsilver.syntax.node.ANameCommand;
36import com.google.clearsilver.jsilver.syntax.node.AUvarCommand;
37import com.google.clearsilver.jsilver.syntax.node.AVarCommand;
38import com.google.clearsilver.jsilver.syntax.node.AWithCommand;
39import com.google.clearsilver.jsilver.syntax.node.EOF;
40import com.google.clearsilver.jsilver.syntax.node.TData;
41
42import java.util.ArrayList;
43import 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 */
49public 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   */
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. */
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   */
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
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
118  public void inAVarCommand(AVarCommand node) {
119    barrier();
120  }
121
122  @Override
123  public void inALvarCommand(ALvarCommand node) {
124    barrier();
125  }
126
127  @Override
128  public void inAUvarCommand(AUvarCommand node) {
129    barrier();
130  }
131
132  @Override
133  public void inAEvarCommand(AEvarCommand node) {
134    barrier();
135  }
136
137  @Override
138  public void inANameCommand(ANameCommand node) {
139    barrier();
140  }
141
142  // loop, each: block barriers.
143
144  @Override
145  public void inALoopCommand(ALoopCommand node) {
146    blockEntry();
147  }
148
149  @Override
150  public void inALoopIncCommand(ALoopIncCommand node) {
151    blockEntry();
152  }
153
154  @Override
155  public void inALoopToCommand(ALoopToCommand node) {
156    blockEntry();
157  }
158
159  @Override
160  public void inAEachCommand(AEachCommand node) {
161    blockEntry();
162  }
163
164  @Override
165  public void inAWithCommand(AWithCommand node) {
166    blockEntry();
167  }
168
169  @Override
170  public void outALoopCommand(ALoopCommand node) {
171    blockExit();
172  }
173
174  @Override
175  public void outALoopIncCommand(ALoopIncCommand node) {
176    blockExit();
177  }
178
179  @Override
180  public void outALoopToCommand(ALoopToCommand node) {
181    blockExit();
182  }
183
184  @Override
185  public void outAEachCommand(AEachCommand node) {
186    blockExit();
187  }
188
189  @Override
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
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
209  public void inACallCommand(ACallCommand node) {
210    barrier();
211  }
212
213  // if: special case: each branch is a block barrier.
214
215  @Override
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
232  public void inAAltCommand(AAltCommand node) {
233    blockEntry();
234  }
235
236  @Override
237  public void outAAltCommand(AAltCommand node) {
238    blockExit();
239  }
240
241  // include, hard include, linclude, hard linclude unconditional barriers.
242
243  @Override
244  public void caseAIncludeCommand(AIncludeCommand node) {
245    barrier();
246  }
247
248  @Override
249  public void caseAHardIncludeCommand(AHardIncludeCommand node) {
250    barrier();
251  }
252
253  @Override
254  public void caseALincludeCommand(ALincludeCommand node) {
255    barrier();
256  }
257
258  @Override
259  public void caseAHardLincludeCommand(AHardLincludeCommand node) {
260    barrier();
261  }
262
263  // EOF: unconditional barrier.
264
265  @Override
266  public void caseEOF(EOF node) {
267    barrier();
268  }
269}
270