package act.view.rythm;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* 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.
* #L%
*/
import act.Act;
import act.app.App;
import act.conf.AppConfig;
import act.util.ActContext;
import act.view.Template;
import act.view.VarDef;
import act.view.View;
import org.osgl.util.C;
import org.rythmengine.Rythm;
import org.rythmengine.RythmEngine;
import org.rythmengine.extension.IFormatter;
import org.rythmengine.extension.ISourceCodeEnhancer;
import org.rythmengine.resource.ClasspathResourceLoader;
import org.rythmengine.template.ITemplate;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.rythmengine.conf.RythmConfigurationKey.*;
/**
* Implement a view with Rythm Template Engine
*/
public class RythmView extends View {
public static final String ID = "rythm";
ConcurrentMap<App, RythmEngine> engines = new ConcurrentHashMap<App, RythmEngine>();
ConcurrentMap<String, Template> templates = new ConcurrentHashMap<String, Template>();
ConcurrentMap<String, String> missings = new ConcurrentHashMap<String, String>();
private boolean isDev;
public RythmView() {
isDev = Act.isDev();
}
@Override
public String name() {
return ID;
}
@Override
protected Template loadTemplate(String resourcePath, ActContext context) {
if (isDev) {
return loadTemplateFromResource(resourcePath, context.app());
}
if (missings.containsKey(resourcePath)) {
return null;
}
Template template = templates.get(resourcePath);
if (null == template) {
template = loadTemplateFromResource(resourcePath, context.app());
if (null != template) {
templates.putIfAbsent(resourcePath, template);
} else {
missings.put(resourcePath, resourcePath);
}
}
return template;
}
public RythmEngine getEngine(App app) {
RythmEngine engine = engines.get(app);
if (null == engine) {
engine = createEngine(app);
RythmEngine engine0 = engines.putIfAbsent(app, engine);
if (null != engine0) {
engine.shutdown();
engine = engine0;
}
}
return engine;
}
private Template loadTemplateFromResource(String resourcePath, App app) {
RythmEngine engine = getEngine(app);
return RythmTemplate.find(engine, resourcePath);
}
private RythmEngine createEngine(App app) {
AppConfig config = app.config();
Properties p = new Properties();
p.put(ENGINE_MODE.getKey(), Act.mode().isDev() ? Rythm.Mode.dev : Rythm.Mode.prod);
p.put(ENGINE_PLUGIN_VERSION.getKey(), Act.VERSION);
p.put(ENGINE_CLASS_LOADER_PARENT_IMPL.getKey(), app.classLoader());
p.put(HOME_TMP.getKey(), createTempHome(app));
Map map = config.rawConfiguration();
for (Object k : map.keySet()) {
String key = k.toString();
if (key.startsWith("rythm.")) {
p.put(key, map.get(key));
}
}
String appRestricted = p.getProperty("rythm.sandbox.restricted_classes", "");
appRestricted += ";act.*";
p.put(SANDBOX_RESTRICTED_CLASS.getKey(), appRestricted);
p.put(HOME_TEMPLATE.getKey(), templateRootDir());
p.put(CODEGEN_SOURCE_CODE_ENHANCER.getKey(), new ISourceCodeEnhancer() {
@Override
public List<String> imports() {
return C.list();
}
@Override
public String sourceCode() {
return "";
}
@Override
public Map<String, ?> getRenderArgDescriptions() {
Map<String, String> map = C.newMap();
for (VarDef var : Act.viewManager().implicitActionViewVariables()) {
map.put(var.name(), var.type());
}
return map;
}
@Override
public void setRenderArgs(ITemplate iTemplate) {
// no need to set render args here as
// it's all done at TemplateBase#exposeImplicitVariables
}
});
RythmEngine engine = new RythmEngine(p);
engine.resourceManager().addResourceLoader(new ClasspathResourceLoader(engine, "rythm"));
Tags tags = app.getInstance(Tags.class);
tags.register(engine);
return engine;
}
public void registerTransformer(App app, Class<?> clazz) {
getEngine(app).registerTransformer(clazz);
}
public void registerBuiltInTransformer(App app, Class<?> clazz) {
getEngine(app).registerTransformer("rythm", "([^a-zA-Z0-9_]s\\(\\)|^s\\(\\))", clazz);
}
public void registerFormatter(App app, IFormatter formatter) {
getEngine(app).extensionManager().registerFormatter(formatter);
}
@Override
protected void reload(App app) {
engines.remove(app);
super.reload(app);
}
private File createTempHome(App app) {
String tmp = System.getProperty("java.io.tmpdir");
File f = new File(tmp, "__rythm_" + app.name());
if (!f.exists() && !f.mkdirs()) {
f = new File(tmp);
}
return f;
}
}