You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by te...@apache.org on 2006/07/08 23:17:12 UTC
svn commit: r420193 [1/2] - in
/incubator/harmony/enhanced/classlib/trunk/modules/luni/src:
main/java/java/util/Scanner.java
test/java/tests/api/java/util/ScannerTest.java
Author: tellison
Date: Sat Jul 8 14:17:12 2006
New Revision: 420193
URL: http://svn.apache.org/viewvc?rev=420193&view=rev
Log:
Apply patch HARMONY-800 (Implementation of new methods findWithinHorizon(Pattern, int) in java.util.Scanner)
Modified:
incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/Scanner.java
incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/ScannerTest.java
Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/Scanner.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/Scanner.java?rev=420193&r1=420192&r2=420193&view=diff
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/Scanner.java (original)
+++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/Scanner.java Sat Jul 8 14:17:12 2006
@@ -1,994 +1,1059 @@
-/* Copyright 2006 The Apache Software Foundation or its licensors, as applicable
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package java.util;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.StringReader;
-import java.io.UnsupportedEncodingException;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.nio.CharBuffer;
-import java.nio.channels.Channels;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.charset.Charset;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.util.regex.MatchResult;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.harmony.luni.util.NotYetImplementedException;
-
-/**
- * A parser that parses a text string to primitive types with the help of
- * regular expression. It supports localized number and various radixes.
- *
- * The input is broken into tokens by the delimiter pattern, which is whitespace
- * by default. The primitive types can be got via corresponding next methods. If
- * the token is not in valid format, an InputMissmatchException is thrown.
- *
- * For example: Scanner s = new Scanner("1A true");
- * System.out.println(s.nextInt(16)); System.out.println(s.nextBoolean()); The
- * result: 26 true
- *
- * A scanner can find or skip specific pattern with no regard to the delimiter.
- * All these methods and the various next and hasNext methods may block.
- *
- * Scanner is not thread-safe without external synchronization
- */
-public final class Scanner implements Iterator<String> {
-
- // Default delimiting pattern
- private static final Pattern DEFAULT_DELIMITER = Pattern
- .compile("\\p{javaWhitespace}+"); //$NON-NLS-1$
-
- //The boolean's pattern
- private static final Pattern BOOLEAN_PATTERN = Pattern.compile(
- "true|false", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
-
- // The pattern matching anything
- private static final Pattern ANY_PATTERN = Pattern.compile("(?s).*");
-
- private static final int DIPLOID = 2;
-
- // Default radix
- private static final int DEFAULT_RADIX = 10;
-
- private static final int DEFAULT_TRUNK_SIZE = 1024;
-
- // The input source of scanner
- private Readable input;
-
- private CharBuffer buffer;
-
- private Pattern delimiter = DEFAULT_DELIMITER;
-
- private Matcher matcher;
-
- private int radix = DEFAULT_RADIX;
-
- private Locale locale = Locale.getDefault();
-
- // The position where find begins
- private int findStartIndex = 0;
-
- // The last find start position
- private int preStartIndex = findStartIndex;
-
- // The length of the buffer
- private int bufferLength = 0;
-
- // Used by find and nextXXX operation
- private boolean closed = false;
-
- private IOException lastIOException;
-
- private boolean matchSuccessful = false;
-
- /**
- * Constructs a scanner that uses File as its input. The default charset is
- * applied when reading the file.
- *
- * @param src
- * the file to be scanned
- * @throws FileNotFoundException
- * if the specified file is not found
- */
- public Scanner(File src) throws FileNotFoundException {
- this(src, Charset.defaultCharset().name());
- }
-
- /**
- * Constructs a scanner that uses File as its input. The specified charset
- * is applied when reading the file.
- *
- * @param src
- * the file to be scanned
- * @param charsetName
- * the name of the encoding type of the file
- * @throws FileNotFoundException
- * if the specified file is not found
- * @throws IllegalArgumentException
- * if the specified coding does not exist
- */
- public Scanner(File src, String charsetName) throws FileNotFoundException {
- if (null == src) {
- throw new NullPointerException(org.apache.harmony.luni.util.Msg
- .getString("KA00a"));
- }
- FileInputStream fis = new FileInputStream(src);
- if (null == charsetName) {
- throw new IllegalArgumentException(org.apache.harmony.luni.util.Msg
- .getString("KA009"));
- }
- try {
- input = new InputStreamReader(fis, charsetName);
- } catch (UnsupportedEncodingException e) {
- try {
- fis.close();
- } catch (IOException ioException) {
- // ignore
- }
- throw new IllegalArgumentException(e.getMessage());
- }
- initialization();
- }
-
- /**
- * Constructs a scanner that uses String as its input.
- *
- * @param src
- * the string to be scanned
- */
- public Scanner(String src) {
- input = new StringReader(src);
- initialization();
- }
-
- /**
- * Constructs a scanner that uses InputStream as its input. The default
- * charset is applied when decoding the input.
- *
- * @param src
- * the input stream to be scanned
- */
- public Scanner(InputStream src) {
- this(src, Charset.defaultCharset().name());
- }
-
- /**
- * Constructs a scanner that uses InputStream as its input. The specified
- * charset is applied when decoding the input.
- *
- * @param src
- * the input stream to be scanned
- * @param charsetName
- * the encoding type of the input stream
- * @throws IllegalArgumentException
- * if the specified character set is not found
- */
- public Scanner(InputStream src, String charsetName) {
- if (null == src) {
- throw new NullPointerException(org.apache.harmony.luni.util.Msg
- .getString("KA00b"));
- }
- try {
- input = new InputStreamReader(src, charsetName);
- } catch (UnsupportedEncodingException e) {
- throw new IllegalArgumentException(e.getMessage());
- }
- initialization();
- }
-
- /**
- * Constructs a scanner that uses Readable as its input.
- *
- * @param src
- * the Readable to be scanned
- */
- public Scanner(Readable src) {
- if (null == src) {
- throw new NullPointerException();
- }
- input = src;
- initialization();
- }
-
- /**
- * Constructs a scanner that uses ReadableByteChannel as its input. The
- * default charset is applied when decoding the input.
- *
- * @param src
- * the ReadableByteChannel to be scanned
- */
- public Scanner(ReadableByteChannel src) {
- this(src, Charset.defaultCharset().name());
- }
-
- /**
- * Constructs a scanner that uses ReadableByteChannel as its input. The
- * specified charset is applied when decoding the input.
- *
- * @param src
- * the ReadableByteChannel to be scanned
- * @param charsetName
- * the encoding type of the content in the ReadableByteChannel
- * @throws IllegalArgumentException
- * if the specified character set is not found
- */
- public Scanner(ReadableByteChannel src, String charsetName) {
- if (null == src) {
- throw new NullPointerException(org.apache.harmony.luni.util.Msg
- .getString("KA00d"));
- }
- if (null == charsetName) {
- throw new IllegalArgumentException(org.apache.harmony.luni.util.Msg
- .getString("KA009"));
- }
- try {
- input = new InputStreamReader(Channels.newInputStream(src),
- charsetName);
- } catch (UnsupportedEncodingException e) {
- throw new IllegalArgumentException(e.getMessage());
- }
- initialization();
- }
-
- /**
- * Closes the underlying input if the input implements Closeable. If the
- * scanner has been closed, this method will take no effect. The scanning
- * operation after calling this method will throw IllegalStateException
- *
- */
- public void close() {
- if (closed == true) {
- return;
- }
- if (input instanceof Closeable) {
- try {
- ((Closeable) input).close();
- } catch (IOException e) {
- lastIOException = e;
- }
- }
- closed = true;
- }
-
- /**
- * Returns the <code>Pattern</code> in use by this scanner.
- *
- * @return the <code>Pattern</code> presently in use by this scanner
- */
- public Pattern delimiter() {
- return delimiter;
- }
-
- //TODO: To implement this feature
- public String findInLine(Pattern pattern) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public String findInLine(String pattern) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public String findWithinHorizon(Pattern pattern, int horizon) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public String findWithinHorizon(String pattern, int horizon) {
- throw new NotYetImplementedException();
- }
-
- /**
- * Returns true if this scanner has next token. This method may be blocked
- * when it is waiting for input to scan. This scanner does not advance past
- * the input.
- *
- * @return true
- * iff this scanner has next token
- * @throws IllegalStateException
- * if the scanner has been closed
- */
- public boolean hasNext() {
- return hasNext(ANY_PATTERN);
- }
-
- /**
- * Returns true if this scanner's next token matches the specified pattern.
- * This method may be blocked when it is waiting for input to scan. This
- * scanner does not advance past the input that matched the pattern.
- *
- * @param pattern
- * the specified pattern to scan
- * @return
- * true iff this scanner's next token matches the specified pattern
- * @throws IllegalStateException
- * if the scanner has been closed
- */
- public boolean hasNext(Pattern pattern) {
- checkClosed();
- if (isInputExhausted()) {
- return false;
- }
- saveCurrentStatus();
- //if the next token exists, set the match region, otherwise return false
- if (!setTokenRegion()) {
- recoverPreviousStatus();
- return false;
- }
- matcher.usePattern(pattern);
- boolean hasNext = false;
- //check whether next token matches the specified pattern
- if (matcher.matches()) {
- hasNext = true;
- }
- recoverPreviousStatus();
- return hasNext;
- }
-
-
- /**
- * Returns true if this scanner's next token matches the pattern constructed
- * from the specified string. This method may be blocked when it is waiting
- * for input to scan. This scanner does not advance past the input that
- * matched the pattern.
- *
- * The invocation of this method in the form hasNext(pattern) behaves in the
- * same way as the invocaiton of hasNext(Pattern.compile(pattern)).
- *
- * @param pattern
- * the string specifying the pattern to scan for
- * @return true
- * iff this scanner's next token matches the specified pattern
- * @throws IllegalStateException
- * if the scanner has been closed
- */
- public boolean hasNext(String pattern) {
- return hasNext(Pattern.compile(pattern));
- }
-
- //TODO: To implement this feature
- public boolean hasNextBigDecimal() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextBigInteger() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextBigInteger(int radix) {
- throw new NotYetImplementedException();
- }
-
- /**
- * Returns true if this scanner's next token can be translated into a valid
- * boolean value. The scanner does not advance past the input that matched.
- *
- * @return true
- * iff the next token in this scanner's input can be translated
- * into a valid boolean value
- * @throws IllegalStateException
- * if the scanner has been closed
- */
- public boolean hasNextBoolean() {
- return hasNext(BOOLEAN_PATTERN);
- }
-
- //TODO: To implement this feature
- public boolean hasNextByte() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextByte(int radix) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextDouble() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextFloat() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextInt() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextInt(int radix) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextLine() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextLong() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextLong(int radix) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextShort() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public boolean hasNextShort(int radix) {
- throw new NotYetImplementedException();
- }
-
- /**
- * returns the last IOException thrown when reading the underlying input. If
- * no exception is thrown, return null.
- *
- * @return the last IOException thrown
- */
- public IOException ioException() {
- return lastIOException;
- }
-
- /**
- * return the locale of this scanner.
- *
- * @return
- * the locale of this scanner
- */
- public Locale locale() {
- return locale;
- }
-
- /**
- * Returns the match result of this scanner's last match operation.This
- * method throws IllegalStateException if no match operation has been
- * performed, or if the last match was unsuccessful.
- *
- * The various nextXXX methods of Scanner provide a match result if they do
- * not complete with throwing an exception. For example, after an invocation
- * of the nextBoolean() method which returned a boolean value, this method
- * returns a match result for the search of the Boolean regular expression
- * defined above. In the same way,the findInLine(java.lang.String),
- * findWithinHorizon(java.lang.String, int), and
- * skip(java.util.regex.Pattern) methods will provide a match result if they
- * are successful.
- *
- * @return the match result of the last match operation
- * @throws IllegalStateException
- * if the match result is available
- */
- public MatchResult match() {
- if (!matchSuccessful) {
- throw new IllegalStateException();
- }
- return matcher.toMatchResult();
- }
-
- /**
- * Finds and Returns the next complete token which is prefixed and postfixed
- * by input that matches the delimiter pattern. This method may be blocked
- * when it is waiting for input to scan, even if a previous invocation of
- * hasNext() returned true. If this match successes, the scanner advances
- * past the next complete token.
- *
- * @return
- * the next complete token
- * @throws IllegalStateException
- * if this scanner has been closed
- * @throws NoSuchElementException
- * if input has been exhausted
- */
- public String next() {
- return next(ANY_PATTERN);
- }
-
- /**
- * Returns the next token which is prefixed and postfixed by input that
- * matches the delimiter pattern if this token matches the specified
- * pattern. This method may be blocked when it is waiting for input to scan,
- * even if a previous invocation of hasNext(Pattern) returned true. If this
- * match successes, the scanner advances past the next token that matched
- * the pattern.
- *
- * @param pattern
- * the specified pattern to scan
- * @return
- * the next token
- * @throws IllegalStateException
- * if this scanner has been closed
- * @throws NoSuchElementException
- * if input has been exhausted
- */
- public String next(Pattern pattern) {
- checkClosed();
- if (null == pattern) {
- throw new NullPointerException();
- }
- matchSuccessful = false;
- if (isInputExhausted()) {
- throw new NoSuchElementException();
- }
- saveCurrentStatus();
- if (!setTokenRegion()) {
- recoverPreviousStatus();
- // if setting match region fails
- throw new NoSuchElementException();
- }
- matcher.usePattern(pattern);
- if (matchSuccessful = matcher.matches()) {
- return matcher.group(0);
- } else {
- recoverPreviousStatus();
- throw new InputMismatchException();
- }
- }
-
- /**
- * Returns the next token which is prefixed and postfixed by input that
- * matches the delimiter pattern if this token matches the pattern
- * constructed from the sepcified string. This method may be blocked when it
- * is waiting for input to scan. If this match successes, the scanner
- * advances past the next token that matched the pattern.
- *
- * The invocation of this method in the form next(pattern) behaves in the
- * same way as the invocaiton of next(Pattern.compile(pattern)).
- *
- * @param pattern
- * the string specifying the pattern to scan for
- * @return
- * the next token
- * @throws IllegalStateException
- * if this scanner has been closed
- * @throws NoSuchElementException
- * if input has been exhausted
- */
- public String next(String pattern) {
- return next(Pattern.compile(pattern));
- }
-
- //TODO: To implement this feature
- public BigDecimal nextBigDecimal() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public BigInteger nextBigInteger() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public BigInteger nextBigInteger(int radix) {
- throw new NotYetImplementedException();
- }
-
- /**
- * Translates the next token in this scanner's input into a boolean value and
- * returns this value. This method will throw InputMismatchException if the
- * next token can not be interpreted as a boolean value with a case
- * insensitive pattern created from the string "true|false". If this match
- * succeeds, the scanner advances past the input that matched.
- *
- * @return the boolean value scanned from the input
- * @throws IllegalStateException
- * if this scanner has been closed
- * @throws NoSuchElementException
- * if input has been exhausted
- * @throws InputMismatchException
- * if the next token can not be translated into a valid boolean
- * value
- */
- public boolean nextBoolean() {
- return Boolean.parseBoolean(next(BOOLEAN_PATTERN));
- }
-
- //TODO: To implement this feature
- public byte nextByte() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public byte nextByte(int radix) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public double nextDouble() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public float nextFloat() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public int nextInt() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public int nextInt(int radix) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public String nextLine() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public long nextLong() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public long nextLong(int radix) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public short nextShort() {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public short nextShort(int radix) {
- throw new NotYetImplementedException();
- }
-
- /**
- * return the radix of this scanner.
- *
- * @return
- * the radix of this scanner
- */
- public int radix() {
- return radix;
- }
-
- //TODO: To implement this feature
- public Scanner skip(Pattern pattern) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public Scanner skip(String pattern) {
- throw new NotYetImplementedException();
- }
-
- //TODO: To implement this feature
- public String toString() {
- throw new NotYetImplementedException();
- }
-
- /**
- * Set the delimiting pattern of this scanner
- *
- * @param pattern
- * the delimiting pattern to use
- * @return this scanner
- */
- public Scanner useDelimiter(Pattern pattern) {
- delimiter = pattern;
- return this;
- }
-
- /**
- * Set the delimiting pattern of this scanner with a pattern compiled from
- * the supplied string value
- *
- * @param pattern
- * a string from which a <code>Pattern</code> can be compiled
- * @return this scanner
- */
- public Scanner useDelimiter(String pattern) {
- return useDelimiter(Pattern.compile(pattern));
- }
-
- /**
- *
- * set the locale of this scanner to a specified locale.
- *
- * @param locale
- * the specified locale to use
- * @return
- * this scanner
- */
- public Scanner useLocale(Locale locale) {
- if (null == locale)
- throw new NullPointerException();
- this.locale = locale;
- return this;
- }
-
- /**
- *
- * set the radix of this scanner to a specified radix.
- *
- * @param radix
- * the specified radix to use
- * @return
- * this scanner
- */
- public Scanner useRadix(int radix) {
- if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
- throw new IllegalArgumentException(org.apache.harmony.luni.util.Msg
- .getString("KA008", radix));
- }
- this.radix = radix;
- return this;
- }
-
- /**
- *
- * The operation of remove is not supported by this implementation of
- * Iterator.
- *
- * @return UnsupportedOperationException
- * if this method is invoked
- */
- public void remove() {
- throw new UnsupportedOperationException();
- }
-
- /*
- * Initial some components.
- */
- private void initialization() {
- buffer = CharBuffer.allocate(DEFAULT_TRUNK_SIZE);
- buffer.limit(0);
- matcher = delimiter.matcher(buffer);
- }
-
- /*
- * Check the scanner's state, if it is closed, IllegalStateException will be
- * thrown.
- */
- private void checkClosed() {
- if (closed) {
- throw new IllegalStateException();
- }
- }
-
- /*
- * Check input resource of this scanner, if it has been exhausted, return
- * true.
- */
- private boolean isInputExhausted() {
- if (findStartIndex == bufferLength) {
- if (readMore()) {
- resetMatcher();
- } else {
- return true;
- }
- }
- return false;
- }
-
- /*
- * Change the matcher's string after reading input
- */
- private void resetMatcher() {
- if (null == matcher) {
- matcher = delimiter.matcher(buffer);
- } else {
- matcher.reset(buffer);
- }
- matcher.region(findStartIndex, bufferLength);
- }
-
- /*
- * save the matcher's last find position
- */
- private void saveCurrentStatus() {
- preStartIndex = findStartIndex;
- }
-
- /*
- * Change the matcher's status to last find position
- */
- private void recoverPreviousStatus() {
- findStartIndex = preStartIndex;
- }
-
- /*
- * Find the prefixed delimiter and posefixed delimiter in the input resource
- * and set the start index and end index of Matcher region. If postfixed
- * delimiter does not exist, the end index is set to be end of input.
- */
- private boolean setTokenRegion() {
- // The position where token begins
- int tokenStartIndex = 0;
- // The position where token ends
- int tokenEndIndex = 0;
- // Use delimiter pattern
- matcher.usePattern(delimiter);
- matcher.region(findStartIndex, bufferLength);
-
- tokenStartIndex = findPreDelimiter();
- if (setHeadTokenRegion(tokenStartIndex)) {
- return true;
- }
- tokenEndIndex = findPostDelimiter();
- // If the second delimiter is not found
- if (-1 == tokenEndIndex) {
- // Just first Delimiter Exists
- if (findStartIndex == bufferLength) {
- return false;
- }
- tokenEndIndex = bufferLength;
- findStartIndex = bufferLength;
- }
-
- matcher.region(tokenStartIndex, tokenEndIndex);
- return true;
- }
-
- /*
- * Find prefixed delimiter
- */
- private int findPreDelimiter() {
- int tokenStartIndex;
- boolean findComplete = false;
- while (!findComplete) {
- if (findComplete = matcher.find()) {
- // If just delimiter remains
- if (matcher.start() == findStartIndex
- && matcher.end() == bufferLength) {
- // If more input resource exists
- if (readMore()) {
- resetMatcher();
- findComplete = false;
- }
- }
- } else {
- if (readMore()) {
- resetMatcher();
- } else {
- return -1;
- }
- }
- }
- tokenStartIndex = matcher.end();
- findStartIndex = matcher.end();
- return tokenStartIndex;
- }
-
- /*
- * Handle some special cases
- */
- private boolean setHeadTokenRegion(int findIndex) {
- int tokenStartIndex;
- int tokenEndIndex;
- boolean setSuccess = false;
- // If no delimiter exists, but something exites in this scanner
- if (-1 == findIndex && preStartIndex != bufferLength) {
- tokenStartIndex = preStartIndex;
- tokenEndIndex = bufferLength;
- findStartIndex = bufferLength;
- matcher.region(tokenStartIndex, tokenEndIndex);
- setSuccess = true;
- }
- // If the first delimiter of scanner is not at the find start position
- if (-1 != findIndex && preStartIndex != matcher.start()) {
- tokenStartIndex = preStartIndex;
- tokenEndIndex = matcher.start();
- findStartIndex = matcher.start();
- // set match region and return
- matcher.region(tokenStartIndex, tokenEndIndex);
- setSuccess = true;
- }
- return setSuccess;
- }
-
- /*
- * Find postfixed delimiter
- */
- private int findPostDelimiter() {
- int tokenEndIndex = 0;
- boolean findComplete = false;
- while (!findComplete) {
- if (findComplete = matcher.find()) {
- if (matcher.start() == findStartIndex
- && matcher.start() == matcher.end()) {
- findComplete = false;
- }
- } else {
- if (readMore()) {
- resetMatcher();
- } else {
- return -1;
- }
- }
- }
- tokenEndIndex = matcher.start();
- findStartIndex = matcher.start();
- return tokenEndIndex;
- }
-
- /*
- * Read more data from underlying Readable. Return false if nothing is
- * available or I/O operation fails.
- */
- private boolean readMore() {
- int oldPosition = buffer.position();
- int oldLimit = buffer.limit();
- // Increase capacity if empty space is not enough
- if (buffer.limit() >= buffer.capacity()) {
- expandBuffer();
- }
-
- // Read input resource
- int readCount = 0;
- try {
- buffer.limit(buffer.capacity());
- buffer.position(oldLimit);
- while ((readCount = input.read(buffer)) == 0) {
- // nothing to do here
- }
- } catch (IOException e) {
- readCount = (buffer.position() - oldLimit);
- lastIOException = e;
- }
-
- // The return value of readable.read() method can be used to record the
- // actual characters read. Consider the scenario: readable puts 4 chars into
- // buffer and then an IOException is thrown out. In this case, buffer is
- // actually grown, but readable.read() will never return.
-
- buffer.flip();
- buffer.position(oldPosition);
- if (-1 != readCount)
- bufferLength = readCount + bufferLength;
- return readCount != -1;
- }
-
- // Expand the size of internal buffer.
- private void expandBuffer() {
- int oldPosition = buffer.position();
- int oldCapacity = buffer.capacity();
- int oldLimit = buffer.limit();
- int newCapacity = oldCapacity * DIPLOID;
- char[] newBuffer = new char[newCapacity];
- if (buffer != null) {
- System.arraycopy(buffer.array(), 0, newBuffer, 0, oldLimit);
- }
- buffer = CharBuffer.wrap(newBuffer, 0, newCapacity);
- buffer.position(oldPosition);
- buffer.limit(oldLimit);
- }
-}
+/* Copyright 2006 The Apache Software Foundation or its licensors, as applicable
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package java.util;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.CharBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.charset.Charset;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.harmony.luni.util.NotYetImplementedException;
+
+/**
+ * A parser that parses a text string to primitive types with the help of
+ * regular expression. It supports localized number and various radixes.
+ *
+ * The input is broken into tokens by the delimiter pattern, which is whitespace
+ * by default. The primitive types can be got via corresponding next methods. If
+ * the token is not in valid format, an InputMissmatchException is thrown.
+ *
+ * For example: Scanner s = new Scanner("1A true");
+ * System.out.println(s.nextInt(16)); System.out.println(s.nextBoolean()); The
+ * result: 26 true
+ *
+ * A scanner can find or skip specific pattern with no regard to the delimiter.
+ * All these methods and the various next and hasNext methods may block.
+ *
+ * Scanner is not thread-safe without external synchronization
+ */
+public final class Scanner implements Iterator<String> {
+
+ // Default delimiting pattern
+ private static final Pattern DEFAULT_DELIMITER = Pattern
+ .compile("\\p{javaWhitespace}+"); //$NON-NLS-1$
+
+ //The boolean's pattern
+ private static final Pattern BOOLEAN_PATTERN = Pattern.compile(
+ "true|false", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
+
+ // The pattern matching anything
+ private static final Pattern ANY_PATTERN = Pattern.compile("(?s).*");
+
+ private static final int DIPLOID = 2;
+
+ // Default radix
+ private static final int DEFAULT_RADIX = 10;
+
+ private static final int DEFAULT_TRUNK_SIZE = 1024;
+
+ // The input source of scanner
+ private Readable input;
+
+ private CharBuffer buffer;
+
+ private Pattern delimiter = DEFAULT_DELIMITER;
+
+ private Matcher matcher;
+
+ private int radix = DEFAULT_RADIX;
+
+ private Locale locale = Locale.getDefault();
+
+ // The position where find begins
+ private int findStartIndex = 0;
+
+ // The last find start position
+ private int preStartIndex = findStartIndex;
+
+ // The length of the buffer
+ private int bufferLength = 0;
+
+ // Used by find and nextXXX operation
+ private boolean closed = false;
+
+ private IOException lastIOException;
+
+ private boolean matchSuccessful = false;
+
+ /**
+ * Constructs a scanner that uses File as its input. The default charset is
+ * applied when reading the file.
+ *
+ * @param src
+ * the file to be scanned
+ * @throws FileNotFoundException
+ * if the specified file is not found
+ */
+ public Scanner(File src) throws FileNotFoundException {
+ this(src, Charset.defaultCharset().name());
+ }
+
+ /**
+ * Constructs a scanner that uses File as its input. The specified charset
+ * is applied when reading the file.
+ *
+ * @param src
+ * the file to be scanned
+ * @param charsetName
+ * the name of the encoding type of the file
+ * @throws FileNotFoundException
+ * if the specified file is not found
+ * @throws IllegalArgumentException
+ * if the specified coding does not exist
+ */
+ public Scanner(File src, String charsetName) throws FileNotFoundException {
+ if (null == src) {
+ throw new NullPointerException(org.apache.harmony.luni.util.Msg
+ .getString("KA00a"));
+ }
+ FileInputStream fis = new FileInputStream(src);
+ if (null == charsetName) {
+ throw new IllegalArgumentException(org.apache.harmony.luni.util.Msg
+ .getString("KA009"));
+ }
+ try {
+ input = new InputStreamReader(fis, charsetName);
+ } catch (UnsupportedEncodingException e) {
+ try {
+ fis.close();
+ } catch (IOException ioException) {
+ // ignore
+ }
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ initialization();
+ }
+
+ /**
+ * Constructs a scanner that uses String as its input.
+ *
+ * @param src
+ * the string to be scanned
+ */
+ public Scanner(String src) {
+ input = new StringReader(src);
+ initialization();
+ }
+
+ /**
+ * Constructs a scanner that uses InputStream as its input. The default
+ * charset is applied when decoding the input.
+ *
+ * @param src
+ * the input stream to be scanned
+ */
+ public Scanner(InputStream src) {
+ this(src, Charset.defaultCharset().name());
+ }
+
+ /**
+ * Constructs a scanner that uses InputStream as its input. The specified
+ * charset is applied when decoding the input.
+ *
+ * @param src
+ * the input stream to be scanned
+ * @param charsetName
+ * the encoding type of the input stream
+ * @throws IllegalArgumentException
+ * if the specified character set is not found
+ */
+ public Scanner(InputStream src, String charsetName) {
+ if (null == src) {
+ throw new NullPointerException(org.apache.harmony.luni.util.Msg
+ .getString("KA00b"));
+ }
+ try {
+ input = new InputStreamReader(src, charsetName);
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ initialization();
+ }
+
+ /**
+ * Constructs a scanner that uses Readable as its input.
+ *
+ * @param src
+ * the Readable to be scanned
+ */
+ public Scanner(Readable src) {
+ if (null == src) {
+ throw new NullPointerException();
+ }
+ input = src;
+ initialization();
+ }
+
+ /**
+ * Constructs a scanner that uses ReadableByteChannel as its input. The
+ * default charset is applied when decoding the input.
+ *
+ * @param src
+ * the ReadableByteChannel to be scanned
+ */
+ public Scanner(ReadableByteChannel src) {
+ this(src, Charset.defaultCharset().name());
+ }
+
+ /**
+ * Constructs a scanner that uses ReadableByteChannel as its input. The
+ * specified charset is applied when decoding the input.
+ *
+ * @param src
+ * the ReadableByteChannel to be scanned
+ * @param charsetName
+ * the encoding type of the content in the ReadableByteChannel
+ * @throws IllegalArgumentException
+ * if the specified character set is not found
+ */
+ public Scanner(ReadableByteChannel src, String charsetName) {
+ if (null == src) {
+ throw new NullPointerException(org.apache.harmony.luni.util.Msg
+ .getString("KA00d"));
+ }
+ if (null == charsetName) {
+ throw new IllegalArgumentException(org.apache.harmony.luni.util.Msg
+ .getString("KA009"));
+ }
+ try {
+ input = new InputStreamReader(Channels.newInputStream(src),
+ charsetName);
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ initialization();
+ }
+
+ /**
+ * Closes the underlying input if the input implements Closeable. If the
+ * scanner has been closed, this method will take no effect. The scanning
+ * operation after calling this method will throw IllegalStateException
+ *
+ */
+ public void close() {
+ if (closed == true) {
+ return;
+ }
+ if (input instanceof Closeable) {
+ try {
+ ((Closeable) input).close();
+ } catch (IOException e) {
+ lastIOException = e;
+ }
+ }
+ closed = true;
+ }
+
+ /**
+ * Returns the <code>Pattern</code> in use by this scanner.
+ *
+ * @return the <code>Pattern</code> presently in use by this scanner
+ */
+ public Pattern delimiter() {
+ return delimiter;
+ }
+
+ //TODO: To implement this feature
+ public String findInLine(Pattern pattern) {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public String findInLine(String pattern) {
+ throw new NotYetImplementedException();
+ }
+
+ /**
+ * @param pattern
+ * @param horizon
+ * @return
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ */
+ public String findWithinHorizon(Pattern pattern, int horizon) {
+ checkClosed();
+ if (null == pattern) {
+ throw new NullPointerException();
+ }
+ if (horizon < 0) {
+ throw new IllegalArgumentException(org.apache.harmony.luni.util.Msg
+ .getString("KA00e")); //$NON-NLS-1$
+ }
+ matcher.usePattern(pattern);
+
+ boolean isInputExhausted = false;
+ String result = null;
+ int findEndIndex = 0;
+ int horizonEndIndex = 0;
+ if (horizon == 0) {
+ horizonEndIndex = Integer.MAX_VALUE;
+ } else {
+ horizonEndIndex = findStartIndex + horizon;
+ }
+ while (true) {
+ findEndIndex = bufferLength;
+
+ // If horizon > 0, then search up to
+ // min( bufferLength, findStartIndex + horizon).
+ // Otherwise search until readable is exhausted.
+ findEndIndex = Math.min(horizonEndIndex, bufferLength);
+ // If horizon == 0, consider horizon as always outside buffer.
+ boolean isHorizonInBuffer = (horizonEndIndex <= bufferLength);
+ // First, try to find pattern within buffer. If pattern can not be
+ // found in buffer, then expand the buffer and try again,
+ // util horizonEndIndex is exceeded or no more input left.
+ matcher.region(findStartIndex, findEndIndex);
+ if (matcher.find()) {
+ if (isHorizonInBuffer || isInputExhausted) {
+ result = matcher.group();
+ break;
+ }
+ } else {
+ // Pattern is not found in buffer while horizonEndIndex is
+ // within buffer, or input is exhausted. Under this situation,
+ // it can be judged that find fails.
+ if (isHorizonInBuffer || isInputExhausted) {
+ break;
+ }
+ }
+
+ // Expand buffer and reset matcher if needed.
+ if (readMore()) {
+ matcher.reset(buffer);
+ } else {
+ isInputExhausted = true;
+ }
+ }
+ if (null != result) {
+ findStartIndex = matcher.end();
+ matchSuccessful = true;
+ } else {
+ matchSuccessful = false;
+ }
+ return result;
+ }
+
+ //TODO: To implement this feature
+ public String findWithinHorizon(String pattern, int horizon) {
+ throw new NotYetImplementedException();
+ }
+
+ /**
+ * Returns true if this scanner has next token. This method may be blocked
+ * when it is waiting for input to scan. This scanner does not advance past
+ * the input.
+ *
+ * @return true
+ * iff this scanner has next token
+ * @throws IllegalStateException
+ * if the scanner has been closed
+ */
+ public boolean hasNext() {
+ return hasNext(ANY_PATTERN);
+ }
+
+ /**
+ * Returns true if this scanner's next token matches the specified pattern.
+ * This method may be blocked when it is waiting for input to scan. This
+ * scanner does not advance past the input that matched the pattern.
+ *
+ * @param pattern
+ * the specified pattern to scan
+ * @return
+ * true iff this scanner's next token matches the specified pattern
+ * @throws IllegalStateException
+ * if the scanner has been closed
+ */
+ public boolean hasNext(Pattern pattern) {
+ checkClosed();
+ if (isInputExhausted()) {
+ return false;
+ }
+ saveCurrentStatus();
+ //if the next token exists, set the match region, otherwise return false
+ if (!setTokenRegion()) {
+ recoverPreviousStatus();
+ return false;
+ }
+ matcher.usePattern(pattern);
+ boolean hasNext = false;
+ //check whether next token matches the specified pattern
+ if (matcher.matches()) {
+ hasNext = true;
+ }
+ recoverPreviousStatus();
+ return hasNext;
+ }
+
+
+ /**
+ * Returns true if this scanner's next token matches the pattern constructed
+ * from the specified string. This method may be blocked when it is waiting
+ * for input to scan. This scanner does not advance past the input that
+ * matched the pattern.
+ *
+ * The invocation of this method in the form hasNext(pattern) behaves in the
+ * same way as the invocaiton of hasNext(Pattern.compile(pattern)).
+ *
+ * @param pattern
+ * the string specifying the pattern to scan for
+ * @return true
+ * iff this scanner's next token matches the specified pattern
+ * @throws IllegalStateException
+ * if the scanner has been closed
+ */
+ public boolean hasNext(String pattern) {
+ return hasNext(Pattern.compile(pattern));
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextBigDecimal() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextBigInteger() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextBigInteger(int radix) {
+ throw new NotYetImplementedException();
+ }
+
+ /**
+ * Returns true if this scanner's next token can be translated into a valid
+ * boolean value. The scanner does not advance past the input that matched.
+ *
+ * @return true
+ * iff the next token in this scanner's input can be translated
+ * into a valid boolean value
+ * @throws IllegalStateException
+ * if the scanner has been closed
+ */
+ public boolean hasNextBoolean() {
+ return hasNext(BOOLEAN_PATTERN);
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextByte() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextByte(int radix) {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextDouble() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextFloat() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextInt() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextInt(int radix) {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextLine() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextLong() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextLong(int radix) {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextShort() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public boolean hasNextShort(int radix) {
+ throw new NotYetImplementedException();
+ }
+
+ /**
+ * returns the last IOException thrown when reading the underlying input. If
+ * no exception is thrown, return null.
+ *
+ * @return the last IOException thrown
+ */
+ public IOException ioException() {
+ return lastIOException;
+ }
+
+ /**
+ * return the locale of this scanner.
+ *
+ * @return
+ * the locale of this scanner
+ */
+ public Locale locale() {
+ return locale;
+ }
+
+ /**
+ * Returns the match result of this scanner's last match operation.This
+ * method throws IllegalStateException if no match operation has been
+ * performed, or if the last match was unsuccessful.
+ *
+ * The various nextXXX methods of Scanner provide a match result if they do
+ * not complete with throwing an exception. For example, after an invocation
+ * of the nextBoolean() method which returned a boolean value, this method
+ * returns a match result for the search of the Boolean regular expression
+ * defined above. In the same way,the findInLine(java.lang.String),
+ * findWithinHorizon(java.lang.String, int), and
+ * skip(java.util.regex.Pattern) methods will provide a match result if they
+ * are successful.
+ *
+ * @return the match result of the last match operation
+ * @throws IllegalStateException
+ * if the match result is available
+ */
+ public MatchResult match() {
+ if (!matchSuccessful) {
+ throw new IllegalStateException();
+ }
+ return matcher.toMatchResult();
+ }
+
+ /**
+ * Finds and Returns the next complete token which is prefixed and postfixed
+ * by input that matches the delimiter pattern. This method may be blocked
+ * when it is waiting for input to scan, even if a previous invocation of
+ * hasNext() returned true. If this match successes, the scanner advances
+ * past the next complete token.
+ *
+ * @return
+ * the next complete token
+ * @throws IllegalStateException
+ * if this scanner has been closed
+ * @throws NoSuchElementException
+ * if input has been exhausted
+ */
+ public String next() {
+ return next(ANY_PATTERN);
+ }
+
+ /**
+ * Returns the next token which is prefixed and postfixed by input that
+ * matches the delimiter pattern if this token matches the specified
+ * pattern. This method may be blocked when it is waiting for input to scan,
+ * even if a previous invocation of hasNext(Pattern) returned true. If this
+ * match successes, the scanner advances past the next token that matched
+ * the pattern.
+ *
+ * @param pattern
+ * the specified pattern to scan
+ * @return
+ * the next token
+ * @throws IllegalStateException
+ * if this scanner has been closed
+ * @throws NoSuchElementException
+ * if input has been exhausted
+ */
+ public String next(Pattern pattern) {
+ checkClosed();
+ if (null == pattern) {
+ throw new NullPointerException();
+ }
+ matchSuccessful = false;
+ if (isInputExhausted()) {
+ throw new NoSuchElementException();
+ }
+ saveCurrentStatus();
+ if (!setTokenRegion()) {
+ recoverPreviousStatus();
+ // if setting match region fails
+ throw new NoSuchElementException();
+ }
+ matcher.usePattern(pattern);
+ if (matchSuccessful = matcher.matches()) {
+ return matcher.group(0);
+ } else {
+ recoverPreviousStatus();
+ throw new InputMismatchException();
+ }
+ }
+
+ /**
+ * Returns the next token which is prefixed and postfixed by input that
+ * matches the delimiter pattern if this token matches the pattern
+ * constructed from the sepcified string. This method may be blocked when it
+ * is waiting for input to scan. If this match successes, the scanner
+ * advances past the next token that matched the pattern.
+ *
+ * The invocation of this method in the form next(pattern) behaves in the
+ * same way as the invocaiton of next(Pattern.compile(pattern)).
+ *
+ * @param pattern
+ * the string specifying the pattern to scan for
+ * @return
+ * the next token
+ * @throws IllegalStateException
+ * if this scanner has been closed
+ * @throws NoSuchElementException
+ * if input has been exhausted
+ */
+ public String next(String pattern) {
+ return next(Pattern.compile(pattern));
+ }
+
+ //TODO: To implement this feature
+ public BigDecimal nextBigDecimal() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public BigInteger nextBigInteger() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public BigInteger nextBigInteger(int radix) {
+ throw new NotYetImplementedException();
+ }
+
+ /**
+ * Translates the next token in this scanner's input into a boolean value and
+ * returns this value. This method will throw InputMismatchException if the
+ * next token can not be interpreted as a boolean value with a case
+ * insensitive pattern created from the string "true|false". If this match
+ * succeeds, the scanner advances past the input that matched.
+ *
+ * @return the boolean value scanned from the input
+ * @throws IllegalStateException
+ * if this scanner has been closed
+ * @throws NoSuchElementException
+ * if input has been exhausted
+ * @throws InputMismatchException
+ * if the next token can not be translated into a valid boolean
+ * value
+ */
+ public boolean nextBoolean() {
+ return Boolean.parseBoolean(next(BOOLEAN_PATTERN));
+ }
+
+ //TODO: To implement this feature
+ public byte nextByte() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public byte nextByte(int radix) {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public double nextDouble() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public float nextFloat() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public int nextInt() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public int nextInt(int radix) {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public String nextLine() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public long nextLong() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public long nextLong(int radix) {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public short nextShort() {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public short nextShort(int radix) {
+ throw new NotYetImplementedException();
+ }
+
+ /**
+ * return the radix of this scanner.
+ *
+ * @return
+ * the radix of this scanner
+ */
+ public int radix() {
+ return radix;
+ }
+
+ //TODO: To implement this feature
+ public Scanner skip(Pattern pattern) {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public Scanner skip(String pattern) {
+ throw new NotYetImplementedException();
+ }
+
+ //TODO: To implement this feature
+ public String toString() {
+ throw new NotYetImplementedException();
+ }
+
+ /**
+ * Set the delimiting pattern of this scanner
+ *
+ * @param pattern
+ * the delimiting pattern to use
+ * @return this scanner
+ */
+ public Scanner useDelimiter(Pattern pattern) {
+ delimiter = pattern;
+ return this;
+ }
+
+ /**
+ * Set the delimiting pattern of this scanner with a pattern compiled from
+ * the supplied string value
+ *
+ * @param pattern
+ * a string from which a <code>Pattern</code> can be compiled
+ * @return this scanner
+ */
+ public Scanner useDelimiter(String pattern) {
+ return useDelimiter(Pattern.compile(pattern));
+ }
+
+ /**
+ *
+ * set the locale of this scanner to a specified locale.
+ *
+ * @param locale
+ * the specified locale to use
+ * @return
+ * this scanner
+ */
+ public Scanner useLocale(Locale locale) {
+ if (null == locale)
+ throw new NullPointerException();
+ this.locale = locale;
+ return this;
+ }
+
+ /**
+ *
+ * set the radix of this scanner to a specified radix.
+ *
+ * @param radix
+ * the specified radix to use
+ * @return
+ * this scanner
+ */
+ public Scanner useRadix(int radix) {
+ if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
+ throw new IllegalArgumentException(org.apache.harmony.luni.util.Msg
+ .getString("KA008", radix));
+ }
+ this.radix = radix;
+ return this;
+ }
+
+ /**
+ *
+ * The operation of remove is not supported by this implementation of
+ * Iterator.
+ *
+ * @return UnsupportedOperationException
+ * if this method is invoked
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ /*
+ * Initial some components.
+ */
+ private void initialization() {
+ buffer = CharBuffer.allocate(DEFAULT_TRUNK_SIZE);
+ buffer.limit(0);
+ matcher = delimiter.matcher(buffer);
+ }
+
+ /*
+ * Check the scanner's state, if it is closed, IllegalStateException will be
+ * thrown.
+ */
+ private void checkClosed() {
+ if (closed) {
+ throw new IllegalStateException();
+ }
+ }
+
+ /*
+ * Check input resource of this scanner, if it has been exhausted, return
+ * true.
+ */
+ private boolean isInputExhausted() {
+ if (findStartIndex == bufferLength) {
+ if (readMore()) {
+ resetMatcher();
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * Change the matcher's string after reading input
+ */
+ private void resetMatcher() {
+ if (null == matcher) {
+ matcher = delimiter.matcher(buffer);
+ } else {
+ matcher.reset(buffer);
+ }
+ matcher.region(findStartIndex, bufferLength);
+ }
+
+ /*
+ * save the matcher's last find position
+ */
+ private void saveCurrentStatus() {
+ preStartIndex = findStartIndex;
+ }
+
+ /*
+ * Change the matcher's status to last find position
+ */
+ private void recoverPreviousStatus() {
+ findStartIndex = preStartIndex;
+ }
+
+ /*
+ * Find the prefixed delimiter and posefixed delimiter in the input resource
+ * and set the start index and end index of Matcher region. If postfixed
+ * delimiter does not exist, the end index is set to be end of input.
+ */
+ private boolean setTokenRegion() {
+ // The position where token begins
+ int tokenStartIndex = 0;
+ // The position where token ends
+ int tokenEndIndex = 0;
+ // Use delimiter pattern
+ matcher.usePattern(delimiter);
+ matcher.region(findStartIndex, bufferLength);
+
+ tokenStartIndex = findPreDelimiter();
+ if (setHeadTokenRegion(tokenStartIndex)) {
+ return true;
+ }
+ tokenEndIndex = findPostDelimiter();
+ // If the second delimiter is not found
+ if (-1 == tokenEndIndex) {
+ // Just first Delimiter Exists
+ if (findStartIndex == bufferLength) {
+ return false;
+ }
+ tokenEndIndex = bufferLength;
+ findStartIndex = bufferLength;
+ }
+
+ matcher.region(tokenStartIndex, tokenEndIndex);
+ return true;
+ }
+
+ /*
+ * Find prefixed delimiter
+ */
+ private int findPreDelimiter() {
+ int tokenStartIndex;
+ boolean findComplete = false;
+ while (!findComplete) {
+ if (findComplete = matcher.find()) {
+ // If just delimiter remains
+ if (matcher.start() == findStartIndex
+ && matcher.end() == bufferLength) {
+ // If more input resource exists
+ if (readMore()) {
+ resetMatcher();
+ findComplete = false;
+ }
+ }
+ } else {
+ if (readMore()) {
+ resetMatcher();
+ } else {
+ return -1;
+ }
+ }
+ }
+ tokenStartIndex = matcher.end();
+ findStartIndex = matcher.end();
+ return tokenStartIndex;
+ }
+
+ /*
+ * Handle some special cases
+ */
+ private boolean setHeadTokenRegion(int findIndex) {
+ int tokenStartIndex;
+ int tokenEndIndex;
+ boolean setSuccess = false;
+ // If no delimiter exists, but something exites in this scanner
+ if (-1 == findIndex && preStartIndex != bufferLength) {
+ tokenStartIndex = preStartIndex;
+ tokenEndIndex = bufferLength;
+ findStartIndex = bufferLength;
+ matcher.region(tokenStartIndex, tokenEndIndex);
+ setSuccess = true;
+ }
+ // If the first delimiter of scanner is not at the find start position
+ if (-1 != findIndex && preStartIndex != matcher.start()) {
+ tokenStartIndex = preStartIndex;
+ tokenEndIndex = matcher.start();
+ findStartIndex = matcher.start();
+ // set match region and return
+ matcher.region(tokenStartIndex, tokenEndIndex);
+ setSuccess = true;
+ }
+ return setSuccess;
+ }
+
+ /*
+ * Find postfixed delimiter
+ */
+ private int findPostDelimiter() {
+ int tokenEndIndex = 0;
+ boolean findComplete = false;
+ while (!findComplete) {
+ if (findComplete = matcher.find()) {
+ if (matcher.start() == findStartIndex
+ && matcher.start() == matcher.end()) {
+ findComplete = false;
+ }
+ } else {
+ if (readMore()) {
+ resetMatcher();
+ } else {
+ return -1;
+ }
+ }
+ }
+ tokenEndIndex = matcher.start();
+ findStartIndex = matcher.start();
+ return tokenEndIndex;
+ }
+
+ /*
+ * Read more data from underlying Readable. Return false if nothing is
+ * available or I/O operation fails.
+ */
+ private boolean readMore() {
+ int oldPosition = buffer.position();
+ int oldLimit = buffer.limit();
+ // Increase capacity if empty space is not enough
+ if (buffer.limit() >= buffer.capacity()) {
+ expandBuffer();
+ }
+
+ // Read input resource
+ int readCount = 0;
+ try {
+ buffer.limit(buffer.capacity());
+ buffer.position(oldLimit);
+ while ((readCount = input.read(buffer)) == 0) {
+ // nothing to do here
+ }
+ } catch (IOException e) {
+ readCount = (buffer.position() - oldLimit);
+ lastIOException = e;
+ }
+
+ // The return value of readable.read() method can be used to record the
+ // actual characters read. Consider the scenario: readable puts 4 chars into
+ // buffer and then an IOException is thrown out. In this case, buffer is
+ // actually grown, but readable.read() will never return.
+
+ buffer.flip();
+ buffer.position(oldPosition);
+ if (-1 != readCount)
+ bufferLength = readCount + bufferLength;
+ return readCount != -1;
+ }
+
+ // Expand the size of internal buffer.
+ private void expandBuffer() {
+ int oldPosition = buffer.position();
+ int oldCapacity = buffer.capacity();
+ int oldLimit = buffer.limit();
+ int newCapacity = oldCapacity * DIPLOID;
+ char[] newBuffer = new char[newCapacity];
+ if (buffer != null) {
+ System.arraycopy(buffer.array(), 0, newBuffer, 0, oldLimit);
+ }
+ buffer = CharBuffer.wrap(newBuffer, 0, newCapacity);
+ buffer.position(oldPosition);
+ buffer.limit(oldLimit);
+ }
+}