/*
* Copyright 2013 eXo Platform SAS
*
* 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 juzu.impl.plugin.template.metamodel;
import juzu.impl.common.CycleDetectionException;
import juzu.impl.common.Logger;
import juzu.impl.common.Name;
import juzu.impl.compiler.BaseProcessor;
import juzu.impl.plugin.application.metamodel.ApplicationMetaModel;
import juzu.impl.metamodel.MetaModelObject;
import juzu.impl.common.JSON;
import juzu.impl.common.Path;
import juzu.impl.template.spi.TemplateProvider;
import juzu.template.TagHandler;
import javax.lang.model.element.Element;
import javax.tools.FileObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
public abstract class AbstractContainerMetaModel extends MetaModelObject implements Iterable<TemplateMetaModel> {
/** . */
private static final Logger log = BaseProcessor.getLogger(AbstractEmitter.class);
/** . */
ApplicationMetaModel application;
/** . */
Name qn;
/** . */
AbstractEmitter emitter;
/** . */
TemplateMetaModelPlugin plugin;
/** . */
final Name name;
/** . */
final HashMap<Path.Absolute, TemplateMetaModel> templates;
public AbstractContainerMetaModel(Name name) {
this.name = name;
this.templates = new HashMap<Path.Absolute, TemplateMetaModel>();
}
@Override
public JSON toJSON() {
JSON json = new JSON();
json.map("values", getChildren(TemplateMetaModel.class));
json.set("qn", qn);
return json;
}
final TagHandler resolveTagHandler(String name) {
TagContainerMetaModel tags = application.getChild(TagContainerMetaModel.KEY);
TagHandler handler = tags.resolveApplicationTagHandler(name);
if (handler == null) {
handler = plugin.tags.get(name);
}
return handler;
}
final void postActivate(TemplateMetaModelPlugin plugin) {
this.plugin = plugin;
evictTemplates();
}
void prePassivate() {
emitter.prePassivate();
plugin = null;
}
void postProcessEvents() {
resolve();
emit();
}
public Path.Absolute resolvePath(Path path) {
return qn.resolve(path);
}
public ApplicationMetaModel getApplication() {
return application;
}
public Name getQN() {
return qn;
}
public TemplateMetaModel get(Path.Absolute path) {
return templates.get(path);
}
public Iterator<TemplateMetaModel> iterator() {
return getChildren(TemplateMetaModel.class).iterator();
}
/**
* Evict templates that are out of date.
*/
private void evictTemplates() {
log.info("Synchronizing existing templates");
for (TemplateMetaModel template : templates.values()) {
if (template.templateModel != null) {
FileObject resource = application.resolveResource(template.getPath());
if (resource == null) {
// That will generate a template not found error
template.templateModel = null;
log.info("Detected template removal " + template.getPath());
}
else if (resource.getLastModified() > template.templateModel.getLastModified()) {
// That will force the regeneration of the template
template.templateModel = null;
log.info("Detected stale template " + template.getPath());
}
else {
log.info("Template " + template.getPath() + " is valid");
}
}
}
}
void resolve() {
//
for (final TemplateMetaModel template : new ArrayList<TemplateMetaModel>(templates.values())) {
if (template.templateModel == null) {
Element[] elements = getElements(template);
application.getProcessingContext().executeWithin(elements[0], new Callable<Void>() {
public Void call() throws Exception {
MetaModelProcessContext processContext = new MetaModelProcessContext(AbstractContainerMetaModel.this, template);
processContext.resolve(template);
return null;
}
});
}
}
}
void emit() {
// Generate missing files from template
for (TemplateMetaModel template : templates.values()) {
Element[] elements = getElements(template);
emitter.emit(template, elements);
}
}
protected abstract Element[] getElements(TemplateMetaModel template);
public Template add(Path.Relative path, List<TemplateRefMetaModel> ref) {
return add(resolvePath(path), ref);
}
public Template add(Path.Absolute path, Iterable<TemplateRefMetaModel> refs) {
if (qn.isPrefix(path.getName())) {
TemplateMetaModel template = templates.get(path);
if (template == null) {
template = new TemplateMetaModel(this, path);
}
for (TemplateRefMetaModel ref : refs) {
try {
ref.add(template);
}
catch (CycleDetectionException e) {
// We have a template cycle and we want to prevent it
StringBuilder path1 = new StringBuilder();
for (Object node : e.getPath()) {
if (path1.length() > 0) {
path1.append("->");
}
if (node instanceof TemplateMetaModel) {
TemplateMetaModel templateNode = (TemplateMetaModel)node;
path1.append(templateNode.getPath().getValue());
} else {
// WTF ?
path1.append(node);
}
}
throw TemplateMetaModel.TEMPLATE_CYCLE.failure(path, path1);
}
}
return template;
} else {
return new Template() {
// Unmanaged template
};
}
}
protected abstract AbstractEmitter createEmitter();
protected abstract TemplateProvider<?> resolveTemplateProvider(String ext);
@Override
protected void postAttach(MetaModelObject parent) {
if (parent instanceof ApplicationMetaModel) {
this.application = (ApplicationMetaModel)parent;
this.qn = application.getName().append(name);
this.emitter = createEmitter();
}
}
}