/*
* =============================================================================
*
* Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org)
*
* 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.thymeleaf.engine;
import java.io.IOException;
import java.io.Writer;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.exceptions.TemplateProcessingException;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelVisitor;
import org.thymeleaf.model.ITemplateEvent;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.util.FastStringWriter;
import org.thymeleaf.util.Validate;
/**
*
* @author Daniel Fernández
* @since 3.0.0
*
*/
public final class TemplateModel implements IModel {
final IEngineConfiguration configuration;
final TemplateData templateData;
final IEngineTemplateEvent[] queue; // This is final because this IModel is IMMUTABLE
// Package-protected constructor, because we don't want anyone creating these objects from outside the engine.
// If a processor (be it standard or custom-made) wants to create a piece of model, that should be a Model
// object, not this.
TemplateModel(
final IEngineConfiguration configuration, final TemplateData templateData,
final IEngineTemplateEvent[] queue) {
super();
Validate.notNull(configuration, "Engine Configuration cannot be null");
Validate.notNull(templateData, "Template Resolution cannot be null");
Validate.notNull(queue, "Event queue cannot be null");
Validate.isTrue(queue.length >= 2, "At least TemplateStart/TemplateEnd events must be added to a TemplateModel");
Validate.isTrue(queue[0] == TemplateStart.TEMPLATE_START_INSTANCE, "First event in queue is not TemplateStart");
Validate.isTrue(queue[queue.length - 1] == TemplateEnd.TEMPLATE_END_INSTANCE, "Last event in queue is not TemplateEnd");
this.configuration = configuration;
this.templateData = templateData;
this.queue = queue;
}
public final TemplateData getTemplateData() {
return this.templateData;
}
public final IEngineConfiguration getConfiguration() {
return this.configuration;
}
public final TemplateMode getTemplateMode() {
return this.templateData.getTemplateMode();
}
public final int size() {
return this.queue.length;
}
public final ITemplateEvent get(final int pos) {
return this.queue[pos];
}
public final void add(final ITemplateEvent event) {
immutableModelException();
}
public final void insert(final int pos, final ITemplateEvent event) {
immutableModelException();
}
public final void replace(final int pos, final ITemplateEvent event) {
immutableModelException();
}
public final void addModel(final IModel model) {
immutableModelException();
}
public final void insertModel(final int pos, final IModel model) {
immutableModelException();
}
public final void remove(final int pos) {
immutableModelException();
}
public final void reset() {
immutableModelException();
}
void process(final ITemplateHandler handler) {
for (int i = 0; i < this.queue.length; i++) {
this.queue[i].beHandled(handler);
}
}
int process(final ITemplateHandler handler, final int offset, final TemplateFlowController controller) {
if (controller == null) {
process(handler);
return this.queue.length;
}
if (this.queue.length == 0 || offset >= this.queue.length) {
return 0;
}
int processed = 0;
for (int i = offset; i < this.queue.length && !controller.stopProcessing; i++) {
this.queue[i].beHandled(handler);
processed++;
}
return processed;
}
public final IModel cloneModel() {
return new Model(this);
}
public final void write(final Writer writer) throws IOException {
for (int i = 0; i < this.queue.length; i++) {
this.queue[i].write(writer);
}
}
public void accept(final IModelVisitor visitor) {
for (int i = 0; i < this.queue.length; i++) {
// We will execute the visitor on the Immutable events, that we need to create during the visit
this.queue[i].accept(visitor);
}
}
@Override
public final String toString() {
try {
final Writer writer = new FastStringWriter();
write(writer);
return writer.toString();
} catch (final IOException e) {
throw new TemplateProcessingException(
"Error while creating String representation of model");
}
}
private static void immutableModelException() {
throw new UnsupportedOperationException(
"Modifications are not allowed on immutable model objects. This model object is an immutable " +
"implementation of the " + IModel.class.getName() + " interface, and no modifications are allowed in " +
"order to keep cache consistency and improve performance. To modify model events, convert first your " +
"immutable model object to a mutable one by means of the " + IModel.class.getName() + "#cloneModel() method");
}
}