001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.util.io;
018:
019: import java.io.IOException;
020: import java.io.Reader;
021:
022: /**
023: * This is not a reader like e.g. FileReader. It rather reads the whole data
024: * untill the end from a source reader into memory and besides that it maintains
025: * the current position (like a reader) it provides String like methods which
026: * conviniently let you navigate (usually forward) in the stream.
027: * <p>
028: * Because the source data are expected to be text, the line and column numbers
029: * are maintained as well for location precise error messages. But it does NOT
030: * automatically update the line and column numbers. You must call
031: * {@link #countLinesTo(int)}
032: *
033: * @author Juergen Donnerstag
034: */
035: public final class FullyBufferedReader {
036: /** All the chars from the resouce */
037: private final String input;
038:
039: /** Position in parse. */
040: private int inputPosition;
041:
042: /** Current line number */
043: private int lineNumber = 1;
044:
045: /** current column number. */
046: private int columnNumber = 1;
047:
048: /** Last place we counted lines from. */
049: private int lastLineCountIndex;
050:
051: /** A variable to remember a certain position in the markup */
052: private int positionMarker;
053:
054: /**
055: * Read all the data from the resource into memory.
056: *
057: * @param reader
058: * The source reader to load the data from
059: * @throws IOException
060: */
061: public FullyBufferedReader(final Reader reader) throws IOException {
062: super ();
063:
064: this .input = Streams.readString(reader);
065: }
066:
067: /**
068: * Get the characters from the position marker to toPos.
069: * <p>
070: * If toPos < 0, than get all data from the position marker until the end.
071: * If toPos less than the current position marker than return an empty
072: * string ""
073: *
074: * @param toPos
075: * Index of first character not included
076: * @return Raw markup (a string) in between these two positions.
077: */
078: public final CharSequence getSubstring(int toPos) {
079: if (toPos < 0) {
080: toPos = this .input.length();
081: } else if (toPos < this .positionMarker) {
082: return "";
083: }
084: return this .input.subSequence(this .positionMarker, toPos);
085: }
086:
087: /**
088: * Get the characters from in between both positions including the char at
089: * fromPos, excluding the char at toPos
090: *
091: * @param fromPos
092: * first index
093: * @param toPos
094: * second index
095: * @return the string (raw markup) in between both positions
096: */
097: public final CharSequence getSubstring(final int fromPos,
098: final int toPos) {
099: return this .input.subSequence(fromPos, toPos);
100: }
101:
102: /**
103: * Gets the current input position
104: *
105: * @return input position
106: */
107: public final int getPosition() {
108: return this .inputPosition;
109: }
110:
111: /**
112: * Remember the current position in markup
113: *
114: * @param pos
115: */
116: public final void setPositionMarker(final int pos) {
117: this .positionMarker = pos;
118: }
119:
120: /**
121: * @return The markup to be parsed
122: */
123: public String toString() {
124: return this .input;
125: }
126:
127: /**
128: * Counts lines starting where we last left off up to the index provided.
129: *
130: * @param end
131: * End index
132: */
133: public final void countLinesTo(final int end) {
134: for (int i = lastLineCountIndex; i < end; i++) {
135: final char ch = this .input.charAt(i);
136: if (ch == '\n') {
137: columnNumber = 1;
138: lineNumber++;
139: } else if (ch != '\r') {
140: columnNumber++;
141: }
142: }
143:
144: lastLineCountIndex = end;
145: }
146:
147: /**
148: * Find a char starting at the current input position
149: *
150: * @param ch
151: * The char to search for
152: * @return -1 if not found
153: */
154: public final int find(final char ch) {
155: return input.indexOf(ch, inputPosition);
156: }
157:
158: /**
159: * Find a char starting at the position provided
160: *
161: * @param ch
162: * The char to search for
163: * @param startPos
164: * The index to start at
165: * @return -1 if not found
166: */
167: public final int find(final char ch, final int startPos) {
168: return input.indexOf(ch, startPos);
169: }
170:
171: /**
172: * Find the string starting at the current input position
173: *
174: * @param str
175: * The string to search for
176: * @return -1 if not found
177: */
178: public final int find(final String str) {
179: return input.indexOf(str, inputPosition);
180: }
181:
182: /**
183: * Find the string starting at the position provided
184: *
185: * @param str
186: * The string to search for
187: * @param startPos
188: * The index to start at
189: * @return -1 if not found
190: */
191: public final int find(final String str, final int startPos) {
192: return input.indexOf(str, startPos);
193: }
194:
195: /**
196: * Position the reader at the index provided. Could be anywhere within the
197: * data
198: *
199: * @param pos
200: * The new current position
201: */
202: public final void setPosition(final int pos) {
203: this .inputPosition = pos;
204: }
205:
206: /**
207: * Get the column number. Note: The column number depends on you calling
208: * countLinesTo(pos). It is not necessarily the column number matching the
209: * current position in the stream.
210: *
211: * @return column number
212: */
213: public final int getColumnNumber() {
214: return this .columnNumber;
215: }
216:
217: /**
218: * Get the line number. Note: The line number depends on you calling
219: * countLinesTo(pos). It is not necessarily the line number matching the
220: * current position in the stream.
221: *
222: * @return line number
223: */
224: public final int getLineNumber() {
225: return this .lineNumber;
226: }
227:
228: /**
229: * Get the number of character read from the source resource. The whole
230: * content, not just until the current position.
231: *
232: * @return Size of the data
233: */
234: public final int size() {
235: return this .input.length();
236: }
237:
238: /**
239: * Get the character at the position provided
240: *
241: * @param pos
242: * The position
243: * @return char at position
244: */
245: public final char charAt(int pos) {
246: return this.input.charAt(pos);
247: }
248: }
|