/* * * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.j2me.pim; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; /** * Reader that knows how to read non-blank lines. Line may be concatenated * according to section 2.1.3 of the vCard 2.1 specification; CRLF followed by * an LWSP character is treated as only the LWSP character. * * <p>The line terminator is taken as CRLF. * */ public class LineReader extends InputStreamReader { /** Input stream. */ private final InputStream in; /** Matcher function. */ private final Matcher matcher; /** * Constructs a line reader handler. * * @param in an InputStream that must support mark() * @param encoding character encoding of input stream * @param matcher filter function * @throws UnsupportedEncodingException if encoding is not * available on the current platform */ public LineReader(InputStream in, String encoding, Matcher matcher) throws UnsupportedEncodingException { super(in, encoding); this.in = new MarkableInputStream(in); this.matcher = matcher; } /** * Reads a non-blank line. * * @return a line of text (without line terminators) * or null if no more lines are available * @throws IOException if a read error occurs */ public String readLine() throws IOException { StringBuffer sb = new StringBuffer(); boolean lineIsOnlyWhiteSpace = true; boolean done = false; for (int i = in.read(); i != -1 && !done; ) { switch (i) { case '\r': { // start of a new line. follow through and see if // it is really a new line i = in.read(); if (i != '\n') { throw new IOException("Bad line terminator"); } // fall through } // be generous and accept '\n' alone as a new line. // this is against the vCard/vCalendar specifications, but // appears to be common practice. case '\n': { // lines with only whitespace are treated as empty lines if (lineIsOnlyWhiteSpace) { sb.setLength(0); } in.mark(1); i = in.read(); reset(); switch (i) { case ' ': case '\t': // append this line to the previous one in.skip(1); break; default: // end of the line if (!lineIsOnlyWhiteSpace) { // return this line done = true; } else { in.skip(1); // read another line and hope it contains // more than white space } break; } break; } case ' ': case '\t': sb.append((char) i); i = in.read(); break; default: sb.append((char) i); if (matcher.match(sb)) { return sb.toString().trim(); } i = in.read(); lineIsOnlyWhiteSpace = false; } } if (lineIsOnlyWhiteSpace) { return null; } else { return sb.toString().trim(); } } /** * Sets marker in input stream. * @param lookahead offset to peek ahead * @throws IOException if any read ahead error occurs */ public void mark(int lookahead) throws IOException { in.mark(lookahead); } /** * Checks if mark is supported. * @return <code>true</code> if mark is supported */ public boolean markSupported() { return true; } /** * Reset the line markers. * @throws IOException if an error occurs accessing the * input stream */ public void reset() throws IOException { in.reset(); } /** * Inner interface for matching function. */ public static interface Matcher { /** * Matches input string buffer. * @param sb pattern string to match * @return <code>true</code> if string matches. */ public boolean match(StringBuffer sb); } }