001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.kvem.midp.pim;
028:
029: import java.io.IOException;
030: import java.io.InputStream;
031: import java.io.InputStreamReader;
032: import java.io.UnsupportedEncodingException;
033:
034: /**
035: * Reader that knows how to read non-blank lines. Line may be concatenated
036: * according to section 2.1.3 of the vCard 2.1 specification; CRLF followed by
037: * an LWSP character is treated as only the LWSP character.
038: *
039: * <p>The line terminator is taken as CRLF.
040: *
041: */
042: public class LineReader extends InputStreamReader {
043: /** Input stream. */
044: private final InputStream in;
045: /** Matcher function. */
046: private final Matcher matcher;
047:
048: /**
049: * Constructs a line reader handler.
050: *
051: * @param in an InputStream that must support mark()
052: * @param encoding character encoding of input stream
053: * @param matcher filter function
054: * @throws UnsupportedEncodingException if encoding is not
055: * available on the current platform
056: */
057: public LineReader(InputStream in, String encoding, Matcher matcher)
058: throws UnsupportedEncodingException {
059:
060: this (new MarkableInputStream(in), encoding, matcher);
061: }
062:
063: /**
064: * Constructs a line reader.
065: * @param in an InputStream that must support mark()
066: * @param encoding character encoding of input stream
067: * @param matcher filter function
068: * @throws UnsupportedEncodingException if encoding is not
069: * available on the current platform
070: */
071: private LineReader(MarkableInputStream in, String encoding,
072: Matcher matcher) throws UnsupportedEncodingException {
073:
074: super (in, encoding);
075: this .in = in;
076: this .matcher = matcher;
077: }
078:
079: /**
080: * Reads a non-blank line.
081: *
082: * @return a line of text (without line terminators)
083: * or null if no more lines are available
084: * @throws IOException if a read error occurs
085: */
086: public String readLine() throws IOException {
087: StringBuffer sb = new StringBuffer();
088: boolean lineIsOnlyWhiteSpace = true;
089: boolean done = false;
090: for (int i = read(); i != -1 && !done;) {
091: switch (i) {
092: case '\r': {
093: // start of a new line. follow through and see if
094: // it is really a new line
095: i = read();
096: if (i != '\n') {
097: throw new IOException("Bad line terminator");
098: }
099: // fall through
100: }
101: // be generous and accept '\n' alone as a new line.
102: // this is against the vCard/vCalendar specifications, but
103: // appears to be common practice.
104: case '\n': {
105: // lines with only whitespace are treated as empty lines
106: if (lineIsOnlyWhiteSpace) {
107: sb.setLength(0);
108: }
109: mark(1);
110: i = read();
111: reset();
112: switch (i) {
113: case ' ':
114: case '\t':
115: // append this line to the previous one
116: skip(1);
117: break;
118: default:
119: // end of the line
120: if (!lineIsOnlyWhiteSpace) {
121: // return this line
122: done = true;
123: } else {
124: skip(1);
125: // read another line and hope it contains
126: // more than white space
127: }
128: break;
129: }
130: break;
131: }
132: case ' ':
133: case '\t':
134: sb.append((char) i);
135: i = read();
136: break;
137: default:
138: sb.append((char) i);
139: if (matcher.match(sb)) {
140: return sb.toString().trim();
141: }
142: i = read();
143: lineIsOnlyWhiteSpace = false;
144: }
145: }
146: if (lineIsOnlyWhiteSpace) {
147: return null;
148: } else {
149: return sb.toString().trim();
150: }
151: }
152:
153: /**
154: * Sets marker in input stream.
155: * @param lookahead offset to peek ahead
156: * @throws IOException if any read ahead error occurs
157: */
158: public void mark(int lookahead) throws IOException {
159: in.mark(lookahead);
160: }
161:
162: /**
163: * Checks if mark is supported.
164: * @return <code>true</code> if mark is supported
165: */
166: public boolean markSupported() {
167: return true;
168: }
169:
170: /**
171: * Reset the line markers.
172: * @throws IOException if an error occurs accessing the
173: * input stream
174: */
175: public void reset() throws IOException {
176: in.reset();
177: }
178:
179: /**
180: * Inner interface for matching function.
181: */
182: public static interface Matcher {
183: /**
184: * Matches input string buffer.
185: * @param sb pattern string to match
186: * @return <code>true</code> if string matches.
187: */
188: public boolean match(StringBuffer sb);
189: }
190: }
|