package greencode.kernel;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import com.google.gson.Gson;
import com.googlecode.htmlcompressor.compressor.HtmlCompressor;
import greencode.exception.GreencodeError;
import greencode.jscript.dom.FunctionHandle;
import greencode.jscript.dom.Window;
import greencode.jscript.dom.window.annotation.Page;
import greencode.util.FileUtils;
import greencode.util.GenericReflection;
import greencode.util.LogMessage;
import greencode.util.MergedFile;
import greencode.util.StringUtils;
public final class FileWeb {
final static Map<String, FileWeb> files = new HashMap<String, FileWeb>();
private final static HashSet<String> requestsCached = new HashSet<String>();
FileWeb(Class<? extends Window> window, Page pageAnnotation) {
this.window = window;
this.pageAnnotation = pageAnnotation;
}
FileWeb() {
this(null, null);
}
final Class<? extends Window> window;
final Page pageAnnotation;
private File file;
private String content, selector, selectedContent, ajaxSelector, ajaxSelectedContent;
long lastModified;
List<FileWeb> inserted;
Document document;
private FileWeb mobileFile;
private FileWeb getCurrent(GreenContext context) {
return mobileFile != null && context.getRequest().isMobile() ? mobileFile : this;
}
String getSelectedContent(String selector, GreenContext context) {
FileWeb current = getCurrent(context);
if(current.selector == null) {
current.selector = selector;
current.selectedContent = current.document.select(selector).html();
}
return current.selectedContent;
}
String getAjaxSelectedContent(String selector, GreenContext context) {
FileWeb current = getCurrent(context);
if(current.ajaxSelector == null) {
current.ajaxSelector = selector;
current.ajaxSelectedContent = current.document.select(selector).html();
}
return current.ajaxSelectedContent;
}
String getContent(GreenContext context) {
return getCurrent(context).content;
}
void setContent(String content) {
this.content = content;
}
private void updateModifiedDate() {
lastModified = file.lastModified();
}
private boolean changed() {
return file != null && lastModified != file.lastModified();
}
private void verifyChanges() {
verifyChanges(this, true);
}
private boolean verifyChanges(final FileWeb file, boolean isPrincipal) {
boolean changed = false;
if(file.inserted != null) {
for(FileWeb i: file.inserted) {
if(verifyChanges(i, false))
changed = true;
}
}
changed = changed || file.changed();
if(changed) {
try {
file.selector = null;
file.ajaxSelector = null;
file.lastModified = 0;
FileWeb.loadStructure(file.file, null, isPrincipal);
} catch(IOException e) {
throw new GreencodeError(e);
}
}
return changed;
}
private static FileWeb loadStructure(File file) throws IOException {
return loadStructure(file, null, false);
}
// TODO: Verificar futuramente para possíveis otimizações.
private static FileWeb loadStructure(File file, FileWeb fileWeb, boolean importCoreJS) throws IOException {
final String ext = FileUtils.getExtension(file.getName());
final boolean
isCss = ext.equals("css"),
isJs = ext.equals("js"),
isView = ext.equals("html") || ext.equals("xhtml") || ext.equals("jsp") || ext.equals("htm");
if(isCss || isJs || isView) {
List<FileWeb> inserted = null;
String content = null, path = null;
Document src = null;
if(isView) {
if(fileWeb == null) {
path = file.toURI().toURL().getPath();
path = path.substring(path.indexOf("WEB-INF/classes/../../") + 22);
fileWeb = files.get(path);
} else
path = fileWeb.pageAnnotation.path();
if(fileWeb != null && !fileWeb.changed())
return fileWeb;
inserted = new ArrayList<FileWeb>();
content = FileUtils.getContentFile(file.toURI().toURL(), GreenCodeConfig.Server.View.charset).replaceAll(Pattern.quote("GREENCODE:{CONTEXT_PATH}"), Core.CONTEXT_PATH);
int lastIndex = 0;
String startString = "GREENCODE:{(";
while((lastIndex = content.indexOf(startString, lastIndex)) != -1) {
final int listCloseIndex = content.indexOf('}', lastIndex);
final String c = content.substring(lastIndex + startString.length(), listCloseIndex);
try {
int closeIndex = c.indexOf(')');
Class<?> clazz = Class.forName(c.substring(0, closeIndex));
content = content.replaceAll(Pattern.quote(startString + c + '}'), GenericReflection.getValue(clazz, c.substring(closeIndex + 2), null).toString());
} catch(ClassNotFoundException e1) {
e1.printStackTrace();
}
lastIndex = listCloseIndex;
}
src = Jsoup.parse(content, GreenCodeConfig.Server.View.charset);
List<Element> listSelf = src.getElementsByTag("template:import");
if(!listSelf.isEmpty()) {
Element ele = listSelf.get(0);
Document templateImported = null;
String templateName = ele.attr("name");
if(templateName != null && !templateName.isEmpty()) {
File f = Cache.templates.get(templateName);
if(f != null) {
FileWeb template = loadStructure(f);
if(!GreenCodeConfig.Server.View.bootable)
inserted.add(template);
templateImported = template.document;
} else
throw new GreencodeError(LogMessage.getMessage("green-0042", templateName));
} else {
FileWeb template = loadStructure(Cache.defaultTemplate);
templateImported = template.document;
if(!GreenCodeConfig.Server.View.bootable)
inserted.add(template);
}
if(templateImported != null) {
ele.remove();
src.head().replaceWith(templateImported.head().clone());
src.body().replaceWith(templateImported.body().clone().append(src.body().html()));
}
String title = ele.attr("title");
if(title != null && !title.isEmpty()) {
Elements e = src.getElementsByTag("title");
if(!e.isEmpty())
e.get(0).text(title);
}
List<Element> elementsHead = src.getElementsByTag("template:head");
if(!elementsHead.isEmpty()) {
for(Element e: elementsHead) {
src.head().append(e.html());
e.remove();
}
}
List<Element> elementsDefine = src.getElementsByTag("template:define");
if(!elementsDefine.isEmpty()) {
List<Element> elementsInsert = src.getElementsByTag("template:insert");
if(!elementsInsert.isEmpty()) {
for(Element eInsert: elementsInsert) {
for(Element eDefine: elementsDefine) {
if(eInsert.attr("name").equals(eDefine.attr("name"))) {
eInsert.after(eDefine.html()).remove();
eDefine.remove();
}
}
}
}
}
}
List<Element> elementsInclude = src.getElementsByTag("template:include");
for(Element element: elementsInclude) {
String attrSrc = element.attr("src");
if(attrSrc != null && !attrSrc.isEmpty()) {
File f = new File(file.getParentFile().getAbsolutePath() + "/" + attrSrc);
try {
FileWeb fw = loadStructure(f);
if(!GreenCodeConfig.Server.View.bootable)
inserted.add(fw);
if(element.hasAttr("head"))
src.head().append(fw.content);
else
element.after(fw.content);
element.remove();
} catch(IOException e) {
throw new GreencodeError(LogMessage.getMessage("green-0020", attrSrc, "template:include", file.getName()));
}
}
}
List<Element> joins = src.head().getElementsByAttribute("join");
for(Element e: joins) {
String filePath = e.attr("file");
if(filePath.isEmpty())
throw new GreencodeError(LogMessage.getMessage("green-0021", "file", e.tagName(), file.getName()));
String[] filesName = e.attr("join").split(",");
File[] files = new File[filesName.length];
for(int i = -1; ++i < filesName.length;) {
String name = filesName[i].trim();
File f = FileUtils.getFileInWebContent(name);
if(!f.exists()) {
throw new GreencodeError(LogMessage.getMessage("green-0020", name, e.tagName(), file.getName()));
}
files[i] = f;
}
MergedFile mergedFile = new MergedFile(FileUtils.getFileInWebContent(e.attr("file")).toURI(), files);
Cache.mergedFiles.put(filePath, mergedFile);
e.removeAttr("join").removeAttr("file");
}
if(importCoreJS)
src.head().prepend("<script type=\"text/javascript\" src=\"" + Core.SRC_CORE_JS_FOR_SCRIPT_HTML + "\" charset=\""+GreenCodeConfig.Server.View.charset+"\"></script>");
content = src.html();
} else
content = FileUtils.getContentFile(file.toURI().toURL());
if(GreenCodeConfig.Server.View.useMinified) {
HtmlCompressor html = new HtmlCompressor();
html.setRemoveIntertagSpaces(true);
content = html.compress(content);
}
if(isView) {
if(fileWeb == null) {
(fileWeb = new FileWeb()).file = file;
files.put(path, fileWeb);
} else
fileWeb.updateModifiedDate();
fileWeb.content = content;
fileWeb.document = src;
if(!inserted.isEmpty())
fileWeb.inserted = inserted;
src = null;
path = null;
return fileWeb;
} else
FileUtils.createFile(content, file);
}
return null;
}
static void registerPage(ClassLoader classLoader, Class<? extends Window> c, Page page, File greencodeFolder) throws IOException {
String path = page.path();
if(files.containsKey(path) && page.URLName().isEmpty() || !(path = page.URLName()).isEmpty() && files.containsKey(path)) {
Console.warning(LogMessage.getMessage("green-0022", path, c.getSimpleName(), files.get(path).window.getSimpleName()));
} else {
FileWeb pReference = new FileWeb(c, page);
if(!page.mobile().path().isEmpty()) {
FileWeb mobileFileWeb = new FileWeb(c, page);
File file = FileUtils.getFileInWebContent(page.mobile().path());
if(file.exists()) {
mobileFileWeb.file = file;
if(GreenCodeConfig.Server.View.bootable)
loadStructure(file, mobileFileWeb, true);
pReference.mobileFile = mobileFileWeb;
} else
throw new GreencodeError(LogMessage.getMessage("green-0014", page.mobile().path()));
}
if(!page.jsModule().isEmpty()) {
final String modulePath = StringUtils.replace(page.jsModule(), ".", "/") + ".js";
final URL url = classLoader.getResource(modulePath);
if(url == null)
throw new RuntimeException(LogMessage.getMessage("green-0038", modulePath));
try {
final File modulesFolder = new File(greencodeFolder.getPath() + "/modules/");
if(!modulesFolder.exists())
modulesFolder.mkdir();
final StringBuilder methodsJS = new StringBuilder();
final Method[] methods = GenericReflection.getDeclaredMethods(c);
for(Method method: methods) {
if(method.getName().equals(Core.INIT_METHOD_NAME)) {
FunctionHandle func = new FunctionHandle(c, method.getName());
methodsJS.append("var ").append(method.getName()).append("=function(onComplete) {var param =").append(new Gson().toJson(func)).append(";param.viewId = __viewId;param.cid = __cid;param.url = CONTEXT_PATH+param.url;Greencode.core.callRequestMethod(principalElement, {}, {event: 'undefined', onComplete: onComplete}, param, []);};");
}
}
FileUtils.createFile("Greencode.modules." + page.name() + "=function(principalElement, __viewId, __cid){" + methodsJS.toString() + FileUtils.getContentFile(url) + "}", modulesFolder.getPath() + "/" + modulePath.substring(modulePath.lastIndexOf('/')));
} catch(IOException e) {
e.printStackTrace();
}
}
File file = FileUtils.getFileInWebContent(page.path());
if(file.exists()) {
pReference.file = file;
files.put(page.URLName().isEmpty() ? page.path() : page.URLName(), pReference);
if(GreenCodeConfig.Server.View.bootable)
loadStructure(file, pReference, true);
} else
throw new GreencodeError(LogMessage.getMessage("green-0014", page.path()));
}
}
static String getModuleName(String jsModule) {
if(jsModule.isEmpty())
return null;
final String modulePath = StringUtils.replace(jsModule, ".", "/") + ".js";
return modulePath.substring(modulePath.lastIndexOf('/') + 1, modulePath.length() - 3);
}
static FileWeb pathAnalyze(String servletPath, FileWeb fileWeb, HttpServletRequest request) {
if(GreenCodeConfig.Server.View.bootable)
return fileWeb;
try {
boolean isView = false;
if(fileWeb != null) {
if(fileWeb.content != null) {
if(GreenCodeConfig.Server.View.seekChange) {
(fileWeb.mobileFile != null && greencode.http.$HttpRequest.isMobile(request) ? fileWeb.mobileFile : fileWeb).verifyChanges();
}
return fileWeb;
}
servletPath = fileWeb.pageAnnotation.path();
isView = true;
} else {
final String ext = FileUtils.getExtension(servletPath);
final boolean isCss = ext.equals("css"), isJs = ext.equals("js");
if((GreenCodeConfig.Server.View.seekChange) && (isCss || isJs)) {
MergedFile mergedFile = Cache.mergedFiles.get(servletPath);
if(mergedFile != null)
mergedFile.verifyChanges();
}
isView = ext.equals("html") || ext.equals("xhtml") || ext.equals("jsp") || ext.equals("htm");
if(!isCss && !isJs && !isView)
return fileWeb;
}
if(fileWeb == null && !requestsCached.contains(servletPath) || fileWeb != null && fileWeb.document == null) {
File file = FileUtils.getFileInWebContent(servletPath);
if(file != null && file.exists()) {
if(isView) {
Console.log("Applying (template" + (GreenCodeConfig.Server.View.useMinified ? ", minified" : "") + ") in " + servletPath);
} else if(GreenCodeConfig.Server.View.useMinified) {
Console.log("Applying (minified) in " + servletPath);
}
fileWeb = loadStructure(file, fileWeb, true);
if(fileWeb != null && fileWeb.mobileFile != null)
loadStructure(fileWeb.mobileFile.file, fileWeb.mobileFile, true);
requestsCached.add(servletPath);
}
}
} catch(IOException e) {
throw new GreencodeError(e);
}
return fileWeb;
}
}