001/* StringReader.java -- permits a String to be read as a character input stream
002   Copyright (C) 1998, 1999, 2000, 2003  Free Software Foundation
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010 
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package java.io;
039
040/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
041 * "The Java Language Specification", ISBN 0-201-63451-1
042 * plus online API docs for JDK 1.2 beta from http://www.javasoft.com.
043 * Status:  Believed complete and correct
044 */
045
046/**
047 * This class permits a <code>String</code> to be read as a character 
048 * input stream.
049 * <p>
050 * The mark/reset functionality in this class behaves differently than
051 * normal.  If no mark has been set, then calling the <code>reset()</code>
052 * method rewinds the read pointer to the beginning of the <code>String</code>.
053 *
054 * @author Aaron M. Renn (arenn@urbanophile.com)
055 * @author Warren Levy (warrenl@cygnus.com)
056 * @date October 19, 1998.  
057 */
058public class StringReader extends Reader
059{
060  /* A String provided by the creator of the stream. */
061  private String buf;
062
063  /* Position of the next char in buf to be read. */
064  private int pos;
065
066  /* The currently marked position in the stream. */
067  private int markedPos;
068
069  /* The index in buf one greater than the last valid character. */
070  private int count;
071
072  /**
073   * Create a new <code>StringReader</code> that will read chars from the 
074   * passed in <code>String</code>.  This stream will read from the beginning 
075   * to the end of the <code>String</code>.
076   *
077   * @param buffer The <code>String</code> this stream will read from.
078   */
079  public StringReader(String buffer)
080  {
081    super();
082    buf = buffer;
083
084    count = buffer.length();
085    markedPos = pos = 0;
086  }
087
088  public void close()
089  {
090    synchronized (lock)
091    {
092      buf = null;
093    }
094  }
095
096  public void mark(int readAheadLimit) throws IOException
097  {
098    synchronized (lock)
099    {
100      if (buf == null)
101        throw new IOException("Stream closed");
102
103      // readAheadLimit is ignored per Java Class Lib. book, p. 1692.
104      markedPos = pos;
105    }
106  }
107
108  public boolean markSupported()
109  {
110    return true;
111  }
112
113  public int read() throws IOException
114  {
115    synchronized (lock)
116    {
117      if (buf == null)
118        throw new IOException("Stream closed");
119
120      if (pos < count)
121        return ((int) buf.charAt(pos++)) & 0xFFFF;
122      return -1;
123    }
124  }
125
126  public int read(char[] b, int off, int len) throws IOException
127  {
128    synchronized (lock)
129    {
130      if (buf == null)
131        throw new IOException("Stream closed");
132
133      /* Don't need to check pos value, arraycopy will check it. */
134      if (off < 0 || len < 0 || off + len > b.length)
135        throw new ArrayIndexOutOfBoundsException();
136
137      if (pos >= count)
138        return -1;
139
140      int lastChar = Math.min(count, pos + len);
141      buf.getChars(pos, lastChar, b, off);
142      int numChars = lastChar - pos;
143      pos = lastChar;
144      return numChars;
145    }
146  }
147
148  /**
149   * This method determines if the stream is ready to be read.  This class
150   * is always ready to read and so always returns <code>true</code>, unless
151   * close() has previously been called in which case an IOException is
152   * thrown.
153   *
154   * @return <code>true</code> to indicate that this object is ready to be read.
155   * @exception IOException If the stream is closed.
156   */
157  public boolean ready() throws IOException
158  {
159    if (buf == null)
160      throw new IOException("Stream closed");
161
162    return true;
163  }
164
165  /**
166   * Sets the read position in the stream to the previously
167   * marked position or to 0 (i.e., the beginning of the stream) if the mark
168   * has not already been set.
169   */
170  public void reset() throws IOException
171  {
172    synchronized (lock)
173    {
174      if (buf == null)
175        throw new IOException("Stream closed");
176
177      pos = markedPos;
178    }
179  }
180
181  /**
182    * This method attempts to skip the requested number of chars in the
183    * input stream.  It does this by advancing the <code>pos</code> value by 
184    * the specified number of chars.  It this would exceed the length of the
185    * buffer, then only enough chars are skipped to position the stream at
186    * the end of the buffer.  The actual number of chars skipped is returned.
187    *
188    * @param n The requested number of chars to skip
189    *
190    * @return The actual number of chars skipped.
191    */
192  public long skip(long n) throws IOException
193  {
194    synchronized (lock)
195    {
196      if (buf == null)
197        throw new IOException("Stream closed");
198
199      // Even though the var numChars is a long, in reality it can never
200      // be larger than an int since the result of subtracting 2 positive
201      // ints will always fit in an int.  Since we have to return a long
202      // anyway, numChars might as well just be a long.
203      long numChars = Math.min((long) (count - pos), n < 0 ? 0L : n);
204      pos += numChars;
205      return numChars;
206    }
207  }
208}
209