/*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If
* not, see .
*/
package com.l2dc.gameserver.cache;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2dc.Config;
import com.l2dcproject.commons.util.ResourceUtil;
import com.l2dc.gameserver.model.actor.instance.L2PcInstance;
import com.l2dc.gameserver.templates.htm.HtmTemplateEngine;
import com.l2dc.gameserver.util.Util;
/**
* @author Layane
*
*/
public class HtmCache
{
private static final Logger LOG = LoggerFactory.getLogger(HtmCache.class);
private final ConcurrentHashMap _cache = new ConcurrentHashMap();
private static final String[] VALID_TAGS = { "column", "unknown", "ul", "u",
"tt", "tr", "title", "textcode", "textarea", "td", "table", "sup", "sub",
"strike", "spin", "select", "right", "pre", "p", "option", "ol", "multiedit",
"li", "left", "input", "img", "i", "html", "h7", "h6", "h5", "h4", "h3", "h2",
"h1", "font", "extend", "edit", "comment", "combobox", "center", "button", "br",
"br1", "body", "bar", "address", "a", "sel", "list", "var", "fore", "readonl",
"rows", "valign", "fixwidth", "bordercolorli", "bordercolorda", "bordercolor",
"border", "bgcolor", "background", "align", "valu", "readonly", "multiple",
"selected", "typ", "type", "maxlength", "checked", "src", "y", "x", "querydelay",
"noscrollbar", "imgsrc", "b", "fg", "size", "face", "color", "deffon", "deffixedfont",
"width", "value", "tooltip", "name", "min", "max", "height", "disabled", "align",
"msg", "link", "href", "action", "head", "tbody", "!--" };
private final static HashSet VALID_TAGS_SET = new HashSet(301);
static
{
VALID_TAGS_SET.addAll(Arrays.asList(VALID_TAGS));
}
private int _loadedFiles;
private long _bytesBuffLen;
/**
*
* @return
*/
public static HtmCache getInstance()
{
return SingletonHolder.INSTANCE;
}
private HtmCache()
{
init(Config.DATAPACK_ROOT);
}
/**
*
*/
public void reload()
{
HtmTemplateEngine.getInstance().reload();
init(Config.DATAPACK_ROOT);
}
/**
*
* @param f
*/
public void init(File f)
{
if (!Config.LAZY_CACHE)
{
LOG.info("Html cache start...");
parseDir(f);
LOG.info("Cache[HTML]: {} megabytes on {} files loaded", String.format("%.3f", getMemoryUsage()), getLoadedFiles());
}
else
{
_cache.clear();
_loadedFiles = 0;
_bytesBuffLen = 0;
LOG.info("Cache[HTML]: Running lazy cache");
}
}
/**
*
* @param f
*/
public void reloadPath(File f)
{
parseDir(f);
LOG.info("Cache[HTML]: Reloaded specified path");
}
/**
*
* @return
*/
public double getMemoryUsage()
{
return ((float) _bytesBuffLen / 1048576);
}
/**
*
* @return
*/
public int getLoadedFiles()
{
return _loadedFiles;
}
private static class HtmFilter implements FileFilter
{
public HtmFilter()
{
}
@Override
public boolean accept(File file)
{
if (!file.isDirectory())
{
return (file.getName().endsWith(".htm") || file.getName().endsWith(".html"));
}
return true;
}
}
private void parseDir(File dir)
{
final FileFilter filter = new HtmFilter();
final File[] files = dir.listFiles(filter);
for (File file : files)
{
if (!file.isDirectory())
loadFile(file);
else
parseDir(file);
}
}
/**
*
* @param file
* @return
*/
public String loadFile(File file)
{
final String relpath = Util.getRelativePath(Config.DATAPACK_ROOT, file);
final HtmFilter filter = new HtmFilter();
if (file.exists() && filter.accept(file) && !file.isDirectory())
{
String content;
FileInputStream fis = null;
try
{
fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
final int bytes = bis.available();
final byte[] raw = new byte[bytes];
bis.read(raw);
bis.close();
content = new String(raw, "UTF-8");
content = content.replaceAll("\r\n", "\n");
content = HtmTemplateEngine.getInstance().prepare(content, file.getAbsolutePath());
String oldContent = _cache.get(relpath);
if (oldContent == null)
{
_bytesBuffLen += bytes;
_loadedFiles++;
}
else
{
_bytesBuffLen = _bytesBuffLen - oldContent.length() + bytes;
}
if(!Config.LAZY_CACHE)
validate(file.getPath(), content);
_cache.put(relpath, content);
return content;
}
catch (Exception e)
{
LOG.error("Cache[HTML]: Problem with htm file {}", file.getAbsolutePath(), e);
}
finally
{
ResourceUtil.closeInputStream(fis);
}
}
return null;
}
/**
*
* @param path
* @param player
* @return
*/
public String getHtmForce(String path, L2PcInstance player)
{
String content = getHtm(path, player);
if (content == null)
{
content = "My text is missing:
" + path + "";
LOG.warn("Cache[HTML]: Missing HTML page: {}", path);
}
return content;
}
/**
*
* @param path
* @param player
* @return
*/
public String getHtm(String path, L2PcInstance player)
{
return getHtm(path, player, null);
}
/**
*
* @param path
* @param player
* @param htmlPrefix - Temp parameter for community board caching, should be removed asap
* @return
*/
public String getHtm(String path, L2PcInstance player, String htmlPrefix)
{
String newPath = null;
String content;
if (player != null)
{
newPath = player.getHtmlPrefix() + path;
content = getHtm(newPath);
if (content != null)
return HtmTemplateEngine.getInstance().parse(content, player, path);
} else if(htmlPrefix != null && !htmlPrefix.isEmpty()) {
newPath = htmlPrefix + path;
content = getHtm(newPath);
if (content != null)
return content;
}
content = getHtm(path);
if (content != null && newPath != null)
_cache.put(newPath, content);
return HtmTemplateEngine.getInstance().parse(content, player, path);
}
private String getHtm(String path)
{
if (path == null || path.isEmpty())
return ""; // avoid possible NPE
String content = _cache.get(path);
if (Config.LAZY_CACHE && content == null)
content = loadFile(new File(Config.DATAPACK_ROOT, path));
return content;
}
/**
*
* @param path
* @return
*/
public boolean contains(String path)
{
return _cache.containsKey(path);
}
/**
* Check if an HTM exists and can be loaded
*
* @param path
* The path to the HTM
* @return
* */
public boolean isLoadable(String path)
{
File file = new File(Config.DATAPACK_ROOT, path);
HtmFilter filter = new HtmFilter();
if (file.exists() && filter.accept(file) && !file.isDirectory())
return true;
return false;
}
private void validate(String filename, String html)
{
outer: for (int begin = 0; (begin = html.indexOf('<', begin)) != -1; begin++)
{
int end;
for (end = begin; end < html.length(); end++)
{
if (html.charAt(end) == '>' || html.charAt(end) == ' ')
break;
// some special quest-replaced tag
if (end == begin + 1 && html.charAt(end) == '?')
continue outer;
}
end++;
String tag = html.substring(begin + 1, end - 1).toLowerCase().replaceAll("/", "");
if (!VALID_TAGS_SET.contains(tag))
{
LOG.info("Cache[HTML]: Invalid tag used: '{}' at pos {} {}", new Object[] {tag, (begin + 1), filename});
}
}
}
@SuppressWarnings("synthetic-access")
private static class SingletonHolder
{
protected static final HtmCache INSTANCE = new HtmCache();
}
}