• 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.compiler;
18 
19 import com.google.clearsilver.jsilver.autoescape.AutoEscapeOptions;
20 import com.google.clearsilver.jsilver.autoescape.EscapeMode;
21 import com.google.clearsilver.jsilver.data.Data;
22 import com.google.clearsilver.jsilver.data.DataContext;
23 import com.google.clearsilver.jsilver.data.DefaultDataContext;
24 import com.google.clearsilver.jsilver.data.TypeConverter;
25 import com.google.clearsilver.jsilver.exceptions.ExceptionUtil;
26 import com.google.clearsilver.jsilver.exceptions.JSilverInterpreterException;
27 import com.google.clearsilver.jsilver.functions.FunctionExecutor;
28 import com.google.clearsilver.jsilver.resourceloader.ResourceLoader;
29 import com.google.clearsilver.jsilver.template.DefaultRenderingContext;
30 import com.google.clearsilver.jsilver.template.Macro;
31 import com.google.clearsilver.jsilver.template.RenderingContext;
32 import com.google.clearsilver.jsilver.template.Template;
33 import com.google.clearsilver.jsilver.template.TemplateLoader;
34 import com.google.clearsilver.jsilver.values.Value;
35 
36 import java.io.IOException;
37 import java.util.Collections;
38 
39 /**
40  * Base class providing help to generated templates.
41  *
42  * Note, many of the methods are public as they are also used by macros.
43  */
44 public abstract class BaseCompiledTemplate implements Template {
45 
46   private FunctionExecutor functionExecutor;
47   private String templateName;
48   private TemplateLoader templateLoader;
49   private EscapeMode escapeMode = EscapeMode.ESCAPE_NONE;
50   private AutoEscapeOptions autoEscapeOptions;
51 
setFunctionExecutor(FunctionExecutor functionExecutor)52   public void setFunctionExecutor(FunctionExecutor functionExecutor) {
53     this.functionExecutor = functionExecutor;
54   }
55 
setTemplateName(String templateName)56   public void setTemplateName(String templateName) {
57     this.templateName = templateName;
58   }
59 
setTemplateLoader(TemplateLoader templateLoader)60   public void setTemplateLoader(TemplateLoader templateLoader) {
61     this.templateLoader = templateLoader;
62   }
63 
64   /**
65    * Set auto escaping options so they can be passed to the rendering context.
66    *
67    * @see AutoEscapeOptions
68    */
setAutoEscapeOptions(AutoEscapeOptions autoEscapeOptions)69   public void setAutoEscapeOptions(AutoEscapeOptions autoEscapeOptions) {
70     this.autoEscapeOptions = autoEscapeOptions;
71   }
72 
73   @Override
render(Data data, Appendable out, ResourceLoader resourceLoader)74   public void render(Data data, Appendable out, ResourceLoader resourceLoader) throws IOException {
75 
76     render(createRenderingContext(data, out, resourceLoader));
77   }
78 
79   @Override
createRenderingContext(Data data, Appendable out, ResourceLoader resourceLoader)80   public RenderingContext createRenderingContext(Data data, Appendable out,
81       ResourceLoader resourceLoader) {
82     DataContext dataContext = new DefaultDataContext(data);
83     return new DefaultRenderingContext(dataContext, resourceLoader, out, functionExecutor,
84         autoEscapeOptions);
85   }
86 
87   @Override
getTemplateName()88   public String getTemplateName() {
89     return templateName;
90   }
91 
92   /**
93    * Sets the EscapeMode in which this template was generated.
94    *
95    * @param mode EscapeMode
96    */
setEscapeMode(EscapeMode mode)97   public void setEscapeMode(EscapeMode mode) {
98     this.escapeMode = mode;
99   }
100 
101   @Override
getEscapeMode()102   public EscapeMode getEscapeMode() {
103     return escapeMode;
104   }
105 
106   @Override
getDisplayName()107   public String getDisplayName() {
108     return templateName;
109   }
110 
111   /**
112    * Verify that the loop arguments are valid. If not, we will skip the loop.
113    */
validateLoopArgs(int start, int end, int increment)114   public static boolean validateLoopArgs(int start, int end, int increment) {
115     if (increment == 0) {
116       return false; // No increment. Avoid infinite loop.
117     }
118     if (increment > 0 && start > end) {
119       return false; // Incrementing the wrong way. Avoid infinite loop.
120     }
121     if (increment < 0 && start < end) {
122       return false; // Incrementing the wrong way. Avoid infinite loop.
123     }
124     return true;
125   }
126 
127 
exists(Data data)128   public static boolean exists(Data data) {
129     return TypeConverter.exists(data);
130   }
131 
asInt(String value)132   public static int asInt(String value) {
133     return TypeConverter.asNumber(value);
134   }
135 
asInt(int value)136   public static int asInt(int value) {
137     return value;
138   }
139 
asInt(boolean value)140   public static int asInt(boolean value) {
141     return value ? 1 : 0;
142   }
143 
asInt(Value value)144   public static int asInt(Value value) {
145     return value.asNumber();
146   }
147 
asInt(Data data)148   public static int asInt(Data data) {
149     return TypeConverter.asNumber(data);
150   }
151 
asString(String value)152   public static String asString(String value) {
153     return value;
154   }
155 
asString(int value)156   public static String asString(int value) {
157     return Integer.toString(value);
158   }
159 
asString(boolean value)160   public static String asString(boolean value) {
161     return value ? "1" : "0";
162   }
163 
asString(Value value)164   public static String asString(Value value) {
165     return value.asString();
166   }
167 
asString(Data data)168   public static String asString(Data data) {
169     return TypeConverter.asString(data);
170   }
171 
asValue(String value)172   public static Value asValue(String value) {
173     // Compiler mode does not use the Value's escapeMode or partiallyEscaped
174     // variables. TemplateTranslator uses other means to determine the proper
175     // escaping to apply. So just set the default escaping flags here.
176     return Value.literalValue(value, EscapeMode.ESCAPE_NONE, false);
177   }
178 
asValue(int value)179   public static Value asValue(int value) {
180     // Compiler mode does not use the Value's escapeMode or partiallyEscaped
181     // variables. TemplateTranslator uses other means to determine the proper
182     // escaping to apply. So just set the default escaping flags here.
183     return Value.literalValue(value, EscapeMode.ESCAPE_NONE, false);
184   }
185 
asValue(boolean value)186   public static Value asValue(boolean value) {
187     // Compiler mode does not use the Value's escapeMode or partiallyEscaped
188     // variables. TemplateTranslator uses other means to determine the proper
189     // escaping to apply. So just set the default escaping flags here.
190     return Value.literalValue(value, EscapeMode.ESCAPE_NONE, false);
191   }
192 
asValue(Value value)193   public static Value asValue(Value value) {
194     return value;
195   }
196 
asVariableValue(String variableName, DataContext context)197   public static Value asVariableValue(String variableName, DataContext context) {
198     return Value.variableValue(variableName, context);
199   }
200 
asBoolean(boolean value)201   public static boolean asBoolean(boolean value) {
202     return value;
203   }
204 
asBoolean(String value)205   public static boolean asBoolean(String value) {
206     return TypeConverter.asBoolean(value);
207   }
208 
asBoolean(int value)209   public static boolean asBoolean(int value) {
210     return value != 0;
211   }
212 
asBoolean(Value value)213   public static boolean asBoolean(Value value) {
214     return value.asBoolean();
215   }
216 
asBoolean(Data data)217   public static boolean asBoolean(Data data) {
218     return TypeConverter.asBoolean(data);
219   }
220 
221   /**
222    * Gets the name of the node for writing. Used by cs name command. Returns empty string if not
223    * found.
224    */
getNodeName(Data data)225   public static String getNodeName(Data data) {
226     return data == null ? "" : data.getSymlink().getName();
227   }
228 
229   /**
230    * Returns child nodes of parent. Parent may be null, in which case an empty iterable is returned.
231    */
getChildren(Data parent)232   public Iterable<? extends Data> getChildren(Data parent) {
233     if (parent == null) {
234       return Collections.emptySet();
235     } else {
236       return parent.getChildren();
237     }
238   }
239 
getTemplateLoader()240   protected TemplateLoader getTemplateLoader() {
241     return templateLoader;
242   }
243 
244   public abstract class CompiledMacro implements Macro {
245 
246     private final String macroName;
247     private final String[] argumentsNames;
248 
CompiledMacro(String macroName, String... argumentsNames)249     protected CompiledMacro(String macroName, String... argumentsNames) {
250       this.macroName = macroName;
251       this.argumentsNames = argumentsNames;
252     }
253 
254     @Override
render(Data data, Appendable out, ResourceLoader resourceLoader)255     public void render(Data data, Appendable out, ResourceLoader resourceLoader) throws IOException {
256       render(createRenderingContext(data, out, resourceLoader));
257     }
258 
259     @Override
createRenderingContext(Data data, Appendable out, ResourceLoader resourceLoader)260     public RenderingContext createRenderingContext(Data data, Appendable out,
261         ResourceLoader resourceLoader) {
262       return BaseCompiledTemplate.this.createRenderingContext(data, out, resourceLoader);
263     }
264 
265     @Override
getTemplateName()266     public String getTemplateName() {
267       return BaseCompiledTemplate.this.getTemplateName();
268     }
269 
270     @Override
getMacroName()271     public String getMacroName() {
272       return macroName;
273     }
274 
275     @Override
getArgumentName(int index)276     public String getArgumentName(int index) {
277       if (index >= argumentsNames.length) {
278         // TODO: Make sure this behavior of failing if too many
279         // arguments are passed to a macro is consistent with JNI / interpreter.
280         throw new JSilverInterpreterException("Too many arguments supplied to macro " + macroName);
281       }
282       return argumentsNames[index];
283     }
284 
getArgumentCount()285     public int getArgumentCount() {
286       return argumentsNames.length;
287     }
288 
getTemplateLoader()289     protected TemplateLoader getTemplateLoader() {
290       return templateLoader;
291     }
292 
293     @Override
getEscapeMode()294     public EscapeMode getEscapeMode() {
295       return BaseCompiledTemplate.this.getEscapeMode();
296     }
297 
298     @Override
getDisplayName()299     public String getDisplayName() {
300       return BaseCompiledTemplate.this.getDisplayName() + ":" + macroName;
301     }
302   }
303 
304   /**
305    * Code common to all three include commands.
306    *
307    * @param templateName String representing name of file to include.
308    * @param ignoreMissingFile {@code true} if any FileNotFound error generated by the template
309    *        loader should be ignored, {@code false} otherwise.
310    * @param context Rendering context to use for the included template.
311    */
include(String templateName, boolean ignoreMissingFile, RenderingContext context)312   protected void include(String templateName, boolean ignoreMissingFile, RenderingContext context) {
313     if (!context.pushIncludeStackEntry(templateName)) {
314       throw new JSilverInterpreterException(createIncludeLoopErrorMessage(templateName, context
315           .getIncludedTemplateNames()));
316     }
317 
318     loadAndRenderIncludedTemplate(templateName, ignoreMissingFile, context);
319 
320     if (!context.popIncludeStackEntry(templateName)) {
321       // Include stack trace is corrupted
322       throw new IllegalStateException("Unable to find on include stack: " + templateName);
323     }
324   }
325 
326   // This method should ONLY be called from include()
loadAndRenderIncludedTemplate(String templateName, boolean ignoreMissingFile, RenderingContext context)327   private void loadAndRenderIncludedTemplate(String templateName, boolean ignoreMissingFile,
328       RenderingContext context) {
329     Template template = null;
330     try {
331       template =
332           templateLoader.load(templateName, context.getResourceLoader(), context
333               .getAutoEscapeMode());
334     } catch (RuntimeException e) {
335       if (ignoreMissingFile && ExceptionUtil.isFileNotFoundException(e)) {
336         return;
337       } else {
338         throw e;
339       }
340     }
341     // Intepret loaded template.
342     try {
343       template.render(context);
344     } catch (IOException e) {
345       throw new JSilverInterpreterException(e.getMessage());
346     }
347   }
348 
createIncludeLoopErrorMessage(String templateName, Iterable<String> includeStack)349   private String createIncludeLoopErrorMessage(String templateName, Iterable<String> includeStack) {
350     StringBuilder message = new StringBuilder();
351     message.append("File included twice: ");
352     message.append(templateName);
353 
354     message.append(" Include stack:");
355     for (String fileName : includeStack) {
356       message.append("\n -> ");
357       message.append(fileName);
358     }
359     message.append("\n -> ");
360     message.append(templateName);
361     return message.toString();
362   }
363 }
364