001/* ========================================================================
002 * JCommon : a free general purpose class library for the Java(tm) platform
003 * ========================================================================
004 *
005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006 * 
007 * Project Info:  http://www.jfree.org/jcommon/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it 
010 * under the terms of the GNU Lesser General Public License as published by 
011 * the Free Software Foundation; either version 2.1 of the License, or 
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but 
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022 * USA.  
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025 * in the United States and other countries.]
026 *
027 * ----------------------
028 * LineBreakIterator.java
029 * ----------------------
030 * (C)opyright 2003, by Thomas Morgner and Contributors.
031 *
032 * Original Author:  Thomas Morgner;
033 * Contributor(s):   David Gilbert (for Object Refinery Limited);
034 *
035 * $Id: LineBreakIterator.java,v 1.4 2005/11/03 09:55:26 mungady Exp $
036 *
037 * Changes
038 * -------
039 * 13-03-2003 : Initial version
040 */
041package org.jfree.util;
042
043import java.util.Iterator;
044
045/**
046 * An iterator that breaks text into lines.
047 * The result is equal to BufferedReader.readLine().
048 *
049 * @author Thomas Morgner
050 */
051public class LineBreakIterator implements Iterator
052{
053  /** A useful constant. */
054  public static final int DONE = -1;
055
056  /** Storage for the text. */
057  private char[] text;
058
059  /** The current position. */
060  private int position;
061
062  /**
063   * Default constructor.
064   */
065  public LineBreakIterator()
066  {
067    setText("");
068  }
069
070  /**
071   * Creates a new line break iterator.
072   *
073   * @param text the text to be broken up.
074   */
075  public LineBreakIterator(final String text)
076  {
077    setText(text);
078  }
079
080  /**
081   * Returns the position of the next break.
082   *
083   * @return A position.
084   */
085  public synchronized int nextPosition()
086  {
087    if (this.text == null)
088    {
089      return DONE;
090    }
091    if (this.position == DONE)
092    {
093      return DONE;
094    }
095
096    // recognize \n, \r, \r\n
097
098    final int nChars = this.text.length;
099    int nextChar = this.position;
100
101    for (;;)
102    {
103      if (nextChar >= nChars)
104      {
105        /* End of text reached */
106        this.position = DONE;
107        return DONE;
108      }
109
110      boolean eol = false;
111      char c = 0;
112      int i;
113
114      // search the next line break, either \n or \r
115      for (i = nextChar; i < nChars; i++)
116      {
117        c = this.text[i];
118        if ((c == '\n') || (c == '\r'))
119        {
120          eol = true;
121          break;
122        }
123      }
124
125      nextChar = i;
126      if (eol)
127      {
128        nextChar++;
129        if (c == '\r')
130        {
131          if ((nextChar < nChars) && (this.text[nextChar] == '\n'))
132          {
133            nextChar++;
134          }
135        }
136        this.position = nextChar;
137        return (this.position);
138      }
139    }
140  }
141
142  /**
143   * Same like next(), but returns the End-Of-Text as
144   * if there was a linebreak added (Reader.readLine() compatible)
145   *
146   * @return The next position.
147   */
148  public int nextWithEnd()
149  {
150    final int pos = this.position;
151    if (pos == DONE)
152    {
153      return DONE;
154    }
155    if (pos == this.text.length)
156    {
157      this.position = DONE;
158      return DONE;
159    }
160    final int retval = nextPosition();
161    if (retval == DONE)
162    {
163      return this.text.length;
164    }
165    return retval;
166  }
167
168  /**
169   * Returns the text to be broken up.
170   *
171   * @return The text.
172   */
173  public String getText()
174  {
175    return new String(this.text);
176  }
177
178  /**
179   * Sets the text to be broken up.
180   *
181   * @param text  the text.
182   */
183  public void setText(final String text)
184  {
185    this.position = 0;
186    this.text = text.toCharArray();
187  }
188
189  /**
190   * Returns <tt>true</tt> if the iteration has more elements. (In other
191   * words, returns <tt>true</tt> if <tt>next</tt> would return an element
192   * rather than throwing an exception.)
193   *
194   * @return <tt>true</tt> if the iterator has more elements.
195   */
196  public boolean hasNext()
197  {
198    return (this.position != DONE);
199  }
200
201  /**
202   * Returns the next element in the iteration.
203   *
204   * @return the next element in the iteration.
205   */
206  public Object next()
207  {
208    if (this.position == DONE)
209    {
210      // allready at the end ...
211      return null;
212    }
213
214    final int lastFound = this.position;
215    int pos = nextWithEnd();
216    if (pos == DONE)
217    {
218      // the end of the text has been reached ...
219      return new String(this.text, lastFound, this.text.length - lastFound);
220    }
221
222    // step one char back
223    if (pos > 0)
224    {
225      final int end = lastFound;
226      for (; ((pos) > end) && ((this.text[pos - 1] == '\n') || this.text[pos - 1] == '\r'); pos--)
227      {
228        // search the end of the current linebreak sequence ..
229      }
230    }
231    //System.out.println ("text: " + new String (text));
232    //System.out.println ("pos: " + pos + " lastFound: " + lastFound);
233    return new String(this.text, lastFound, pos - lastFound);
234  }
235
236  /**
237   *
238   * Removes from the underlying collection the last element returned by the
239   * iterator (optional operation).  This method can be called only once per
240   * call to <tt>next</tt>.  The behavior of an iterator is unspecified if
241   * the underlying collection is modified while the iteration is in
242   * progress in any way other than by calling this method.
243   *
244   * @exception UnsupportedOperationException if the <tt>remove</tt>
245   *    operation is not supported by this Iterator.
246   * @exception IllegalStateException if the <tt>next</tt> method has not
247   *    yet been called, or the <tt>remove</tt> method has already
248   *    been called after the last call to the <tt>next</tt>
249   *    method.
250   */
251  public void remove()
252  {
253      throw new UnsupportedOperationException("This iterator is read-only.");
254  }
255}