/* * 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(); } } }