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