package com.gustz.dove.web.base.view.tpl;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TplFactory {
private String cxtPath;
private String realPath;
private String resLibPath;
private String prefix;
private String viewPrefix;
private ViewHeadMeta viewHeadMeta;
private ConcurrentMap<String, Future<Tpl>> cacheMap = new ConcurrentHashMap<String, Future<Tpl>>();
private ExecutorService executor = Executors.newCachedThreadPool();
private ConcurrentMap<String, Object> locks = new ConcurrentHashMap<String, Object>();
private final Object lock = new Object();
public TplFactory(String realPath, String prefix, String cxtPath, ViewHeadMeta viewHeadMeta) {
super();
this.realPath = realPath;
this.prefix = prefix;
this.cxtPath = cxtPath;
this.viewHeadMeta = viewHeadMeta;
}
private static void close(Closeable io) {
if (io != null)
try {
io.close();
} catch (Throwable t) {
// print then ignore it.
t.printStackTrace();
}
}
public Tpl getTpl(String path, String resLibPath, String viewPrefix, boolean cache) throws FileNotFoundException {
this.resLibPath = resLibPath;
this.viewPrefix = viewPrefix;
if (cache) { // cache
return getTplWithCache(path);
} else { // no cache
return getTplNoCache(path);
}
}
private Tpl getTplWithCache(final String path) {
while (!cacheMap.containsKey(path)) {
if (locks.putIfAbsent(path, lock) == null) { // yes, i get the lock
Callable<Tpl> callable = new Callable<Tpl>() {
@Override
public Tpl call() throws Exception {
return loadTpl(path);
}
};
cacheMap.put(path, executor.submit(callable));
}
}
try {
return cacheMap.get(path).get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
private Tpl getTplNoCache(String path) throws FileNotFoundException {
return loadTpl(path);
}
private Tpl loadTpl(String path) throws FileNotFoundException {
String html = this.parseResLibHtml(path);
return html == null ? null : Tpls.parseHtml(html);
}
private String getViewHeadMeta() {
if (this.viewHeadMeta == null) {
return "";
}
StringBuilder sbd = new StringBuilder("");
// meta title
String title = this.viewHeadMeta.getTitle();
if (title != null && !title.isEmpty()) {
sbd.append("<title>").append(title).append("</title>");
}
// meta content
String kwd = this.viewHeadMeta.getKeywords();
if (kwd != null && !kwd.isEmpty()) {
sbd.append("<meta name=\"Keywords\" content=\"").append(kwd).append("\" />");
}
String desc = this.viewHeadMeta.getDescription();
if (desc != null && !desc.isEmpty()) {
sbd.append("<meta name=\"description\" content=\"").append(desc).append("\" />");
}
return sbd.toString();
}
private String parseResLibHtml(String path) throws FileNotFoundException {
// get view head meta
String libHtml = this.getViewHeadMeta();
// common lib
libHtml += readString(this.resLibPath).replace("\n", "");
libHtml = libHtml.replace("<!DOCTYPE html>", "").replace("<html>", "").replace("<head>", "");
libHtml = libHtml.replace("{{cxtPath}}", this.cxtPath);
libHtml = libHtml.replace("</head>", "").replace("</html>", "");
// add module lib
libHtml += "<script src=\"" + this.cxtPath + "/" + this.viewPrefix + "/" + path.replace(".html", ".js");
libHtml += "\" type=\"text/javascript\"></script>";
// replace resource lib to html
String html = this.readString(this.prefix + "/" + path);
if (html == null || html.isEmpty()) {
throw new FileNotFoundException("/" + this.prefix + "/" + path);
}
return html.replace("<head-lib id=\"resource-lib\"/>", libHtml);
}
/**
* Read string value from a file.
*
* @param path
* @return the content of the file, or null if the file does not exists.
* @throws RuntimeException IOException, permission Exception, or some others.
*/
private String readString(String path) {
File file = new File(this.realPath, path);
InputStream is;
try {
is = new FileInputStream(file);
} catch (FileNotFoundException e1) {
return null;
}
InputStreamReader reader;
try {
reader = new InputStreamReader(is, "UTF-8"); // only utf8 is supported.
} catch (UnsupportedEncodingException e) {
close(is);
throw new RuntimeException(e);
}
StringBuilder ret = new StringBuilder(102400);
char[] cs = new char[102400];
int c;
try {
while ((c = reader.read(cs)) > 0) {
ret.append(cs, 0, c);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
close(reader);
}
return ret.toString();
}
}