org.thymeleaf.engine.Model Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of thymeleaf Show documentation
Show all versions of thymeleaf Show documentation
Modern server-side Java template engine for both web and standalone environments
/*
* =============================================================================
*
* Copyright (c) 2011-2018, 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 java.util.Arrays;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.exceptions.TemplateProcessingException;
import org.thymeleaf.model.ICDATASection;
import org.thymeleaf.model.ICloseElementTag;
import org.thymeleaf.model.IComment;
import org.thymeleaf.model.IDocType;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelVisitor;
import org.thymeleaf.model.IOpenElementTag;
import org.thymeleaf.model.IProcessingInstruction;
import org.thymeleaf.model.IStandaloneElementTag;
import org.thymeleaf.model.ITemplateEnd;
import org.thymeleaf.model.ITemplateEvent;
import org.thymeleaf.model.ITemplateStart;
import org.thymeleaf.model.IText;
import org.thymeleaf.model.IXMLDeclaration;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.util.FastStringWriter;
import org.thymeleaf.util.Validate;
/**
*
* @author Daniel Fernández
* @since 3.0.0
*
*/
final class Model implements IModel {
private static final int INITIAL_EVENT_QUEUE_SIZE = 50; // 50 events by default, will auto-grow
private IEngineConfiguration configuration;
private TemplateMode templateMode;
IEngineTemplateEvent[] queue;
int queueSize;
Model(final IEngineConfiguration configuration, final TemplateMode templateMode) {
super();
Validate.notNull(configuration, "Engine Configuration cannot be null");
Validate.notNull(templateMode, "Template Mode cannot be null");
this.templateMode = templateMode;
this.configuration = configuration;
this.queue = new IEngineTemplateEvent[INITIAL_EVENT_QUEUE_SIZE];
Arrays.fill(this.queue, null);
this.queueSize = 0;
}
Model(final IModel model) {
super();
Validate.notNull(model, "Model cannot be null");
this.configuration = model.getConfiguration();
this.templateMode = model.getTemplateMode();
if (model instanceof Model) {
final Model mmodel = (Model) model;
this.queue = mmodel.queue.clone();
this.queueSize = mmodel.queueSize;
} else if (model instanceof TemplateModel) {
final TemplateModel templateModel = (TemplateModel) model;
this.queue = new IEngineTemplateEvent[templateModel.queue.length + INITIAL_EVENT_QUEUE_SIZE/2];
System.arraycopy(templateModel.queue, 1, this.queue, 0, templateModel.queue.length - 2);
this.queueSize = templateModel.queue.length - 2;
} else {
this.queue = new IEngineTemplateEvent[INITIAL_EVENT_QUEUE_SIZE];
Arrays.fill(this.queue, null);
this.queueSize = 0;
insertModel(0, model);
}
}
public final IEngineConfiguration getConfiguration() {
return this.configuration;
}
public final TemplateMode getTemplateMode() {
return this.templateMode;
}
public int size() {
return this.queueSize;
}
public ITemplateEvent get(final int pos) {
return this.queue[pos];
}
public void add(final ITemplateEvent event) {
insert(this.queueSize, event);
}
public void insert(final int pos, final ITemplateEvent event) {
if (event == null) {
return;
}
final IEngineTemplateEvent engineEvent = asEngineEvent(event);
// Check that the event that is going to be inserted is not a template start/end
if (engineEvent == TemplateStart.TEMPLATE_START_INSTANCE || engineEvent == TemplateEnd.TEMPLATE_END_INSTANCE) {
throw new TemplateProcessingException(
"Cannot insert event of type TemplateStart/TemplateEnd. These " +
"events can only be added to models internally during template parsing.");
}
// Check there is room for a new event, or grow the queue if not
if (this.queue.length == this.queueSize) {
this.queue = Arrays.copyOf(this.queue, this.queue.length + INITIAL_EVENT_QUEUE_SIZE/2);
}
// Make room for the new event
if (pos != this.queueSize) {
System.arraycopy(this.queue, pos, this.queue, pos + 1, this.queueSize - pos);
}
// Set the new event in its new position
this.queue[pos] = engineEvent;
this.queueSize++;
}
public void replace(final int pos, final ITemplateEvent event) {
if (event == null) {
return;
}
final IEngineTemplateEvent engineEvent = asEngineEvent(event);
// Check that the event that is going to be inserted is not a template start/end
if (engineEvent == TemplateStart.TEMPLATE_START_INSTANCE || engineEvent == TemplateEnd.TEMPLATE_END_INSTANCE) {
throw new TemplateProcessingException(
"Cannot insert event of type TemplateStart/TemplateEnd. These " +
"events can only be added to models internally during template parsing.");
}
// Set the new event in its new position
this.queue[pos] = engineEvent;
}
public void addModel(final IModel model) {
insertModel(this.queueSize, model);
}
public void insertModel(final int pos, final IModel model) {
if (model == null || model.size() == 0) {
return;
}
if (this.configuration != model.getConfiguration()) {
throw new TemplateProcessingException(
"Cannot add model of class " + model.getClass().getName() + " to the current template, as " +
"it was created using a different Template Engine Configuration.");
}
if (this.templateMode != model.getTemplateMode()) {
throw new TemplateProcessingException(
"Cannot add model of class " + model.getClass().getName() + " to the current template, as " +
"it was created using a different Template Mode: " + model.getTemplateMode() + " instead of " +
"the current " + this.templateMode);
}
if (this.queue.length <= (this.queueSize + model.size())) {
// We need to grow the queue!
this.queue = Arrays.copyOf(this.queue, Math.max(this.queueSize + model.size(), this.queue.length + INITIAL_EVENT_QUEUE_SIZE/2));
}
if (model instanceof TemplateModel) {
doInsertTemplateModel(pos, (TemplateModel)model);
} else if (model instanceof Model) {
doInsertModel(pos, (Model)model);
} else {
doInsertOtherModel(pos, model);
}
}
private void doInsertModel(final int pos, final Model model) {
// Make room for the new events (if necessary because pos < this.queueSize)
System.arraycopy(this.queue, pos, this.queue, pos + model.queueSize, this.queueSize - pos);
// Copy the new events to their new position
System.arraycopy(model.queue, 0, this.queue, pos, model.queueSize);
this.queueSize += model.queueSize;
}
private void doInsertTemplateModel(final int pos, final TemplateModel model) {
// We compute the insertion size by subtracting the TemplateStart/TemplateEnd events
final int insertionSize = model.queue.length - 2;
// Make room for the new events (if necessary because pos < this.queueSize)
System.arraycopy(this.queue, pos, this.queue, pos + insertionSize, this.queueSize - pos);
// Copy the new events to their new position
System.arraycopy(model.queue, 1, this.queue, pos, insertionSize);
this.queueSize += insertionSize;
}
private void doInsertOtherModel(final int pos, final IModel model) {
// We know nothing about this model implementation, so we will use the public interface methods
final int modelSize = model.size();
for (int i = 0; i < modelSize; i++) {
insert(pos + i, model.get(i));
}
}
public void remove(final int pos) {
System.arraycopy(this.queue, pos + 1, this.queue, pos, this.queueSize - (pos + 1));
this.queueSize--;
}
public void reset() {
this.queueSize = 0;
}
void process(final ITemplateHandler handler) {
for (int i = 0; i < this.queueSize; i++) {
this.queue[i].beHandled(handler);
}
}
int process(final ITemplateHandler handler, final int offset, final TemplateFlowController controller) {
if (controller == null) {
process(handler);
return this.queueSize;
}
if (this.queueSize == 0 || offset >= this.queueSize) {
return 0;
}
int i = offset;
while (i < this.queueSize && !controller.stopProcessing) {
this.queue[i++].beHandled(handler);
}
return (i - offset);
}
public IModel cloneModel() {
return new Model(this);
}
void resetAsCloneOf(final Model model) {
this.configuration = model.configuration;
this.templateMode = model.templateMode;
if (this.queue.length < model.queueSize) {
this.queue = new IEngineTemplateEvent[model.queueSize];
}
System.arraycopy(model.queue, 0, this.queue, 0, model.queueSize);
this.queueSize = model.queueSize;
}
public final void write(final Writer writer) throws IOException {
for (int i = 0; i < this.queueSize; i++) {
this.queue[i].write(writer);
}
}
public void accept(final IModelVisitor visitor) {
for (int i = 0; i < this.queueSize; i++) {
// We will execute the visitor on the Immutable events, that we need to create during the visit
this.queue[i].accept(visitor);
}
}
// Note we will use object equality for comparing events here - the idea is to check whether
// a model has been changed at all, and replacing an event with an equivalent one would be
// considered "a change" anyway.
boolean sameAs(final Model model) {
if (model == null || model.queueSize != this.queueSize) {
return false;
}
for (int i = 0; i < this.queueSize; i++) {
if (this.queue[i] != model.queue[i]) {
return false;
}
}
return true;
}
@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");
}
}
static IEngineTemplateEvent asEngineEvent(final ITemplateEvent event) {
if (event instanceof IEngineTemplateEvent) {
return (IEngineTemplateEvent)event;
}
if (event instanceof IText) {
return Text.asEngineText((IText) event);
}
if (event instanceof IOpenElementTag) {
return OpenElementTag.asEngineOpenElementTag((IOpenElementTag) event);
}
if (event instanceof ICloseElementTag) {
return CloseElementTag.asEngineCloseElementTag((ICloseElementTag) event);
}
if (event instanceof IStandaloneElementTag) {
return StandaloneElementTag.asEngineStandaloneElementTag((IStandaloneElementTag) event);
}
if (event instanceof IDocType) {
return DocType.asEngineDocType((IDocType) event);
}
if (event instanceof IComment) {
return Comment.asEngineComment((IComment) event);
}
if (event instanceof ICDATASection) {
return CDATASection.asEngineCDATASection((ICDATASection) event);
}
if (event instanceof IXMLDeclaration) {
return XMLDeclaration.asEngineXMLDeclaration((IXMLDeclaration) event);
}
if (event instanceof IProcessingInstruction) {
return ProcessingInstruction.asEngineProcessingInstruction((IProcessingInstruction) event);
}
if (event instanceof ITemplateStart) {
return TemplateStart.asEngineTemplateStart((ITemplateStart) event);
}
if (event instanceof ITemplateEnd) {
return TemplateEnd.asEngineTemplateEnd((ITemplateEnd) event);
}
throw new TemplateProcessingException(
"Cannot handle in event of type: " + event.getClass().getName());
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy