org.thymeleaf.templateparser.raw.RawParser 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-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.templateparser.raw;
import java.io.Reader;
import java.io.StringReader;
import java.util.Arrays;
/*
* The RawParser is very silly: it will just read the resource using its buffers and issue a handleText event.
*
* Note that, instead of using AttoParser's IMarkupParser interface, the much simpler ITextHandler is used here instead.
*
* @author Daniel Fernandez
* @since 3.0.0
*
*/
final class RawParser {
private final BufferPool pool;
RawParser(final int poolSize, final int bufferSize) {
super();
this.pool = new BufferPool(poolSize, bufferSize);
}
public void parse(final String document, final IRawHandler handler)
throws RawParseException {
if (document == null) {
throw new IllegalArgumentException("Document cannot be null");
}
parse(new StringReader(document), handler);
}
public void parse(
final Reader reader, final IRawHandler handler)
throws RawParseException {
if (reader == null) {
throw new IllegalArgumentException("Reader cannot be null");
}
if (handler == null) {
throw new IllegalArgumentException("Handler cannot be null");
}
parseDocument(reader, this.pool.poolBufferSize, handler);
}
/*
* This method receiving the buffer size with package visibility allows
* testing different buffer sizes.
*/
void parseDocument(final Reader reader, final int suggestedBufferSize, final IRawHandler handler)
throws RawParseException {
// We are trying to read the entire resource into the buffer. If it's not big enough,
// then it will have to be grown. Note grown buffers are not maintained by the pool,
// so template cache should play a more important role for this, allowing the system to
// not require the creation of large buffers each time.
final long parsingStartTimeNanos = System.nanoTime();
char[] buffer = null;
try {
handler.handleDocumentStart(parsingStartTimeNanos, 1, 1);
int bufferSize = suggestedBufferSize;
buffer = this.pool.allocateBuffer(bufferSize);
int bufferContentSize = reader.read(buffer);
boolean cont = (bufferContentSize != -1);
while (cont) {
if (bufferContentSize == bufferSize) {
// Buffer is not big enough, double it!
char[] newBuffer = null;
try {
bufferSize *= 2;
newBuffer = this.pool.allocateBuffer(bufferSize);
System.arraycopy(buffer, 0, newBuffer, 0, bufferContentSize);
this.pool.releaseBuffer(buffer);
buffer = newBuffer;
} catch (final Exception ignored) {
this.pool.releaseBuffer(newBuffer);
}
}
final int read = reader.read(buffer, bufferContentSize, (bufferSize - bufferContentSize));
if (read != -1) {
bufferContentSize += read;
} else {
cont = false;
}
}
handler.handleText(buffer, 0, bufferContentSize, 1, 1);
final int[] lastLineCol = computeLastLineCol(buffer, bufferContentSize);
final long parsingEndTimeNanos = System.nanoTime();
handler.handleDocumentEnd(parsingEndTimeNanos, (parsingEndTimeNanos - parsingStartTimeNanos), lastLineCol[0], lastLineCol[1]);
} catch (final RawParseException e) {
throw e;
} catch (final Exception e) {
throw new RawParseException(e);
} finally {
this.pool.releaseBuffer(buffer);
try {
reader.close();
} catch (final Throwable ignored) {
// This exception can be safely ignored
}
}
}
private static int[] computeLastLineCol(final char[] buffer, final int bufferContentSize) {
if (bufferContentSize == 0) {
return new int[] {1, 1};
}
int line = 1;
int col = 1;
char c;
int lastLineFeed = 0;
int n = bufferContentSize;
int i = 0;
while (n-- != 0) {
c = buffer[i];
if (c == '\n') {
line++;
lastLineFeed = i;
}
i++;
}
col = bufferContentSize - lastLineFeed;
return new int[] {line, col};
}
/*
* This class models a pool of buffers, used to keep the amount of
* large char[] buffer objects required to operate to a minimum.
*
* Note this pool never blocks, so if a new buffer is needed and all
* are currently allocated, a new char[] object is created and returned.
*
*/
private static final class BufferPool {
private final char[][] pool;
private final boolean[] allocated;
private final int poolBufferSize;
private BufferPool(final int poolSize, final int poolBufferSize) {
super();
this.pool = new char[poolSize][];
this.allocated = new boolean[poolSize];
this.poolBufferSize = poolBufferSize;
for (int i = 0; i < this.pool.length; i++) {
this.pool[i] = new char[this.poolBufferSize];
}
Arrays.fill(this.allocated, false);
}
private synchronized char[] allocateBuffer(final int bufferSize) {
if (bufferSize != this.poolBufferSize) {
// We will only pool buffers of the default size. If a different size is required, we just
// create it without pooling.
return new char[bufferSize];
}
for (int i = 0; i < this.pool.length; i++) {
if (!this.allocated[i]) {
this.allocated[i] = true;
return this.pool[i];
}
}
return new char[bufferSize];
}
private synchronized void releaseBuffer(final char[] buffer) {
if (buffer == null) {
return;
}
if (buffer.length != this.poolBufferSize) {
// This buffer cannot be part of the pool - only buffers with a specific size are contained
return;
}
for (int i = 0; i < this.pool.length; i++) {
if (this.pool[i] == buffer) {
// Found it. Mark it as non-allocated
this.allocated[i] = false;
return;
}
}
// The buffer wasn't part of our pool. Just return.
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy