/* * Copyright 2014 Martin Kouba * * 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 org.trimou.engine.parser; import java.util.Collections; import java.util.List; import java.util.ListIterator; import java.util.Map; import org.trimou.Mustache; import org.trimou.annotations.Internal; import org.trimou.engine.MustacheEngine; import org.trimou.engine.context.ExecutionContext; import org.trimou.engine.context.ExecutionContexts; import org.trimou.engine.listener.MustacheListener; import org.trimou.engine.listener.MustacheRenderingEvent; import org.trimou.engine.resource.AbstractReleaseCallbackContainer; import org.trimou.engine.segment.RootSegment; import org.trimou.exception.MustacheException; import org.trimou.exception.MustacheProblem; import org.trimou.util.ImmutableMap; import org.trimou.util.ImmutableMap.ImmutableMapBuilder; /** * A Mustache template. * * @author Martin Kouba */ @Internal public class Template implements Mustache { private final long generatedId; private final String name; private final MustacheEngine engine; private final ExecutionContext globalExecutionContext; private final Map<String, Template> nestedTemplates; private volatile Template parent; private volatile RootSegment rootSegment; /** * * @param generatedId * @param name * @param engine */ public Template(Long generatedId, String name, MustacheEngine engine) { this(generatedId, name, engine, null); } /** * * @param generatedId * @param name * @param engine * @param nestedTemplates */ public Template(Long generatedId, String name, MustacheEngine engine, List<Template> nestedTemplates) { this.generatedId = generatedId; this.name = name; this.engine = engine; this.globalExecutionContext = ExecutionContexts .newGlobalExecutionContext(engine.getConfiguration()); if (nestedTemplates == null || nestedTemplates.isEmpty()) { this.nestedTemplates = Collections.emptyMap(); } else { ImmutableMapBuilder<String, Template> builder = ImmutableMap .builder(); for (Template template : nestedTemplates) { builder.put(template.getName(), template); } this.nestedTemplates = builder.build(); } } @Override public Long getGeneratedId() { return generatedId; } @Override public String getName() { return name; } @Override public String render(Object data) { StringBuilder builder = new StringBuilder(); render(builder, data); return builder.toString(); } @Override public void render(Appendable appendable, Object data) { DefaultMustacheRenderingEvent event = new DefaultMustacheRenderingEvent( engine.getConfiguration().getIdentifierGenerator() .generate(MustacheRenderingEvent.class)); try { renderingStarted(event); appendable = rootSegment.execute(appendable, data != null ? globalExecutionContext.setContextObject(data) : globalExecutionContext); // We need for flush the async appendable if needed RootSegment.flushAsyncAppendable(appendable); renderingFinished(event); } finally { event.release(); } } public RootSegment getRootSegment() { return rootSegment; } synchronized void initRootSegment(RootSegment rootSegment) { if (this.rootSegment != null) { throw new MustacheException( MustacheProblem.TEMPLATE_MODIFICATION_NOT_ALLOWED); } this.rootSegment = rootSegment; } synchronized void initParent(Template parent) { if (this.parent != null) { throw new MustacheException( MustacheProblem.TEMPLATE_MODIFICATION_NOT_ALLOWED); } this.parent = parent; } public MustacheEngine getEngine() { return engine; } public Template getNestedTemplate(String name) { return parent != null ? parent.getNestedTemplate(name) : nestedTemplates.get(name); } private void renderingStarted(MustacheRenderingEvent event) { List<MustacheListener> listeners = engine.getConfiguration() .getMustacheListeners(); if (listeners.isEmpty()) { return; } for (MustacheListener listener : listeners) { listener.renderingStarted(event); } } private void renderingFinished(MustacheRenderingEvent event) { List<MustacheListener> listeners = engine.getConfiguration() .getMustacheListeners(); if (listeners.isEmpty()) { return; } for (ListIterator<MustacheListener> iterator = listeners .listIterator(listeners.size()); iterator.hasPrevious();) { iterator.previous().renderingFinished(event); } } /** * * @author Martin Kouba */ private class DefaultMustacheRenderingEvent extends AbstractReleaseCallbackContainer implements MustacheRenderingEvent { private final long id; /** * * @param id */ public DefaultMustacheRenderingEvent(long id) { this.id = id; } @Override public String getMustacheName() { return name; } @Override public long getMustacheGeneratedId() { return generatedId; } @Override public Long getGeneratedId() { return id; } } @Override public String toString() { return String.format("Template %s", name); } }