org.apache.wicket.util.io.FullyBufferedReader Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.wicket.util.io;
import java.io.IOException;
import java.io.Reader;
import java.text.ParseException;
/**
* This is not a reader like e.g. FileReader. It rather reads the whole data until the end from a
* source reader into memory and besides that it maintains the current position (like a reader) it
* provides String like methods which conveniently let you navigate (usually forward) in the stream.
*
* Because the source data are expected to be text, the line and column numbers are maintained as
* well for location precise error messages. But it does NOT automatically update the line and
* column numbers. You must call {@link #countLinesTo(int)}
*
* @author Juergen Donnerstag
*/
public final class FullyBufferedReader
{
/** All the chars from the resource */
private final String input;
/** Position in parse. */
private int inputPosition;
/** Current line number */
private int lineNumber = 1;
/** current column number. */
private int columnNumber = 1;
/** Last place we counted lines from. */
private int lastLineCountIndex;
/** A variable to remember a certain position in the markup */
private int positionMarker;
/**
* Read all the data from the resource into memory.
*
* @param reader
* The source reader to load the data from
* @throws IOException
*/
public FullyBufferedReader(final Reader reader) throws IOException
{
this(Streams.readString(reader));
}
/**
* Construct.
*
* @param input
* The source string
*/
public FullyBufferedReader(String input)
{
this.input = input;
}
/**
* Get the characters from the position marker to toPos.
*
* If toPos < 0, than get all data from the position marker until the end. If toPos less than
* the current position marker than return an empty string ""
*
* @param toPos
* Index of first character not included
* @return Raw markup (a string) in between these two positions.
*/
public final CharSequence getSubstring(int toPos)
{
if (toPos < 0)
{
toPos = input.length();
}
else if (toPos < positionMarker)
{
return "";
}
return input.subSequence(positionMarker, toPos);
}
/**
* Get the characters from in between both positions including the char at fromPos, excluding
* the char at toPos
*
* @param fromPos
* first index
* @param toPos
* second index
* @return the string (raw markup) in between both positions
*/
public final CharSequence getSubstring(final int fromPos, final int toPos)
{
return input.subSequence(fromPos, toPos);
}
/**
* Gets the current input position
*
* @return input position
*/
public final int getPosition()
{
return inputPosition;
}
/**
* Remember the current position in markup
*
* @param pos
*/
public final void setPositionMarker(final int pos)
{
positionMarker = pos;
}
/**
* @return The markup to be parsed
*/
@Override
public String toString()
{
return input;
}
/**
* Counts lines starting where we last left off up to the index provided.
*
* @param end
* End index
*/
public final void countLinesTo(final int end)
{
for (int i = lastLineCountIndex; i < end; i++)
{
final char ch = input.charAt(i);
if (ch == '\n')
{
columnNumber = 1;
lineNumber++;
}
else if (ch != '\r')
{
columnNumber++;
}
}
lastLineCountIndex = end;
}
/**
* Find a char starting at the current input position
*
* @param ch
* The char to search for
* @return -1 if not found
*/
public final int find(final char ch)
{
return input.indexOf(ch, inputPosition);
}
/**
* Find a char starting at the position provided
*
* @param ch
* The char to search for
* @param startPos
* The index to start at
* @return -1 if not found
*/
public final int find(final char ch, final int startPos)
{
return input.indexOf(ch, startPos);
}
/**
* Find the string starting at the current input position
*
* @param str
* The string to search for
* @return -1 if not found
*/
public final int find(final String str)
{
return input.indexOf(str, inputPosition);
}
/**
* Find the string starting at the position provided
*
* @param str
* The string to search for
* @param startPos
* The index to start at
* @return -1 if not found
*/
public final int find(final String str, final int startPos)
{
return input.indexOf(str, startPos);
}
/**
* Find a char starting at the position provided. The char must not be inside a quoted string
* (single or double)
*
* @param ch
* The char to search for
* @param startPos
* The index to start at
* @return -1 if not found
* @throws ParseException
*/
public int findOutOfQuotes(final char ch, int startPos) throws ParseException
{
return findOutOfQuotes(ch, startPos, (char)0);
}
/**
* Find a char starting at the position provided. The char must not be inside a quoted string
* (single or double)
*
* @param ch
* The char to search for
* @param startPos
* The index to start at
* @param quotationChar
* The current quotation char. Must be ' or ", otherwise will be ignored.
* @return -1 if not found
* @throws ParseException
*/
public int findOutOfQuotes(final char ch, int startPos, char quotationChar)
throws ParseException
{
int closeBracketIndex = find(ch, startPos + 1);
if (closeBracketIndex != -1)
{
CharSequence tagCode = getSubstring(startPos, closeBracketIndex + 1);
for (int i = 0; i < tagCode.length(); i++)
{
char currentChar = tagCode.charAt(i);
char previousTag = tagCode.charAt(i > 0 ? i - 1 : 0);
if (quotationChar == 0 && (currentChar == '\'' || currentChar == '\"'))
{// I'm entering inside a quoted string. Set quotationChar
quotationChar = currentChar;
countLinesTo(startPos + i);
}
else if (currentChar == quotationChar && previousTag != '\\')
{ // I'm out of quotes, reset quotationChar
quotationChar = 0;
}
// I've found character but I'm inside quotes
if (currentChar == ch && quotationChar != 0)
{
return findOutOfQuotes(ch, closeBracketIndex + 1, quotationChar);
}
}
}
else if (quotationChar != 0)
{
// quotes not balanced!
throw new ParseException("Opening/closing quote not found for quote at " + "(line " +
getLineNumber() + ", column " + getColumnNumber() + ")", startPos);
}
return closeBracketIndex;
}
/**
* Position the reader at the index provided. Could be anywhere within the data
*
* @param pos
* The new current position
*/
public final void setPosition(final int pos)
{
inputPosition = pos;
}
/**
* Get the column number. Note: The column number depends on you calling countLinesTo(pos). It
* is not necessarily the column number matching the current position in the stream.
*
* @return column number
*/
public final int getColumnNumber()
{
return columnNumber;
}
/**
* Get the line number. Note: The line number depends on you calling countLinesTo(pos). It is
* not necessarily the line number matching the current position in the stream.
*
* @return line number
*/
public final int getLineNumber()
{
return lineNumber;
}
/**
* Get the number of character read from the source resource. The whole content, not just until
* the current position.
*
* @return Size of the data
*/
public final int size()
{
return input.length();
}
/**
* Get the character at the position provided
*
* @param pos
* The position
* @return char at position
*/
public final char charAt(final int pos)
{
return input.charAt(pos);
}
}