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