001: /* ContentReader.java NanoXML/Java
002: *
003: * $Revision: 1421 $
004: * $Date: 2006-03-12 09:32:32 -0700 (Sun, 12 Mar 2006) $
005: * $Name$
006: *
007: * This file is part of NanoXML 2 for Java.
008: * Copyright (C) 2001 Marc De Scheemaecker, All Rights Reserved.
009: *
010: * This software is provided 'as-is', without any express or implied warranty.
011: * In no event will the authors be held liable for any damages arising from the
012: * use of this software.
013: *
014: * Permission is granted to anyone to use this software for any purpose,
015: * including commercial applications, and to alter it and redistribute it
016: * freely, subject to the following restrictions:
017: *
018: * 1. The origin of this software must not be misrepresented; you must not
019: * claim that you wrote the original software. If you use this software in
020: * a product, an acknowledgment in the product documentation would be
021: * appreciated but is not required.
022: *
023: * 2. Altered source versions must be plainly marked as such, and must not be
024: * misrepresented as being the original software.
025: *
026: * 3. This notice may not be removed or altered from any source distribution.
027: */
028:
029: package net.n3.nanoxml;
030:
031: import java.io.IOException;
032: import java.io.Reader;
033:
034: /**
035: * This reader reads data from another reader until a certain string is encountered.
036: *
037: * @author Marc De Scheemaecker
038: * @version $Name$, $Version$
039: */
040: class ContentReader extends Reader {
041:
042: /**
043: * The encapsulated reader.
044: */
045: private IXMLReader reader;
046:
047: /**
048: * The encapsulated entityResolver.
049: */
050: private IXMLEntityResolver entityResolver;
051:
052: /**
053: * The escape char (& or %).
054: */
055: private char escapeChar;
056:
057: /**
058: * The delimiter that will indicate the end of the stream.
059: */
060: private char[] delimiter;
061:
062: /**
063: * The characters that have been read too much.
064: */
065: private String charsReadTooMuch;
066:
067: /**
068: * The number of characters in the delimiter that stil need to be scanned.
069: */
070: private int charsToGo;
071:
072: /**
073: * True if the escape char (& or %) needs to be left untouched.
074: */
075: private boolean useLowLevelReader;
076:
077: /**
078: * True if we are passed the initial prefix.
079: */
080: private boolean pastInitialPrefix;
081:
082: /**
083: * Creates the reader.
084: *
085: * @param reader the encapsulated reader
086: * @param entityResolver resolves entities
087: * @param escapeChar escape character (& or %)
088: * @param delimiter the delimiter, as a backwards string, that will indicate the end of the
089: * stream
090: * @param useLowLevelReader true if & needs to be left untouched; false if entities need to
091: * be processed
092: * @param prefix chars that are already read
093: */
094: ContentReader(IXMLReader reader, IXMLEntityResolver entityResolver,
095: char escapeChar, char[] delimiter,
096: boolean useLowLevelReader, String prefix) {
097: this .delimiter = delimiter;
098: this .charsToGo = this .delimiter.length;
099: this .charsReadTooMuch = prefix;
100: this .useLowLevelReader = useLowLevelReader;
101: this .pastInitialPrefix = false;
102: this .reader = reader;
103: this .entityResolver = entityResolver;
104: this .escapeChar = escapeChar;
105: }
106:
107: /**
108: * Cleans up the object when it's destroyed.
109: */
110: protected void finalize() throws Throwable {
111: this .reader = null;
112: this .entityResolver = null;
113: this .delimiter = null;
114: this .charsReadTooMuch = null;
115: super .finalize();
116: }
117:
118: /**
119: * Reads a block of data.
120: *
121: * @param buffer where to put the read data
122: * @param offset first position in buffer to put the data
123: * @param size maximum number of chars to read
124: *
125: * @return the number of chars read, or -1 if at EOF
126: *
127: * @throws java.io.IOException if an error occurred reading the data
128: */
129: public int read(char[] buffer, int offset, int size)
130: throws IOException {
131: int charsRead = 0;
132: boolean isEntity[] = new boolean[1];
133: isEntity[0] = false;
134:
135: if ((offset + size) > buffer.length) {
136: size = buffer.length - offset;
137: }
138:
139: while ((this .charsToGo > 0) && (charsRead < size)) {
140: char ch;
141:
142: if (this .charsReadTooMuch.length() > 0) {
143: ch = this .charsReadTooMuch.charAt(0);
144: this .charsReadTooMuch = this .charsReadTooMuch
145: .substring(1);
146: } else {
147: this .pastInitialPrefix = true;
148:
149: try {
150: if (useLowLevelReader) {
151: ch = this .reader.read();
152: } else {
153: ch = XMLUtil.read(this .reader, isEntity,
154: this .escapeChar, this .entityResolver);
155:
156: if (!isEntity[0]) {
157: if (ch == '&') {
158: this .reader.startNewStream(XMLUtil
159: .scanEntity(isEntity,
160: this .reader,
161: this .escapeChar,
162: this .entityResolver));
163: ch = this .reader.read();
164: }
165: }
166: }
167: } catch (XMLParseException e) {
168: throw new RuntimeException(e.getMessage());
169: // necessary to be able to implement Reader
170: }
171: }
172:
173: if (isEntity[0]) {
174: buffer[offset + charsRead] = ch;
175: charsRead++;
176: } else {
177: if ((ch == (this .delimiter[this .charsToGo - 1]))
178: && pastInitialPrefix) {
179: --this .charsToGo;
180: } else if (this .charsToGo < this .delimiter.length) {
181: this .charsReadTooMuch = new String(this .delimiter,
182: this .charsToGo + 1, this .delimiter.length
183: - this .charsToGo)
184: + ch;
185: this .charsToGo = this .delimiter.length;
186: buffer[offset + charsRead] = this .delimiter[this .charsToGo - 1];
187: charsRead++;
188: } else {
189: buffer[offset + charsRead] = ch;
190: charsRead++;
191: }
192: }
193: }
194:
195: if (charsRead == 0) {
196: charsRead = -1;
197: }
198:
199: return charsRead;
200: }
201:
202: /**
203: * Skips remaining data and closes the stream.
204: *
205: * @throws java.io.IOException if an error occurred reading the data
206: */
207: public void close() throws IOException {
208: while (this .charsToGo > 0) {
209: char ch;
210:
211: if (this .charsReadTooMuch.length() > 0) {
212: ch = this .charsReadTooMuch.charAt(0);
213: this .charsReadTooMuch = this .charsReadTooMuch
214: .substring(1);
215: } else {
216: if (useLowLevelReader) {
217: ch = this .reader.read();
218: } else {
219: try {
220: ch = XMLUtil.read(this .reader, null,
221: this .escapeChar, this .entityResolver);
222: } catch (XMLParseException e) {
223: throw new RuntimeException(e.getMessage());
224: // necessary to be able to implement Reader
225: }
226: }
227: }
228:
229: if (ch == (this .delimiter[this .charsToGo - 1])) {
230: --this .charsToGo;
231: } else if (this .charsToGo < this .delimiter.length) {
232: this .charsReadTooMuch = new String(this .delimiter,
233: this .charsToGo + 1, this.delimiter.length
234: - this.charsToGo)
235: + ch;
236: this.charsToGo = this.delimiter.length;
237: }
238: }
239: }
240:
241: }
|