/*
* Copyright 2012 XueSong Guo.
*
* 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 cn.webwheel.results;
import cn.webwheel.ResultInterpreter;
import cn.webwheel.WebContext;
import cn.webwheel.template.FileVisitor;
import cn.webwheel.template.RendererFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Result interpreter for template result
* @see TemplateResult
*/
public class TemplateResultInterpreter implements ResultInterpreter<TemplateResult> {
protected Map<String, RendererInfo> rendererInfoMap = new ConcurrentHashMap<String, RendererInfo>();
protected int cacheTTL = 5000;
protected RendererFactory factory;
protected String charset;
protected String contentType;
protected Map<Class, BeanGetter> getterMap = new ConcurrentHashMap<Class, BeanGetter>();
public TemplateResultInterpreter(File root, String charset) {
this.charset = charset;
factory = new RendererFactory(root, charset, new ObjectMapper());
}
public int getCacheTTL() {
return cacheTTL;
}
public void setCacheTTL(int cacheTTL) {
this.cacheTTL = cacheTTL;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
@Override
public void interpret(TemplateResult result, WebContext ctx) throws IOException, ServletException {
String path = result.getPath();
if (path == null) {
path = ctx.getPath();
}
long now = System.currentTimeMillis();
RendererInfo ri = rendererInfoMap.get(path);
if (ri != null) {
if (now - ri.lastCheckTime > cacheTTL) {
long lm = 0;
for (File file : ri.files) {
lm += file.lastModified();
}
if (lm != ri.lastModified) {
ri = null;
}
}
}
if (ri == null) {
ri = new RendererInfo();
final RendererInfo finalRi = ri;
FileVisitor fv = new FileVisitor() {
@Override
public String read(File root, String file, String charset) throws IOException {
finalRi.files.add(new File(root, file));
return super.read(root, file, charset);
}
};
ri.renderer = factory.create(path, fv, result.getTemplateCharset());
for (File file : ri.files) {
ri.lastModified += file.lastModified();
}
ri.lastCheckTime = now;
rendererInfoMap.put(path, ri);
}
{
String contentType = result.getContentType();
if (contentType == null) contentType = this.contentType;
if (contentType == null) contentType = ri.renderer.contentType;
if (contentType == null) contentType = "text/html";
ctx.getResponse().setContentType(contentType);
}
{
String charset = result.getRenderCharset();
if (charset == null) charset = this.charset;
if (charset == null) charset = ri.renderer.charset;
if (charset == null) charset = "utf-8";
ctx.getResponse().setCharacterEncoding(charset);
}
BeanGetter beanGetter = getterMap.get(result.getCom().getClass());
if (beanGetter == null) {
getterMap.put(result.getCom().getClass(), beanGetter = new BeanGetter(result.getCom().getClass()));
}
Map<String, Object> map;
try {
map = beanGetter.set(result.getCom());
} catch (Exception e) {
throw new ServletException(e);
}
ri.renderer.render(ctx.getResponse().getWriter(), map);
}
static class BeanGetter {
Field[] fields;
Map<String, Method> methods = new HashMap<String, Method>();
BeanGetter(Class cls) {
fields = cls.getFields();
for (Method method : cls.getMethods()) {
Class<?> rt = method.getReturnType();
if (rt == Void.class) continue;
if (method.getParameterTypes().length > 0) continue;
String name = method.getName();
if (name.length() > 3 && name.startsWith("get")) {
methods.put(name.substring(3, 4).toLowerCase() + name.substring(4), method);
} else if (name.length() > 2 && name.startsWith("is") && (rt == boolean.class || rt == Boolean.class)) {
methods.put(name.substring(2, 3).toLowerCase() + name.substring(3), method);
}
}
}
Map<String, Object> set(Object com) throws IllegalAccessException, InvocationTargetException {
Map<String, Object> map = new HashMap<String, Object>();
for (Field field : fields) {
map.put(field.getName(), field.get(com));
}
for (Map.Entry<String, Method> entry : methods.entrySet()) {
map.put(entry.getKey(), entry.getValue().invoke(com));
}
return map;
}
}
}