/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.clearsilver.jsilver.precompiler;
import com.google.clearsilver.jsilver.autoescape.AutoEscapeOptions;
import com.google.clearsilver.jsilver.autoescape.EscapeMode;
import com.google.clearsilver.jsilver.compiler.BaseCompiledTemplate;
import com.google.clearsilver.jsilver.functions.FunctionExecutor;
import com.google.clearsilver.jsilver.resourceloader.ResourceLoader;
import com.google.clearsilver.jsilver.template.DelegatingTemplateLoader;
import com.google.clearsilver.jsilver.template.Template;
import com.google.clearsilver.jsilver.template.TemplateLoader;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
/**
* TemplateLoader that stores objects from precompiled Template classes and serves them when asked
* for them. If not found, it passes the request on to the delegate TemplateLoader.
*/
public class PrecompiledTemplateLoader implements DelegatingTemplateLoader {
/**
* This is the next TemplateLoader to ask if we don't find the template.
*/
private final TemplateLoader nextLoader;
private final Map<Object, BaseCompiledTemplate> templateMap;
private final AutoEscapeOptions autoEscapeOptions;
public PrecompiledTemplateLoader(TemplateLoader nextLoader,
Map<Object, String> templateToClassNameMap, FunctionExecutor globalFunctionExecutor,
AutoEscapeOptions autoEscapeOptions) {
this.nextLoader = nextLoader;
this.autoEscapeOptions = autoEscapeOptions;
this.templateMap = makeTemplateMap(templateToClassNameMap, globalFunctionExecutor);
}
private Map<Object, BaseCompiledTemplate> makeTemplateMap(
Map<Object, String> templateToClassNameMap, FunctionExecutor globalFunctionExecutor) {
Map<Object, BaseCompiledTemplate> templateMap = new HashMap<Object, BaseCompiledTemplate>();
ClassLoader classLoader = getClass().getClassLoader();
for (Map.Entry<Object, String> entry : templateToClassNameMap.entrySet()) {
String className = entry.getValue();
BaseCompiledTemplate compiledTemplate = loadTemplateObject(className, classLoader);
// Fill in the necessary
compiledTemplate.setFunctionExecutor(globalFunctionExecutor);
compiledTemplate.setTemplateName(entry.getKey().toString());
compiledTemplate.setTemplateLoader(this);
if (entry.getKey() instanceof PrecompiledTemplateMapKey) {
PrecompiledTemplateMapKey mapKey = (PrecompiledTemplateMapKey) entry.getKey();
// The template's escapeMode is not currently used as the autoescaping is all
// handled at compile time. Still set it in case it is needed later on.
compiledTemplate.setEscapeMode(mapKey.getEscapeMode());
} else {
compiledTemplate.setEscapeMode(EscapeMode.ESCAPE_NONE);
}
compiledTemplate.setAutoEscapeOptions(autoEscapeOptions);
templateMap.put(entry.getKey(), compiledTemplate);
}
return ImmutableMap.copyOf(templateMap);
}
@VisibleForTesting
protected BaseCompiledTemplate loadTemplateObject(String className, ClassLoader classLoader) {
try {
Class<?> templateClass = classLoader.loadClass(className);
// TODO: Not safe to use in untrusted environments
// Does not handle ClassCastException or
// verify class type before calling newInstance.
return (BaseCompiledTemplate) templateClass.newInstance();
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Class not found: " + className, e);
} catch (IllegalAccessException e) {
throw new Error(e);
} catch (InstantiationException e) {
throw new Error(e);
}
}
@Override
public void setTemplateLoaderDelegate(TemplateLoader templateLoaderDelegate) {
for (BaseCompiledTemplate template : templateMap.values()) {
template.setTemplateLoader(templateLoaderDelegate);
}
}
@Override
public Template load(String templateName, ResourceLoader resourceLoader, EscapeMode escapeMode) {
Object key = resourceLoader.getKey(templateName);
PrecompiledTemplateMapKey mapKey = new PrecompiledTemplateMapKey(key, escapeMode);
Template template = templateMap.get(mapKey);
if (template != null) {
return template;
} else {
return nextLoader.load(templateName, resourceLoader, escapeMode);
}
}
/**
* We don't cache temporary templates here so we just call delegate TemplateLoader.
*/
@Override
public Template createTemp(String name, String content, EscapeMode escapeMode) {
return nextLoader.createTemp(name, content, escapeMode);
}
}