All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.enhydra.xml.xmlc.misc.SSIParsedStream Maven / Gradle / Ivy

The newest version!
/*
 * Enhydra Java Application Server Project
 *
 * The contents of this file are subject to the Enhydra Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License on
 * the Enhydra web site ( http://www.enhydra.org/ ).
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific terms governing rights and limitations
 * under the License.
 *
 * The Initial Developer of the Enhydra Application Server is Lutris
 * Technologies, Inc. The Enhydra Application Server and portions created
 * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
 * All Rights Reserved.
 *
 * Contributor(s):
 *
 * $Id: SSIParsedStream.java,v 1.2 2005/01/26 08:29:24 jkjome Exp $
 */

package org.enhydra.xml.xmlc.misc;

import java.io.IOException;
import java.io.Reader;

import org.enhydra.xml.io.InputSourceOps;
import org.xml.sax.InputSource;

/**
 * Implements the reading and parsing of an SSI stream.  When a SSI directive
 * is encountered, a new instance of this class is created and pushed on a
 * stack.
 * 

* To simplified the implementation, the entire file is read into a buffer. * A previous version attempted to buffer less, but this ended up being a bit * tricky due to the need to scan while reading and still maintain the block * read() method. */ final class SSIParsedStream { /** * Character returned to indicate EOF. */ public static final int AT_EOF = -1; /** * Character returned to indicate the beginning of an * SSI directive. */ public static final int AT_SSI = -2; /** * SSI directive prefix definitions. */ private static final String SSI_PREFIX_STR = ""; private static final int SSI_SUFFIX_LEN = SSI_SUFFIX_STR.length(); private static final char[] SSI_SUFFIX; /** Initial buffer size to use. May grow as needed. */ private static final int INIT_BUFFER_SIZE = 8*1024; /** Maximum nesting depth of SSIs. */ private static final int MAX_NESTING_DEPTH = 64; /** Initial StringBuffer size for a SSI directive argument */ private static final int SSI_INIT_ARG_STRING_SIZE = 64; /** Input source that was used. */ private InputSource fSource; /** * Next stream in the stack. This was the one that included this * file. */ private SSIParsedStream fPrevStream; /** * Nesting depth. */ private int fDepth; /** * Buffer for input, contains the entire file. */ private char[] fBuffer; /** * Index of the next character in the buffer. */ private int fNextCharIdx; /** * Index of the last valid character in the buffer. */ private int fLastCharIdx; /** * Index of the start of the next SSI directive in the buffer. Set to * fLastCharIdx+1 if no SSI directive is contained. */ private int fSSIStartIdx; /** * Map of stream position to source file and line number. Shared by all * SSIParsedStream. */ private LineNumberRecorder fLineNumbers; /** * Class initializer */ static { SSI_PREFIX = new char[SSI_PREFIX_LEN]; SSI_PREFIX_STR.getChars(0, SSI_PREFIX_LEN, SSI_PREFIX, 0); SSI_SUFFIX = new char[SSI_SUFFIX_LEN]; SSI_SUFFIX_STR.getChars(0, SSI_SUFFIX_LEN, SSI_SUFFIX, 0); } /** * Constructor. Open the named file. */ public SSIParsedStream(InputSource source, LineNumberRecorder lineNumbers) throws IOException { this(source, lineNumbers, null); } /** * Constructor. Open the named file. */ public SSIParsedStream(InputSource source, LineNumberRecorder lineNumbers, SSIParsedStream prevStream) throws IOException { if (source.getSystemId() == null) { throw new IOException("SSI InputSource must have a system id"); } fSource = source; fLineNumbers = lineNumbers; fLineNumbers.pushFile(getSystemId()); if (prevStream != null) { fSource.setEncoding(prevStream.fSource.getEncoding()); fPrevStream = prevStream; fDepth = prevStream.fDepth + 1; if (fDepth > MAX_NESTING_DEPTH) { throw new IOException("SSI max nesting depth exceeded at: " + getSystemId()); } } readIntoBuffer(fSource); } /** * Expand the buffer for the next read. */ private void expandBuffer() { char[] newBuffer = new char[fBuffer.length * 2]; System.arraycopy(fBuffer, 0, newBuffer, 0, fBuffer.length); fBuffer = newBuffer; } /** * Read the file into the buffer. */ private void readIntoBuffer(Reader in) throws IOException { int charsRead; int readIdx = 0; fBuffer = new char[INIT_BUFFER_SIZE]; while (true) { if (readIdx >= fBuffer.length) { expandBuffer(); } charsRead = in.read(fBuffer, readIdx, fBuffer.length - readIdx); if (charsRead < 0) { break; } readIdx += charsRead; } fNextCharIdx = 0; fLastCharIdx = readIdx - 1; // Need by scanForSSIStart below fSSIStartIdx = scanForSSIStart(fNextCharIdx); } /** * Open the file and read it into the buffer. */ private void readIntoBuffer(InputSource source) throws IOException { Reader in = InputSourceOps.open(source); try { readIntoBuffer(in); } finally { InputSourceOps.closeIfOpened(source, in); } } /** * Get the system id of the associated file. */ public String getSystemId() { return fSource.getSystemId(); } /** * Close this input stream, returning the previous stream. */ public SSIParsedStream pop() throws IOException { fLineNumbers.popFile(); return fPrevStream; } /** * Check for SSI prefix at specified offset in buffer, reading more * into the buffer if necessary to hold an entire prefix. */ private boolean isSSIPrefix(int idx) throws IOException { for (int prefixIdx = 0; prefixIdx < SSI_PREFIX_LEN; prefixIdx++, idx++) { if (fBuffer[idx] != SSI_PREFIX[prefixIdx]) { return false; } } return true; } /** * Check for the SSI suffix (comment close) at specified offset in buffer. */ private boolean isSSISuffix(int idx) throws IOException { for (int suffixIdx = 0; suffixIdx < SSI_SUFFIX_LEN; suffixIdx++, idx++) { if (fBuffer[idx] != SSI_SUFFIX[suffixIdx]) { return false; } } return true; } /** * Scan for the start of an SSI directive in the buffer. This checks for * the `