Home | History | Annotate | Download | only in compiler
      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 
     52   public void setFunctionExecutor(FunctionExecutor functionExecutor) {
     53     this.functionExecutor = functionExecutor;
     54   }
     55 
     56   public void setTemplateName(String templateName) {
     57     this.templateName = templateName;
     58   }
     59 
     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    */
     69   public void setAutoEscapeOptions(AutoEscapeOptions autoEscapeOptions) {
     70     this.autoEscapeOptions = autoEscapeOptions;
     71   }
     72 
     73   @Override
     74   public void render(Data data, Appendable out, ResourceLoader resourceLoader) throws IOException {
     75 
     76     render(createRenderingContext(data, out, resourceLoader));
     77   }
     78 
     79   @Override
     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
     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    */
     97   public void setEscapeMode(EscapeMode mode) {
     98     this.escapeMode = mode;
     99   }
    100 
    101   @Override
    102   public EscapeMode getEscapeMode() {
    103     return escapeMode;
    104   }
    105 
    106   @Override
    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    */
    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 
    128   public static boolean exists(Data data) {
    129     return TypeConverter.exists(data);
    130   }
    131 
    132   public static int asInt(String value) {
    133     return TypeConverter.asNumber(value);
    134   }
    135 
    136   public static int asInt(int value) {
    137     return value;
    138   }
    139 
    140   public static int asInt(boolean value) {
    141     return value ? 1 : 0;
    142   }
    143 
    144   public static int asInt(Value value) {
    145     return value.asNumber();
    146   }
    147 
    148   public static int asInt(Data data) {
    149     return TypeConverter.asNumber(data);
    150   }
    151 
    152   public static String asString(String value) {
    153     return value;
    154   }
    155 
    156   public static String asString(int value) {
    157     return Integer.toString(value);
    158   }
    159 
    160   public static String asString(boolean value) {
    161     return value ? "1" : "0";
    162   }
    163 
    164   public static String asString(Value value) {
    165     return value.asString();
    166   }
    167 
    168   public static String asString(Data data) {
    169     return TypeConverter.asString(data);
    170   }
    171 
    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 
    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 
    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 
    193   public static Value asValue(Value value) {
    194     return value;
    195   }
    196 
    197   public static Value asVariableValue(String variableName, DataContext context) {
    198     return Value.variableValue(variableName, context);
    199   }
    200 
    201   public static boolean asBoolean(boolean value) {
    202     return value;
    203   }
    204 
    205   public static boolean asBoolean(String value) {
    206     return TypeConverter.asBoolean(value);
    207   }
    208 
    209   public static boolean asBoolean(int value) {
    210     return value != 0;
    211   }
    212 
    213   public static boolean asBoolean(Value value) {
    214     return value.asBoolean();
    215   }
    216 
    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    */
    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    */
    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 
    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 
    249     protected CompiledMacro(String macroName, String... argumentsNames) {
    250       this.macroName = macroName;
    251       this.argumentsNames = argumentsNames;
    252     }
    253 
    254     @Override
    255     public void render(Data data, Appendable out, ResourceLoader resourceLoader) throws IOException {
    256       render(createRenderingContext(data, out, resourceLoader));
    257     }
    258 
    259     @Override
    260     public RenderingContext createRenderingContext(Data data, Appendable out,
    261         ResourceLoader resourceLoader) {
    262       return BaseCompiledTemplate.this.createRenderingContext(data, out, resourceLoader);
    263     }
    264 
    265     @Override
    266     public String getTemplateName() {
    267       return BaseCompiledTemplate.this.getTemplateName();
    268     }
    269 
    270     @Override
    271     public String getMacroName() {
    272       return macroName;
    273     }
    274 
    275     @Override
    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 
    285     public int getArgumentCount() {
    286       return argumentsNames.length;
    287     }
    288 
    289     protected TemplateLoader getTemplateLoader() {
    290       return templateLoader;
    291     }
    292 
    293     @Override
    294     public EscapeMode getEscapeMode() {
    295       return BaseCompiledTemplate.this.getEscapeMode();
    296     }
    297 
    298     @Override
    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    */
    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()
    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 
    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