0001: /* Licensed to the Apache Software Foundation (ASF) under one or more
0002: * contributor license agreements. See the NOTICE file distributed with
0003: * this work for additional information regarding copyright ownership.
0004: * The ASF licenses this file to You under the Apache License, Version 2.0
0005: * (the "License"); you may not use this file except in compliance with
0006: * the License. You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package java.util;
0017:
0018: import java.io.Closeable;
0019: import java.io.File;
0020: import java.io.FileInputStream;
0021: import java.io.FileNotFoundException;
0022: import java.io.IOException;
0023: import java.io.InputStream;
0024: import java.io.InputStreamReader;
0025: import java.io.StringReader;
0026: import java.io.UnsupportedEncodingException;
0027: import java.math.BigDecimal;
0028: import java.math.BigInteger;
0029: import java.nio.CharBuffer;
0030: import java.nio.channels.Channels;
0031: import java.nio.channels.ReadableByteChannel;
0032: import java.nio.charset.Charset;
0033: import java.text.DecimalFormat;
0034: import java.text.NumberFormat;
0035: import java.util.regex.MatchResult;
0036: import java.util.regex.Matcher;
0037: import java.util.regex.Pattern;
0038:
0039: /**
0040: * A parser that parses a text string of primitive types and strings with the
0041: * help of regular expressions. It supports localized numbers and various
0042: * radixes.
0043: *
0044: * The input is broken into tokens by the delimiter pattern, which is whitespace
0045: * by default. The primitive types can be obtained via corresponding next*
0046: * methods. If the token is not in a valid format, an InputMismatchException is
0047: * thrown.
0048: *
0049: * For example: <code>Scanner s = new Scanner("1A true");
0050: * System.out.println(s.nextInt(16)); System.out.println(s.nextBoolean());</code>
0051: * The result: <code>26 true</code>
0052: *
0053: * A scanner can also find or skip specific patterns with no regard to the
0054: * delimiter. All these methods and the various next* and hasNext* methods may
0055: * block.
0056: *
0057: * The Scanner class is not thread-safe.
0058: */
0059: public final class Scanner implements Iterator<String> {
0060:
0061: // Default delimiting pattern.
0062: private static final Pattern DEFAULT_DELIMITER = Pattern
0063: .compile("\\p{javaWhitespace}+"); //$NON-NLS-1$
0064:
0065: // The boolean's pattern.
0066: private static final Pattern BOOLEAN_PATTERN = Pattern.compile(
0067: "true|false", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
0068:
0069: // Pattern used to recognize line terminator.
0070: private static final Pattern LINE_TERMINATOR;
0071:
0072: // Pattern used to recognize multiple line terminators.
0073: private static final Pattern MULTI_LINE_TERMINATOR;
0074:
0075: // Pattern used to recognize a line with a line terminator.
0076: private static final Pattern LINE_PATTERN;
0077:
0078: static {
0079: String terminator = "\n|\r\n|\r|\u0085|\u2028|\u2029"; //$NON-NLS-1$
0080:
0081: LINE_TERMINATOR = Pattern.compile(terminator);
0082:
0083: StringBuilder multiTerminator = new StringBuilder();
0084: MULTI_LINE_TERMINATOR = Pattern.compile(multiTerminator.append(
0085: "(") //$NON-NLS-1$
0086: .append(terminator).append(")+").toString()); //$NON-NLS-1$
0087: StringBuilder line = new StringBuilder();
0088: LINE_PATTERN = Pattern.compile(line.append(".*(") //$NON-NLS-1$
0089: .append(terminator).append(")|.+(") //$NON-NLS-1$
0090: .append(terminator).append(")?").toString()); //$NON-NLS-1$
0091: }
0092:
0093: // The pattern matches anything.
0094: private static final Pattern ANY_PATTERN = Pattern
0095: .compile("(?s).*"); //$NON-NLS-1$
0096:
0097: private static final int DIPLOID = 2;
0098:
0099: // Default radix.
0100: private static final int DEFAULT_RADIX = 10;
0101:
0102: private static final int DEFAULT_TRUNK_SIZE = 1024;
0103:
0104: // The input source of scanner.
0105: private Readable input;
0106:
0107: private CharBuffer buffer;
0108:
0109: private Pattern delimiter = DEFAULT_DELIMITER;
0110:
0111: private Matcher matcher;
0112:
0113: private int integerRadix = DEFAULT_RADIX;
0114:
0115: private Locale locale = Locale.getDefault();
0116:
0117: // The position where find begins.
0118: private int findStartIndex = 0;
0119:
0120: // The last find start position.
0121: private int preStartIndex = findStartIndex;
0122:
0123: // The length of the buffer.
0124: private int bufferLength = 0;
0125:
0126: // Record the status of this scanner. True if the scanner
0127: // is closed.
0128: private boolean closed = false;
0129:
0130: private IOException lastIOException;
0131:
0132: private boolean matchSuccessful = false;
0133:
0134: private DecimalFormat decimalFormat;
0135:
0136: // Records whether the underlying readable has more input.
0137: private boolean inputExhausted = false;
0138:
0139: private Object cacheHasNextValue = null;
0140:
0141: private int cachehasNextIndex = -1;
0142:
0143: private enum DataType {
0144: /*
0145: * Stands for Integer
0146: */
0147: INT,
0148: /*
0149: * Stands for Float
0150: */
0151: FLOAT;
0152: }
0153:
0154: /**
0155: * Constructs a scanner that uses File as its input. The default charset is
0156: * applied when reading the file.
0157: *
0158: * @param src
0159: * the file to be scanned
0160: * @throws FileNotFoundException
0161: * if the specified file is not found
0162: */
0163: public Scanner(File src) throws FileNotFoundException {
0164: this (src, Charset.defaultCharset().name());
0165: }
0166:
0167: /**
0168: * Constructs a scanner that uses File as its input. The specified charset
0169: * is applied when reading the file.
0170: *
0171: * @param src
0172: * the file to be scanned
0173: * @param charsetName
0174: * the name of the encoding type of the file
0175: * @throws FileNotFoundException
0176: * if the specified file is not found
0177: * @throws IllegalArgumentException
0178: * if the specified coding does not exist
0179: */
0180: public Scanner(File src, String charsetName)
0181: throws FileNotFoundException {
0182: if (null == src) {
0183: throw new NullPointerException(
0184: org.apache.harmony.luni.util.Msg.getString("KA00a")); //$NON-NLS-1$
0185: }
0186: FileInputStream fis = new FileInputStream(src);
0187: if (null == charsetName) {
0188: throw new IllegalArgumentException(
0189: org.apache.harmony.luni.util.Msg.getString("KA009")); //$NON-NLS-1$
0190: }
0191: try {
0192: input = new InputStreamReader(fis, charsetName);
0193: } catch (UnsupportedEncodingException e) {
0194: try {
0195: fis.close();
0196: } catch (IOException ioException) {
0197: // ignore
0198: }
0199: throw new IllegalArgumentException(e.getMessage());
0200: }
0201: initialization();
0202: }
0203:
0204: /**
0205: * Constructs a scanner that uses String as its input.
0206: *
0207: * @param src
0208: * the string to be scanned
0209: */
0210: public Scanner(String src) {
0211: input = new StringReader(src);
0212: initialization();
0213: }
0214:
0215: /**
0216: * Constructs a scanner that uses InputStream as its input. The default
0217: * charset is applied when decoding the input.
0218: *
0219: * @param src
0220: * the input stream to be scanned
0221: */
0222: public Scanner(InputStream src) {
0223: this (src, Charset.defaultCharset().name());
0224: }
0225:
0226: /**
0227: * Constructs a scanner that uses InputStream as its input. The specified
0228: * charset is applied when decoding the input.
0229: *
0230: * @param src
0231: * the input stream to be scanned
0232: * @param charsetName
0233: * the encoding type of the input stream
0234: * @throws IllegalArgumentException
0235: * if the specified character set is not found
0236: */
0237: public Scanner(InputStream src, String charsetName) {
0238: if (null == src) {
0239: throw new NullPointerException(
0240: org.apache.harmony.luni.util.Msg.getString("KA00b")); //$NON-NLS-1$
0241: }
0242: try {
0243: input = new InputStreamReader(src, charsetName);
0244: } catch (UnsupportedEncodingException e) {
0245: throw new IllegalArgumentException(e.getMessage());
0246: }
0247: initialization();
0248: }
0249:
0250: /**
0251: * Constructs a scanner that uses Readable as its input.
0252: *
0253: * @param src
0254: * the Readable to be scanned
0255: */
0256: public Scanner(Readable src) {
0257: if (null == src) {
0258: throw new NullPointerException();
0259: }
0260: input = src;
0261: initialization();
0262: }
0263:
0264: /**
0265: * Constructs a scanner that uses ReadableByteChannel as its input. The
0266: * default charset is applied when decoding the input.
0267: *
0268: * @param src
0269: * the ReadableByteChannel to be scanned
0270: */
0271: public Scanner(ReadableByteChannel src) {
0272: this (src, Charset.defaultCharset().name());
0273: }
0274:
0275: /**
0276: * Constructs a scanner that uses ReadableByteChannel as its input. The
0277: * specified charset is applied when decoding the input.
0278: *
0279: * @param src
0280: * the ReadableByteChannel to be scanned
0281: * @param charsetName
0282: * the encoding type of the content in the ReadableByteChannel
0283: * @throws IllegalArgumentException
0284: * if the specified character set is not found
0285: */
0286: public Scanner(ReadableByteChannel src, String charsetName) {
0287: if (null == src) {
0288: throw new NullPointerException(
0289: org.apache.harmony.luni.util.Msg.getString("KA00d")); //$NON-NLS-1$
0290: }
0291: if (null == charsetName) {
0292: throw new IllegalArgumentException(
0293: org.apache.harmony.luni.util.Msg.getString("KA009")); //$NON-NLS-1$
0294: }
0295: input = Channels.newReader(src, charsetName);
0296: initialization();
0297: }
0298:
0299: /**
0300: * Closes the underlying input if the input implements Closeable. If the
0301: * scanner has been closed, this method will take no effect. The scanning
0302: * operation after calling this method will throw IllegalStateException
0303: *
0304: */
0305: public void close() {
0306: if (closed) {
0307: return;
0308: }
0309: if (input instanceof Closeable) {
0310: try {
0311: ((Closeable) input).close();
0312: } catch (IOException e) {
0313: lastIOException = e;
0314: }
0315: }
0316: closed = true;
0317: }
0318:
0319: /**
0320: * Returns the <code>Pattern</code> in use by this scanner.
0321: *
0322: * @return the <code>Pattern</code> presently in use by this scanner
0323: */
0324: public Pattern delimiter() {
0325: return delimiter;
0326: }
0327:
0328: /**
0329: * Tries to find the pattern in input. Delimiters are ignored. If the
0330: * pattern is found before line terminator, the matched string will be
0331: * returned, and the scanner will advance to the end of the matched string.
0332: * Otherwise, null will be returned and the scanner will not advance the
0333: * input. When waiting for input, the scanner may be blocked.
0334: *
0335: * All the input may be cached if no line terminator exists in the buffer.
0336: *
0337: * @param pattern
0338: * the pattern used to match input
0339: * @return the matched string
0340: * @throws IllegalStateException
0341: * if the scanner is closed
0342: */
0343: public String findInLine(Pattern pattern) {
0344: checkClosed();
0345: checkNull(pattern);
0346: int horizonLineSeparator = 0;
0347:
0348: matcher.usePattern(MULTI_LINE_TERMINATOR);
0349: matcher.region(findStartIndex, bufferLength);
0350:
0351: boolean findComplete = false;
0352: int terminatorLength = 0;
0353: while (!findComplete) {
0354: if (matcher.find()) {
0355: horizonLineSeparator = matcher.start();
0356: terminatorLength = matcher.end() - matcher.start();
0357: findComplete = true;
0358: } else {
0359: if (!inputExhausted) {
0360: readMore();
0361: resetMatcher();
0362: } else {
0363: horizonLineSeparator = bufferLength;
0364: findComplete = true;
0365: }
0366: }
0367: }
0368:
0369: matcher.usePattern(pattern);
0370:
0371: /*
0372: * TODO The following 2 statements are used to deal with regex's bug.
0373: * java.util.regex.Matcher.region(int start, int end) implementation
0374: * does not have any effects when called. They will be removed once the
0375: * bug is fixed.
0376: */
0377: int oldLimit = buffer.limit();
0378: buffer.limit(horizonLineSeparator);
0379: // ========== To deal with regex bug ====================
0380:
0381: matcher.region(findStartIndex, horizonLineSeparator);
0382: if (matcher.find()) {
0383: // The scanner advances past the input that matched
0384: findStartIndex = matcher.end();
0385: // If the matched pattern is immediately followed by line
0386: // terminator.
0387: if (horizonLineSeparator == matcher.end()) {
0388: findStartIndex += terminatorLength;
0389: }
0390: matchSuccessful = true;
0391:
0392: // ========== To deal with regex bug ====================
0393: buffer.limit(oldLimit);
0394: // ========== To deal with regex bug ====================
0395:
0396: return matcher.group();
0397: }
0398:
0399: // ========== To deal with regex bug ====================
0400: buffer.limit(oldLimit);
0401: // ========== To deal with regex bug ====================
0402:
0403: matchSuccessful = false;
0404: return null;
0405: }
0406:
0407: /**
0408: * Tries to find the pattern compiled from the specified string. The
0409: * delimiter will be ignored. It is the same as invoke
0410: * findInLine(Pattern.compile(pattern))
0411: *
0412: * @param pattern
0413: * a string used to construct a pattern which in turn used to
0414: * match input
0415: * @return the matched string
0416: * @throws IllegalStateException
0417: * if the scanner is closed
0418: */
0419: public String findInLine(String pattern) {
0420: return findInLine(Pattern.compile(pattern));
0421: }
0422:
0423: /**
0424: * Tries to find the pattern in input from current position to the specified
0425: * horizon. Delimiters are ignored. If the pattern is found, the matched
0426: * string will be returned, and the scanner will advance to the end of the
0427: * matched string. Otherwise, null will be returned and scanner will not
0428: * advance the input. When waiting for input, the scanner may be blocked.
0429: *
0430: * Scanner will never search exceed horizon code points from current
0431: * position. The position of horizon does have effects on the result of
0432: * match. For example, when input is "123", and current position is at zero,
0433: * findWithinHorizon(Pattern.compile("\\p{Digit}{3}"), 2) will return null.
0434: * While findWithinHorizon(Pattern.compile("\\p{Digit}{3}"), 3) will return
0435: * "123". Horizon is treated as a transparent, non-anchoring bound. (refer
0436: * to {@link Matcher#useTransparentBounds} and
0437: * {@link Matcher#useAnchoringBounds})
0438: *
0439: * Horizon whose value is zero will be ignored and the whole input will be
0440: * used for search. Under this situation, all the input may be cached.
0441: *
0442: * An IllegalArgumentException will be thrown out if horizon is less than
0443: * zero.
0444: *
0445: * @param pattern
0446: * the pattern used to scan
0447: * @param horizon
0448: * the search limit
0449: * @return the matched string
0450: * @throws IllegalStateException
0451: * if the scanner is closed
0452: * @throws IllegalArgumentException
0453: * if horizon is less than zero
0454: */
0455: public String findWithinHorizon(Pattern pattern, int horizon) {
0456: checkClosed();
0457: checkNull(pattern);
0458: if (horizon < 0) {
0459: throw new IllegalArgumentException(
0460: org.apache.harmony.luni.util.Msg.getString("KA00e")); //$NON-NLS-1$
0461: }
0462: matcher.usePattern(pattern);
0463:
0464: String result = null;
0465: int findEndIndex = 0;
0466: int horizonEndIndex = 0;
0467: if (horizon == 0) {
0468: horizonEndIndex = Integer.MAX_VALUE;
0469: } else {
0470: horizonEndIndex = findStartIndex + horizon;
0471: }
0472: while (true) {
0473: findEndIndex = bufferLength;
0474:
0475: // If horizon > 0, then search up to
0476: // min( bufferLength, findStartIndex + horizon).
0477: // Otherwise search until readable is exhausted.
0478: findEndIndex = Math.min(horizonEndIndex, bufferLength);
0479: // If horizon == 0, consider horizon as always outside buffer.
0480: boolean isHorizonInBuffer = (horizonEndIndex <= bufferLength);
0481: // First, try to find pattern within buffer. If pattern can not be
0482: // found in buffer, then expand the buffer and try again,
0483: // util horizonEndIndex is exceeded or no more input left.
0484: matcher.region(findStartIndex, findEndIndex);
0485: if (matcher.find()) {
0486: if (isHorizonInBuffer || inputExhausted) {
0487: result = matcher.group();
0488: break;
0489: }
0490: } else {
0491: // Pattern is not found in buffer while horizonEndIndex is
0492: // within buffer, or input is exhausted. Under this situation,
0493: // it can be judged that find fails.
0494: if (isHorizonInBuffer || inputExhausted) {
0495: break;
0496: }
0497: }
0498:
0499: // Expand buffer and reset matcher if needed.
0500: if (!inputExhausted) {
0501: readMore();
0502: resetMatcher();
0503: }
0504: }
0505: if (null != result) {
0506: findStartIndex = matcher.end();
0507: matchSuccessful = true;
0508: } else {
0509: matchSuccessful = false;
0510: }
0511: return result;
0512: }
0513:
0514: /**
0515: * Tries to find the pattern in input from current position to the specified
0516: * horizon. Delimiters are ignored.
0517: *
0518: * It is the same as invoke findWithinHorizon(Pattern.compile(pattern)).
0519: *
0520: * @param pattern
0521: * the pattern used to scan
0522: * @param horizon
0523: * the search limit
0524: * @return the matched string
0525: * @throws IllegalStateException
0526: * if the scanner is closed
0527: * @throws IllegalArgumentException
0528: * if horizon is less than zero
0529: */
0530: public String findWithinHorizon(String pattern, int horizon) {
0531: return findWithinHorizon(Pattern.compile(pattern), horizon);
0532: }
0533:
0534: /**
0535: * Returns true if this scanner has one or more tokens remaining to parse.
0536: * Will block if the data is still being read.
0537: *
0538: * @return true if this scanner has one or more tokens remaining
0539: * @throws IllegalStateException
0540: * if the scanner has been closed
0541: */
0542: public boolean hasNext() {
0543: return hasNext(ANY_PATTERN);
0544: }
0545:
0546: /**
0547: * Returns true if this scanner has one or more tokens remaining to parse
0548: * and the next token matches the given pattern. Will block if the data is
0549: * still being read.
0550: *
0551: * @param pattern
0552: * the pattern to check for
0553: * @return true if this scanner has more tokens and the next token matches
0554: * the pattern
0555: * @throws IllegalStateException
0556: * if the scanner has been closed
0557: */
0558: public boolean hasNext(Pattern pattern) {
0559: checkClosed();
0560: checkNull(pattern);
0561: matchSuccessful = false;
0562: saveCurrentStatus();
0563: // if the next token exists, set the match region, otherwise return
0564: // false
0565: if (!setTokenRegion()) {
0566: recoverPreviousStatus();
0567: return false;
0568: }
0569: matcher.usePattern(pattern);
0570: boolean hasNext = false;
0571: // check whether next token matches the specified pattern
0572: if (matcher.matches()) {
0573: cachehasNextIndex = findStartIndex;
0574: matchSuccessful = true;
0575: hasNext = true;
0576: }
0577: recoverPreviousStatus();
0578: return hasNext;
0579: }
0580:
0581: /**
0582: * Returns true if this scanner has one or more tokens remaining to parse
0583: * and the next token matches a pattern compiled from the given string. Will
0584: * block if the data is still being read.
0585: *
0586: * Equivalent to <code>hasNext(Pattern.compile(pattern))</code>.
0587: *
0588: * @param pattern
0589: * the string representation of the pattern to check for
0590: * @return true if this scanner has more tokens and the next token matches
0591: * the pattern
0592: * @throws IllegalStateException
0593: * if the scanner has been closed
0594: */
0595: public boolean hasNext(String pattern) {
0596: return hasNext(Pattern.compile(pattern));
0597: }
0598:
0599: /**
0600: * Returns true if this scanner's next token can be translated into a valid
0601: * BigDecimal.
0602: *
0603: * @return true if the next token in this scanner's input can be translated
0604: * into a valid BigDecimal
0605: * @throws IllegalStateException
0606: * if the scanner has been closed
0607: */
0608: public boolean hasNextBigDecimal() {
0609: Pattern floatPattern = getFloatPattern();
0610: boolean isBigDecimalValue = false;
0611: if (hasNext(floatPattern)) {
0612: String floatString = matcher.group();
0613: floatString = removeLocaleInfoFromFloat(floatString);
0614: try {
0615: cacheHasNextValue = new BigDecimal(floatString);
0616: isBigDecimalValue = true;
0617: } catch (NumberFormatException e) {
0618: matchSuccessful = false;
0619: }
0620: }
0621: return isBigDecimalValue;
0622: }
0623:
0624: /**
0625: * Returns true if this scanner's next token can be translated into a valid
0626: * BigInteger in the default radix.
0627: *
0628: * @return true if the next token in this scanner's input can be translated
0629: * into a valid BigInteger
0630: * @throws IllegalStateException
0631: * if the scanner has been closed
0632: */
0633: public boolean hasNextBigInteger() {
0634: return hasNextBigInteger(integerRadix);
0635: }
0636:
0637: /**
0638: * Returns true if this scanner's next token can be translated into a valid
0639: * BigInteger in the specified radix.
0640: *
0641: * @param radix
0642: * the radix used to translate the token into a BigInteger
0643: * @return true if the next token in this scanner's input can be translated
0644: * into a valid BigInteger
0645: * @throws IllegalStateException
0646: * if the scanner has been closed
0647: */
0648: public boolean hasNextBigInteger(int radix) {
0649: Pattern integerPattern = getIntegerPattern(radix);
0650: boolean isBigIntegerValue = false;
0651: if (hasNext(integerPattern)) {
0652: String intString = matcher.group();
0653: intString = removeLocaleInfo(intString, DataType.INT);
0654: try {
0655: cacheHasNextValue = new BigInteger(intString, radix);
0656: isBigIntegerValue = true;
0657: } catch (NumberFormatException e) {
0658: matchSuccessful = false;
0659: }
0660: }
0661: return isBigIntegerValue;
0662: }
0663:
0664: /**
0665: * Returns true if this scanner's next token can be translated into a valid
0666: * boolean value.
0667: *
0668: * @return true if the next token in this scanner's input can be translated
0669: * into a valid boolean value
0670: * @throws IllegalStateException
0671: * if the scanner has been closed
0672: */
0673: public boolean hasNextBoolean() {
0674: return hasNext(BOOLEAN_PATTERN);
0675: }
0676:
0677: /**
0678: * Returns true if this scanner's next token can be translated into a valid
0679: * byte value in the default radix.
0680: *
0681: * @return true if the next token in this scanner's input can be translated
0682: * into a valid byte value
0683: * @throws IllegalStateException
0684: * if the scanner has been closed
0685: */
0686: public boolean hasNextByte() {
0687: return hasNextByte(integerRadix);
0688: }
0689:
0690: /**
0691: * Returns true if this scanner's next token can be translated into a valid
0692: * byte value in the specified radix.
0693: *
0694: * @param radix
0695: * the radix used to translate the token into a byte value
0696: * @return true if the next token in this scanner's input can be translated
0697: * into a valid byte value
0698: * @throws IllegalStateException
0699: * if the scanner has been closed
0700: */
0701: public boolean hasNextByte(int radix) {
0702: Pattern integerPattern = getIntegerPattern(radix);
0703: boolean isByteValue = false;
0704: if (hasNext(integerPattern)) {
0705: String intString = matcher.group();
0706: intString = removeLocaleInfo(intString, DataType.INT);
0707: try {
0708: cacheHasNextValue = Byte.valueOf(intString, radix);
0709: isByteValue = true;
0710: } catch (NumberFormatException e) {
0711: matchSuccessful = false;
0712: }
0713: }
0714: return isByteValue;
0715: }
0716:
0717: /**
0718: * Returns true if this scanner's next token can be translated into a valid
0719: * double value.
0720: *
0721: * @return true if the next token in this scanner's input can be translated
0722: * into a valid double value
0723: * @throws IllegalStateException
0724: * if the scanner has been closed
0725: */
0726: public boolean hasNextDouble() {
0727: Pattern floatPattern = getFloatPattern();
0728: boolean isDoubleValue = false;
0729: if (hasNext(floatPattern)) {
0730: String floatString = matcher.group();
0731: floatString = removeLocaleInfoFromFloat(floatString);
0732: try {
0733: cacheHasNextValue = Double.valueOf(floatString);
0734: isDoubleValue = true;
0735: } catch (NumberFormatException e) {
0736: matchSuccessful = false;
0737: }
0738: }
0739: return isDoubleValue;
0740: }
0741:
0742: /**
0743: * Returns true if this scanner's next token can be translated into a valid
0744: * float value.
0745: *
0746: * @return true if the next token in this scanner's input can be translated
0747: * into a valid float value
0748: * @throws IllegalStateException
0749: * if the scanner has been closed
0750: */
0751: public boolean hasNextFloat() {
0752: Pattern floatPattern = getFloatPattern();
0753: boolean isFloatValue = false;
0754: if (hasNext(floatPattern)) {
0755: String floatString = matcher.group();
0756: floatString = removeLocaleInfoFromFloat(floatString);
0757: try {
0758: cacheHasNextValue = Float.valueOf(floatString);
0759: isFloatValue = true;
0760: } catch (NumberFormatException e) {
0761: matchSuccessful = false;
0762: }
0763: }
0764: return isFloatValue;
0765: }
0766:
0767: /**
0768: * Returns true if this scanner's next token can be translated into a valid
0769: * int value in the default radix.
0770: *
0771: * @return true if the next token in this scanner's input can be translated
0772: * into a valid int value
0773: * @throws IllegalStateException
0774: * if the scanner has been closed
0775: */
0776: public boolean hasNextInt() {
0777: return hasNextInt(integerRadix);
0778: }
0779:
0780: /**
0781: * Returns true if this scanner's next token can be translated into a valid
0782: * int value in the specified radix.
0783: *
0784: * @param radix
0785: * the radix used to translate the token into an int value
0786: * @return true if the next token in this scanner's input can be translated
0787: * into a valid int value
0788: * @throws IllegalStateException
0789: * if the scanner has been closed
0790: */
0791: public boolean hasNextInt(int radix) {
0792: Pattern integerPattern = getIntegerPattern(radix);
0793: boolean isIntValue = false;
0794: if (hasNext(integerPattern)) {
0795: String intString = matcher.group();
0796: intString = removeLocaleInfo(intString, DataType.INT);
0797: try {
0798: cacheHasNextValue = Integer.valueOf(intString, radix);
0799: isIntValue = true;
0800: } catch (NumberFormatException e) {
0801: matchSuccessful = false;
0802: }
0803: }
0804: return isIntValue;
0805: }
0806:
0807: /**
0808: * Returns true if there is another line in the input. Otherwise, returns
0809: * false. When waiting for input, the scanner may be blocked.
0810: *
0811: * @return true if there is another line in the input. Otherwise, false will
0812: * be returned.
0813: * @throws IllegalStateException
0814: * if the scanner is closed
0815: */
0816: public boolean hasNextLine() {
0817: checkClosed();
0818: matcher.usePattern(LINE_PATTERN);
0819: matcher.region(findStartIndex, bufferLength);
0820:
0821: boolean hasNextLine = false;
0822: while (true) {
0823: if (matcher.find()) {
0824: matchSuccessful = true;
0825: hasNextLine = true;
0826: break;
0827: } else {
0828: if (inputExhausted) {
0829: matchSuccessful = false;
0830: break;
0831: }
0832: }
0833: if (!inputExhausted) {
0834: readMore();
0835: resetMatcher();
0836: }
0837: }
0838: return hasNextLine;
0839: }
0840:
0841: /**
0842: * Returns true if this scanner's next token can be translated into a valid
0843: * long value in the default radix.
0844: *
0845: * @return true if the next token in this scanner's input can be translated
0846: * into a valid long value
0847: * @throws IllegalStateException
0848: * if the scanner has been closed
0849: */
0850: public boolean hasNextLong() {
0851: return hasNextLong(integerRadix);
0852: }
0853:
0854: /**
0855: * Returns true if this scanner's next token can be translated into a valid
0856: * long value in the specified radix.
0857: *
0858: * @param radix
0859: * the radix used to translate the token into a long value
0860: * @return true if the next token in this scanner's input can be translated
0861: * into a valid long value
0862: * @throws IllegalStateException
0863: * if the scanner has been closed
0864: */
0865: public boolean hasNextLong(int radix) {
0866: Pattern integerPattern = getIntegerPattern(radix);
0867: boolean isLongValue = false;
0868: if (hasNext(integerPattern)) {
0869: String intString = matcher.group();
0870: intString = removeLocaleInfo(intString, DataType.INT);
0871: try {
0872: cacheHasNextValue = Long.valueOf(intString, radix);
0873: isLongValue = true;
0874: } catch (NumberFormatException e) {
0875: matchSuccessful = false;
0876: }
0877: }
0878: return isLongValue;
0879: }
0880:
0881: /**
0882: * Returns true if this scanner's next token can be translated into a valid
0883: * short value in the default radix.
0884: *
0885: * @return true if the next token in this scanner's input can be translated
0886: * into a valid short value
0887: * @throws IllegalStateException
0888: * if the scanner has been closed
0889: */
0890: public boolean hasNextShort() {
0891: return hasNextShort(integerRadix);
0892: }
0893:
0894: /**
0895: * Returns true if this scanner's next token can be translated into a valid
0896: * short value in the specified radix.
0897: *
0898: * @param radix
0899: * the radix used to translate the token into a short value
0900: * @return true if the next token in this scanner's input can be translated
0901: * into a valid short value
0902: * @throws IllegalStateException
0903: * if the scanner has been closed
0904: */
0905: public boolean hasNextShort(int radix) {
0906: Pattern integerPattern = getIntegerPattern(radix);
0907: boolean isShortValue = false;
0908: if (hasNext(integerPattern)) {
0909: String intString = matcher.group();
0910: intString = removeLocaleInfo(intString, DataType.INT);
0911: try {
0912: cacheHasNextValue = Short.valueOf(intString, radix);
0913: isShortValue = true;
0914: } catch (NumberFormatException e) {
0915: matchSuccessful = false;
0916: }
0917: }
0918: return isShortValue;
0919: }
0920:
0921: /**
0922: * Returns the last IOException thrown when reading the underlying input. If
0923: * no exception is thrown, return null.
0924: *
0925: * @return the last IOException thrown
0926: */
0927: public IOException ioException() {
0928: return lastIOException;
0929: }
0930:
0931: /**
0932: * Return the locale of this scanner.
0933: *
0934: * @return the locale of this scanner
0935: */
0936: public Locale locale() {
0937: return locale;
0938: }
0939:
0940: /**
0941: * Returns the match result of this scanner's last match operation.This
0942: * method throws IllegalStateException if no match operation has been
0943: * performed, or if the last match was unsuccessful.
0944: *
0945: * The various nextXXX methods of Scanner provide a match result if they do
0946: * not complete with throwing an exception. For example, after an invocation
0947: * of the nextBoolean() method which returned a boolean value, this method
0948: * returns a match result for the search of the Boolean regular expression
0949: * defined above. In the same way,the findInLine(java.lang.String),
0950: * findWithinHorizon(java.lang.String, int), and
0951: * skip(java.util.regex.Pattern) methods will provide a match result if they
0952: * are successful.
0953: *
0954: * @return the match result of the last match operation
0955: * @throws IllegalStateException
0956: * if the match result is not available
0957: */
0958: public MatchResult match() {
0959: if (!matchSuccessful) {
0960: throw new IllegalStateException();
0961: }
0962: return matcher.toMatchResult();
0963: }
0964:
0965: /**
0966: * Returns the next token. The token will be both prefixed and postfixed by
0967: * the delimiter that is currently being used (or a string that matches the
0968: * delimiter pattern). Will block if input is being read.
0969: *
0970: * @return the next token
0971: * @throws IllegalStateException
0972: * if this scanner has been closed
0973: * @throws NoSuchElementException
0974: * if input has been exhausted
0975: */
0976: public String next() {
0977: return next(ANY_PATTERN);
0978: }
0979:
0980: /**
0981: * Returns the next token if it matches the specified pattern. The token
0982: * will be both prefixed and postfixed by the delimiter that is currently
0983: * being used (or a string that matches the delimiter pattern). Will block
0984: * if input is being read.
0985: *
0986: * @param pattern
0987: * the pattern to check for
0988: * @return the next token
0989: * @throws IllegalStateException
0990: * if this scanner has been closed
0991: * @throws NoSuchElementException
0992: * if input has been exhausted
0993: * @throws InputMismatchException
0994: * if the next token does not match the pattern given
0995: */
0996: public String next(Pattern pattern) {
0997: checkClosed();
0998: checkNull(pattern);
0999: matchSuccessful = false;
1000: saveCurrentStatus();
1001: if (!setTokenRegion()) {
1002: recoverPreviousStatus();
1003: // if setting match region fails
1004: throw new NoSuchElementException();
1005: }
1006: matcher.usePattern(pattern);
1007: if (!matcher.matches()) {
1008: recoverPreviousStatus();
1009: throw new InputMismatchException();
1010:
1011: }
1012: matchSuccessful = true;
1013: return matcher.group();
1014: }
1015:
1016: /**
1017: * Returns the next token if it matches the specified pattern. The token
1018: * will be both prefixed and postfixed by the delimiter that is currently
1019: * being used (or a string that matches the delimiter pattern). Will block
1020: * if input is being read.
1021: *
1022: * Equivalent to <code>next(Pattern.compile(pattern))</code>.
1023: *
1024: * @param pattern
1025: * the string representation of the pattern to check for
1026: * @return the next token
1027: * @throws IllegalStateException
1028: * if this scanner has been closed
1029: * @throws NoSuchElementException
1030: * if input has been exhausted
1031: * @throws InputMismatchException
1032: * if the next token does not match the pattern given
1033: */
1034: public String next(String pattern) {
1035: return next(Pattern.compile(pattern));
1036: }
1037:
1038: /**
1039: * Returns the next token as a BigDecimal. Will block if input is being
1040: * read.
1041: *
1042: * If the next token can be translated into a BigDecimal the following is
1043: * done: All locale specific prefixes, group separators, and locale specific
1044: * suffixes are removed. Then non-ASCII digits are mapped into ASCII digits
1045: * via {@link Character#digit(char, int)}, a negative sign (-) is added if
1046: * the locale specific negative prefix or suffix was present. Finally the
1047: * resulting String is passed to {@link BigDecimal#BigDecimal(String)}}.
1048: *
1049: * @return the next token as a BigDecimal
1050: * @throws IllegalStateException
1051: * if this scanner has been closed
1052: * @throws NoSuchElementException
1053: * if input has been exhausted
1054: * @throws InputMismatchException
1055: * if the next token can not be translated into a valid
1056: * BigDecimal
1057: */
1058: public BigDecimal nextBigDecimal() {
1059: checkClosed();
1060: Object obj = cacheHasNextValue;
1061: cacheHasNextValue = null;
1062: if (obj instanceof BigDecimal) {
1063: findStartIndex = cachehasNextIndex;
1064: return (BigDecimal) obj;
1065: }
1066: Pattern floatPattern = getFloatPattern();
1067: String floatString = next(floatPattern);
1068: floatString = removeLocaleInfoFromFloat(floatString);
1069: BigDecimal bigDecimalValue;
1070: try {
1071: bigDecimalValue = new BigDecimal(floatString);
1072: } catch (NumberFormatException e) {
1073: matchSuccessful = false;
1074: recoverPreviousStatus();
1075: throw new InputMismatchException();
1076: }
1077: return bigDecimalValue;
1078: }
1079:
1080: /**
1081: * Returns the next token as a BigInteger. Will block if input is being
1082: * read.
1083: *
1084: * Equivalent to <code>nextBigInteger(DEFAULT_RADIX)</code>
1085: *
1086: * @return the next token as a BigInteger
1087: * @throws IllegalStateException
1088: * if this scanner has been closed
1089: * @throws NoSuchElementException
1090: * if input has been exhausted
1091: * @throws InputMismatchException
1092: * if the next token can not be translated into a valid
1093: * BigInteger
1094: */
1095: public BigInteger nextBigInteger() {
1096: return nextBigInteger(integerRadix);
1097: }
1098:
1099: /**
1100: * Returns the next token as a BigInteger with the specified radix. Will
1101: * block if input is being read.
1102: *
1103: * If the next token can be translated into a BigInteger the following is
1104: * done: All locale specific prefixes, group separators, and locale specific
1105: * suffixes are removed. Then non-ASCII digits are mapped into ASCII digits
1106: * via {@link Character#digit(char, int)}, a negative sign (-) is added if
1107: * the locale specific negative prefix or suffix was present. Finally the
1108: * resulting String is passed to {@link BigInteger#BigInteger(String, int)}}
1109: * with the specified radix.
1110: *
1111: * @param radix
1112: * the radix used to translate the token into a BigInteger
1113: * @return the next token as a BigInteger
1114: * @throws IllegalStateException
1115: * if this scanner has been closed
1116: * @throws NoSuchElementException
1117: * if input has been exhausted
1118: * @throws InputMismatchException
1119: * if the next token can not be translated into a valid
1120: * BigInteger
1121: */
1122: public BigInteger nextBigInteger(int radix) {
1123: checkClosed();
1124: Object obj = cacheHasNextValue;
1125: cacheHasNextValue = null;
1126: if (obj instanceof BigInteger) {
1127: findStartIndex = cachehasNextIndex;
1128: return (BigInteger) obj;
1129: }
1130: Pattern integerPattern = getIntegerPattern(radix);
1131: String intString = next(integerPattern);
1132: intString = removeLocaleInfo(intString, DataType.INT);
1133: BigInteger bigIntegerValue;
1134: try {
1135: bigIntegerValue = new BigInteger(intString, radix);
1136: } catch (NumberFormatException e) {
1137: matchSuccessful = false;
1138: recoverPreviousStatus();
1139: throw new InputMismatchException();
1140: }
1141: return bigIntegerValue;
1142: }
1143:
1144: /**
1145: * Returns the next token as a boolean. Will block if input is being read.
1146: *
1147: * @return the next token as a boolean
1148: * @throws IllegalStateException
1149: * if this scanner has been closed
1150: * @throws NoSuchElementException
1151: * if input has been exhausted
1152: * @throws InputMismatchException
1153: * if the next token can not be translated into a valid boolean
1154: * value
1155: */
1156: public boolean nextBoolean() {
1157: return Boolean.parseBoolean(next(BOOLEAN_PATTERN));
1158: }
1159:
1160: /**
1161: * Returns the next token as a byte. Will block if input is being read.
1162: *
1163: * Equivalent to <code>nextByte(DEFAULT_RADIX)</code>
1164: *
1165: * @return the next token as a byte
1166: * @throws IllegalStateException
1167: * if this scanner has been closed
1168: * @throws NoSuchElementException
1169: * if input has been exhausted
1170: * @throws InputMismatchException
1171: * if the next token can not be translated into a valid byte
1172: * value
1173: */
1174: public byte nextByte() {
1175: return nextByte(integerRadix);
1176: }
1177:
1178: /**
1179: * Returns the next token as a byte with the specified radix. Will block if
1180: * input is being read.
1181: *
1182: * If the next token can be translated into a byte the following is done:
1183: * All locale specific prefixes, group separators, and locale specific
1184: * suffixes are removed. Then non-ASCII digits are mapped into ASCII digits
1185: * via {@link Character#digit(char, int)}, a negative sign (-) is added if
1186: * the locale specific negative prefix or suffix was present. Finally the
1187: * resulting String is passed to {@link Byte#parseByte(String, int)}} with
1188: * the specified radix.
1189: *
1190: * @param radix
1191: * the radix used to translate the token into byte value
1192: * @return the next token as a byte
1193: * @throws IllegalStateException
1194: * if this scanner has been closed
1195: * @throws NoSuchElementException
1196: * if input has been exhausted
1197: * @throws InputMismatchException
1198: * if the next token can not be translated into a valid byte
1199: * value
1200: */
1201: @SuppressWarnings("boxing")
1202: public byte nextByte(int radix) {
1203: checkClosed();
1204: Object obj = cacheHasNextValue;
1205: cacheHasNextValue = null;
1206: if (obj instanceof Byte) {
1207: findStartIndex = cachehasNextIndex;
1208: return (Byte) obj;
1209: }
1210: Pattern integerPattern = getIntegerPattern(radix);
1211: String intString = next(integerPattern);
1212: intString = removeLocaleInfo(intString, DataType.INT);
1213: byte byteValue = 0;
1214: try {
1215: byteValue = Byte.parseByte(intString, radix);
1216: } catch (NumberFormatException e) {
1217: matchSuccessful = false;
1218: recoverPreviousStatus();
1219: throw new InputMismatchException();
1220: }
1221: return byteValue;
1222: }
1223:
1224: /**
1225: * Returns the next token as a double. Will block if input is being read.
1226: *
1227: * If the next token can be translated into a double the following is done:
1228: * All locale specific prefixes, group separators, and locale specific
1229: * suffixes are removed. Then non-ASCII digits are mapped into ASCII digits
1230: * via {@link Character#digit(char, int)}, a negative sign (-) is added if
1231: * the locale specific negative prefix or suffix was present. Finally the
1232: * resulting String is passed to {@link Double#parseDouble(String)}}. If
1233: * the token matches the localized NaN or infinity strings, it is also
1234: * passed to {@link Double#parseDouble(String)}}.
1235: *
1236: * @return the next token as a double
1237: * @throws IllegalStateException
1238: * if this scanner has been closed
1239: * @throws NoSuchElementException
1240: * if input has been exhausted
1241: * @throws InputMismatchException
1242: * if the next token can not be translated into a valid double
1243: * value
1244: */
1245: @SuppressWarnings("boxing")
1246: public double nextDouble() {
1247: checkClosed();
1248: Object obj = cacheHasNextValue;
1249: cacheHasNextValue = null;
1250: if (obj instanceof Double) {
1251: findStartIndex = cachehasNextIndex;
1252: return (Double) obj;
1253: }
1254: Pattern floatPattern = getFloatPattern();
1255: String floatString = next(floatPattern);
1256: floatString = removeLocaleInfoFromFloat(floatString);
1257: double doubleValue = 0;
1258: try {
1259: doubleValue = Double.parseDouble(floatString);
1260: } catch (NumberFormatException e) {
1261: matchSuccessful = false;
1262: recoverPreviousStatus();
1263: throw new InputMismatchException();
1264: }
1265: return doubleValue;
1266: }
1267:
1268: /**
1269: * Returns the next token as a float. Will block if input is being read.
1270: *
1271: * If the next token can be translated into a float the following is done:
1272: * All locale specific prefixes, group separators, and locale specific
1273: * suffixes are removed. Then non-ASCII digits are mapped into ASCII digits
1274: * via {@link Character#digit(char, int)}, a negative sign (-) is added if
1275: * the locale specific negative prefix or suffix was present. Finally the
1276: * resulting String is passed to {@link Float#parseFloat(String)}}.If the
1277: * token matches the localized NaN or infinity strings, it is also passed to
1278: * {@link Float#parseFloat(String)}}.
1279: *
1280: * @return the next token as a float
1281: * @throws IllegalStateException
1282: * if this scanner has been closed
1283: * @throws NoSuchElementException
1284: * if input has been exhausted
1285: * @throws InputMismatchException
1286: * if the next token can not be translated into a valid float
1287: * value
1288: */
1289: @SuppressWarnings("boxing")
1290: public float nextFloat() {
1291: checkClosed();
1292: Object obj = cacheHasNextValue;
1293: cacheHasNextValue = null;
1294: if (obj instanceof Float) {
1295: findStartIndex = cachehasNextIndex;
1296: return (Float) obj;
1297: }
1298: Pattern floatPattern = getFloatPattern();
1299: String floatString = next(floatPattern);
1300: floatString = removeLocaleInfoFromFloat(floatString);
1301: float floatValue = 0;
1302: try {
1303: floatValue = Float.parseFloat(floatString);
1304: } catch (NumberFormatException e) {
1305: matchSuccessful = false;
1306: recoverPreviousStatus();
1307: throw new InputMismatchException();
1308: }
1309: return floatValue;
1310: }
1311:
1312: /**
1313: * Returns the next token as an int. Will block if input is being read.
1314: *
1315: * Equivalent to <code>nextInt(DEFAULT_RADIX)</code>.
1316: *
1317: * @return the next token as an int
1318: * @throws IllegalStateException
1319: * if this scanner has been closed
1320: * @throws NoSuchElementException
1321: * if input has been exhausted
1322: * @throws InputMismatchException
1323: * if the next token can not be translated into a valid int
1324: * value
1325: */
1326: public int nextInt() {
1327: return nextInt(integerRadix);
1328: }
1329:
1330: /**
1331: * Returns the next token as an int with the specified radix. Will block if
1332: * input is being read.
1333: *
1334: * If the next token can be translated into an int the following is done:
1335: * All locale specific prefixes, group separators, and locale specific
1336: * suffixes are removed. Then non-ASCII digits are mapped into ASCII digits
1337: * via {@link Character#digit(char, int)}, a negative sign (-) is added if
1338: * the locale specific negative prefix or suffix was present. Finally the
1339: * resulting String is passed to {@link Integer#parseInt(String, int)} with
1340: * the specified radix.
1341: *
1342: * @param radix
1343: * the radix used to translate the token into an int value
1344: * @return the next token as an int
1345: * @throws IllegalStateException
1346: * if this scanner has been closed
1347: * @throws NoSuchElementException
1348: * if input has been exhausted
1349: * @throws InputMismatchException
1350: * if the next token can not be translated into a valid int
1351: * value
1352: */
1353: @SuppressWarnings("boxing")
1354: public int nextInt(int radix) {
1355: checkClosed();
1356: Object obj = cacheHasNextValue;
1357: cacheHasNextValue = null;
1358: if (obj instanceof Integer) {
1359: findStartIndex = cachehasNextIndex;
1360: return (Integer) obj;
1361: }
1362: Pattern integerPattern = getIntegerPattern(radix);
1363: String intString = next(integerPattern);
1364: intString = removeLocaleInfo(intString, DataType.INT);
1365: int intValue = 0;
1366: try {
1367: intValue = Integer.parseInt(intString, radix);
1368: } catch (NumberFormatException e) {
1369: matchSuccessful = false;
1370: recoverPreviousStatus();
1371: throw new InputMismatchException();
1372: }
1373: return intValue;
1374: }
1375:
1376: /**
1377: * Returns the skipped input and advances the scanner to the beginning of
1378: * the next line. The returned result will exclude any line terminator.
1379: *
1380: * When searching, if no line terminator is found, then a large amount of
1381: * input will be cached. If no line at all can be found, a
1382: * NoSuchElementException will be thrown out.
1383: *
1384: * @return the skipped line
1385: * @throws IllegalStateException
1386: * if the scanner is closed
1387: * @throws NoSuchElementException
1388: * if no line can be found, e.g. when input is an empty string
1389: */
1390: public String nextLine() {
1391: checkClosed();
1392:
1393: matcher.usePattern(LINE_PATTERN);
1394: matcher.region(findStartIndex, bufferLength);
1395: String result = null;
1396: while (true) {
1397: if (matcher.find()) {
1398: matchSuccessful = true;
1399: findStartIndex = matcher.end();
1400: result = matcher.group();
1401: break;
1402: } else {
1403: if (inputExhausted) {
1404: matchSuccessful = false;
1405: throw new NoSuchElementException();
1406: }
1407: }
1408: if (!inputExhausted) {
1409: readMore();
1410: resetMatcher();
1411: }
1412: }
1413: // Find text without line terminator here.
1414: if (null != result) {
1415: Matcher terminatorMatcher = LINE_TERMINATOR.matcher(result);
1416: if (terminatorMatcher.find()) {
1417: result = result.substring(0, terminatorMatcher.start());
1418: }
1419: }
1420: return result;
1421: }
1422:
1423: /**
1424: * Returns the next token as a long. Will block if input is being read.
1425: *
1426: * Equivalent to <code>nextLong(DEFAULT_RADIX)</code>.
1427: *
1428: * @return the next token as a long
1429: * @throws IllegalStateException
1430: * if this scanner has been closed
1431: * @throws NoSuchElementException
1432: * if input has been exhausted
1433: * @throws InputMismatchException
1434: * if the next token can not be translated into a valid long
1435: * value
1436: */
1437: public long nextLong() {
1438: return nextLong(integerRadix);
1439: }
1440:
1441: /**
1442: * Returns the next token as a long with the specified radix. Will block if
1443: * input is being read.
1444: *
1445: * If the next token can be translated into a long the following is done:
1446: * All locale specific prefixes, group separators, and locale specific
1447: * suffixes are removed. Then non-ASCII digits are mapped into ASCII digits
1448: * via {@link Character#digit(char, int)}, a negative sign (-) is added if
1449: * the locale specific negative prefix or suffix was present. Finally the
1450: * resulting String is passed to {@link Long#parseLong(String, int)}} with
1451: * the specified radix.
1452: *
1453: * @param radix
1454: * the radix used to translate the token into a long value
1455: * @return the long value scanned from the input
1456: * @throws IllegalStateException
1457: * if this scanner has been closed
1458: * @throws NoSuchElementException
1459: * if input has been exhausted
1460: * @throws InputMismatchException
1461: * if the next token can not be translated into a valid long
1462: * value
1463: */
1464: @SuppressWarnings("boxing")
1465: public long nextLong(int radix) {
1466: checkClosed();
1467: Object obj = cacheHasNextValue;
1468: cacheHasNextValue = null;
1469: if (obj instanceof Long) {
1470: findStartIndex = cachehasNextIndex;
1471: return (Long) obj;
1472: }
1473: Pattern integerPattern = getIntegerPattern(radix);
1474: String intString = next(integerPattern);
1475: intString = removeLocaleInfo(intString, DataType.INT);
1476: long longValue = 0;
1477: try {
1478: longValue = Long.parseLong(intString, radix);
1479: } catch (NumberFormatException e) {
1480: matchSuccessful = false;
1481: recoverPreviousStatus();
1482: throw new InputMismatchException();
1483: }
1484: return longValue;
1485: }
1486:
1487: /**
1488: * Returns the next token as a short. Will block if input is being read.
1489: *
1490: * Equivalent to <code>nextShort(DEFAULT_RADIX)</code>.
1491: *
1492: * @return the next token as a short
1493: * @throws IllegalStateException
1494: * if this scanner has been closed
1495: * @throws NoSuchElementException
1496: * if input has been exhausted
1497: * @throws InputMismatchException
1498: * if the next token can not be translated into a valid short
1499: * value
1500: */
1501: public short nextShort() {
1502: return nextShort(integerRadix);
1503: }
1504:
1505: /**
1506: * Returns the next token as a short with the specified radix. Will block if
1507: * input is being read.
1508: *
1509: * If the next token can be translated into a short the following is done:
1510: * All locale specific prefixes, group separators, and locale specific
1511: * suffixes are removed. Then non-ASCII digits are mapped into ASCII digits
1512: * via {@link Character#digit(char, int)}, a negative sign (-) is added if
1513: * the locale specific negative prefix or suffix was present. Finally the
1514: * resulting String is passed to {@link Short#parseShort(String, int)}}
1515: * with the specified radix.
1516: *
1517: * @param radix
1518: * the radix used to translate the token into short value
1519: * @return the short value scanned from the input
1520: * @throws IllegalStateException
1521: * if this scanner has been closed
1522: * @throws NoSuchElementException
1523: * if input has been exhausted
1524: * @throws InputMismatchException
1525: * if the next token can not be translated into a valid short
1526: * value
1527: */
1528: @SuppressWarnings("boxing")
1529: public short nextShort(int radix) {
1530: checkClosed();
1531: Object obj = cacheHasNextValue;
1532: cacheHasNextValue = null;
1533: if (obj instanceof Short) {
1534: findStartIndex = cachehasNextIndex;
1535: return (Short) obj;
1536: }
1537: Pattern integerPattern = getIntegerPattern(radix);
1538: String intString = next(integerPattern);
1539: intString = removeLocaleInfo(intString, DataType.INT);
1540: short shortValue = 0;
1541: try {
1542: shortValue = Short.parseShort(intString, radix);
1543: } catch (NumberFormatException e) {
1544: matchSuccessful = false;
1545: recoverPreviousStatus();
1546: throw new InputMismatchException();
1547: }
1548: return shortValue;
1549: }
1550:
1551: /**
1552: * Return the radix of this scanner.
1553: *
1554: * @return the radix of this scanner
1555: */
1556: public int radix() {
1557: return integerRadix;
1558: }
1559:
1560: /**
1561: * Tries to use specified pattern to match input from the current position.
1562: * The delimiter will be ignored. If matches, the matched input will be
1563: * skipped. If an anchored match of the specified pattern succeeds, input
1564: * will also be skipped. Otherwise, a NoSuchElementException will be thrown
1565: * out.
1566: *
1567: * Patterns that can match a lot of input may cause the scanner to read in a
1568: * large amount of input.
1569: *
1570: * Uses a pattern that matches nothing( sc.skip(Pattern.compile("[ \t]*")) )
1571: * will suppress NoSuchElementException.
1572: *
1573: * @param pattern
1574: * used to skip over input
1575: * @return the scanner itself
1576: * @throws IllegalStateException
1577: * if the scanner is closed
1578: * @throws NoSuchElementException
1579: * if the specified pattern match fails
1580: */
1581: public Scanner skip(Pattern pattern) {
1582: checkClosed();
1583: checkNull(pattern);
1584: matcher.usePattern(pattern);
1585: matcher.region(findStartIndex, bufferLength);
1586: while (true) {
1587: if (matcher.lookingAt()) {
1588: boolean matchInBuffer = matcher.end() < bufferLength
1589: || (matcher.end() == bufferLength && inputExhausted);
1590: if (matchInBuffer) {
1591: matchSuccessful = true;
1592: findStartIndex = matcher.end();
1593: break;
1594: }
1595: } else {
1596: if (inputExhausted) {
1597: matchSuccessful = false;
1598: throw new NoSuchElementException();
1599: }
1600: }
1601: if (!inputExhausted) {
1602: readMore();
1603: resetMatcher();
1604: }
1605: }
1606: return this ;
1607: }
1608:
1609: /**
1610: * Tries to use the specified string to construct a pattern. And then uses
1611: * the constructed pattern to match input from the current position. The
1612: * delimiter will be ignored.
1613: *
1614: * It is the same as invoke skip(Pattern.compile(pattern))
1615: *
1616: * @param pattern
1617: * the string used to construct a pattern which in turn used to
1618: * match input
1619: * @return the matched input
1620: * @throws IllegalStateException
1621: * if the scanner is closed
1622: */
1623: public Scanner skip(String pattern) {
1624: return skip(Pattern.compile(pattern));
1625: }
1626:
1627: /**
1628: * Returns a string. The string is used to represent this scanner. Contained
1629: * information may be helpful for debugging. The format of the string is
1630: * unspecified.
1631: *
1632: * @return a string to represent this scanner
1633: */
1634: @Override
1635: public String toString() {
1636: StringBuilder stringBuilder = new StringBuilder();
1637: stringBuilder.append(this .getClass()).append(": ") //$NON-NLS-1$
1638: .append("{(delimiter:") //$NON-NLS-1$
1639: .append(delimiter).append(")(findStartIndex=") //$NON-NLS-1$
1640: .append(findStartIndex).append(")(match succeed=") //$NON-NLS-1$
1641: .append(matchSuccessful).append(")(closed=") //$NON-NLS-1$
1642: .append(closed).append(")}"); //$NON-NLS-1$
1643: return stringBuilder.toString();
1644: }
1645:
1646: /**
1647: * Set the delimiting pattern of this scanner
1648: *
1649: * @param pattern
1650: * the delimiting pattern to use
1651: * @return this scanner
1652: */
1653: public Scanner useDelimiter(Pattern pattern) {
1654: delimiter = pattern;
1655: return this ;
1656: }
1657:
1658: /**
1659: * Set the delimiting pattern of this scanner with a pattern compiled from
1660: * the supplied string value
1661: *
1662: * @param pattern
1663: * a string from which a <code>Pattern</code> can be compiled
1664: * @return this scanner
1665: */
1666: public Scanner useDelimiter(String pattern) {
1667: return useDelimiter(Pattern.compile(pattern));
1668: }
1669:
1670: /**
1671: *
1672: * Set the locale of this scanner to a specified locale.
1673: *
1674: * @param l
1675: * the specified locale to use
1676: * @return this scanner
1677: */
1678: public Scanner useLocale(Locale l) {
1679: if (null == l) {
1680: throw new NullPointerException();
1681: }
1682: this .locale = l;
1683: return this ;
1684: }
1685:
1686: /**
1687: *
1688: * Set the radix of this scanner to the specified radix.
1689: *
1690: * @param radix
1691: * the specified radix to use
1692: * @return this scanner
1693: */
1694: public Scanner useRadix(int radix) {
1695: if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
1696: throw new IllegalArgumentException(
1697: org.apache.harmony.luni.util.Msg.getString(
1698: "KA008", radix)); //$NON-NLS-1$
1699: }
1700: this .integerRadix = radix;
1701: return this ;
1702: }
1703:
1704: /**
1705: * Remove is not a supported operation on Scanner.
1706: *
1707: * @throws UnsupportedOperationException if this method is invoked
1708: */
1709: public void remove() {
1710: throw new UnsupportedOperationException();
1711: }
1712:
1713: /*
1714: * Initialize some components.
1715: */
1716: private void initialization() {
1717: buffer = CharBuffer.allocate(DEFAULT_TRUNK_SIZE);
1718: buffer.limit(0);
1719: matcher = delimiter.matcher(buffer);
1720: }
1721:
1722: /*
1723: * Check the scanner's state, if it is closed, IllegalStateException will be
1724: * thrown.
1725: */
1726: private void checkClosed() {
1727: if (closed) {
1728: throw new IllegalStateException();
1729: }
1730: }
1731:
1732: /*
1733: * Check the given pattern. If it is null, then a NullPointerException will
1734: * be thrown.
1735: */
1736: private void checkNull(Pattern pattern) {
1737: if (null == pattern) {
1738: throw new NullPointerException();
1739: }
1740: }
1741:
1742: /*
1743: * Change the matcher's string after reading input
1744: */
1745: private void resetMatcher() {
1746: if (null == matcher) {
1747: matcher = delimiter.matcher(buffer);
1748: } else {
1749: matcher.reset(buffer);
1750: }
1751: matcher.region(findStartIndex, bufferLength);
1752: }
1753:
1754: /*
1755: * Save the matcher's last find position
1756: */
1757: private void saveCurrentStatus() {
1758: preStartIndex = findStartIndex;
1759: }
1760:
1761: /*
1762: * Change the matcher's status to last find position
1763: */
1764: private void recoverPreviousStatus() {
1765: findStartIndex = preStartIndex;
1766: }
1767:
1768: /*
1769: * Get integer's pattern
1770: */
1771: private Pattern getIntegerPattern(int radix) {
1772: if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
1773: throw new IllegalArgumentException(
1774: org.apache.harmony.luni.util.Msg.getString(
1775: "KA00e", radix)); //$NON-NLS-1$
1776: }
1777: decimalFormat = (DecimalFormat) NumberFormat
1778: .getInstance(locale);
1779:
1780: String allAvailableDigits = "0123456789abcdefghijklmnopqrstuvwxyz"; //$NON-NLS-1$
1781: String ASCIIDigit = allAvailableDigits.substring(0, radix);
1782: String nonZeroASCIIDigit = allAvailableDigits.substring(1,
1783: radix);
1784:
1785: StringBuilder digit = new StringBuilder("((?i)[").append(ASCIIDigit) //$NON-NLS-1$
1786: .append("]|\\p{javaDigit})"); //$NON-NLS-1$
1787: StringBuilder nonZeroDigit = new StringBuilder("((?i)[").append( //$NON-NLS-1$
1788: nonZeroASCIIDigit).append(
1789: "]|([\\p{javaDigit}&&[^0]]))"); //$NON-NLS-1$
1790: StringBuilder numeral = getNumeral(digit, nonZeroDigit);
1791:
1792: StringBuilder integer = new StringBuilder("(([-+]?(").append(numeral) //$NON-NLS-1$
1793: .append(")))|(").append(addPositiveSign(numeral)).append(")|(") //$NON-NLS-1$ //$NON-NLS-2$
1794: .append(addNegativeSign(numeral)).append(")"); //$NON-NLS-1$
1795:
1796: Pattern integerPattern = Pattern.compile(integer.toString());
1797: return integerPattern;
1798: }
1799:
1800: /*
1801: * Get pattern of float
1802: */
1803: private Pattern getFloatPattern() {
1804: decimalFormat = (DecimalFormat) NumberFormat
1805: .getInstance(locale);
1806:
1807: StringBuilder digit = new StringBuilder(
1808: "([0-9]|(\\p{javaDigit}))"); //$NON-NLS-1$
1809: StringBuilder nonZeroDigit = new StringBuilder(
1810: "[\\p{javaDigit}&&[^0]]"); //$NON-NLS-1$
1811: StringBuilder numeral = getNumeral(digit, nonZeroDigit);
1812:
1813: String decimalSeparator = "\\" + decimalFormat.getDecimalFormatSymbols()//$NON-NLS-1$
1814: .getDecimalSeparator();
1815: StringBuilder decimalNumeral = new StringBuilder("(").append(numeral) //$NON-NLS-1$
1816: .append("|").append(numeral) //$NON-NLS-1$
1817: .append(decimalSeparator).append(digit)
1818: .append("*+|").append( //$NON-NLS-1$
1819: decimalSeparator).append(digit).append("++)"); //$NON-NLS-1$
1820: StringBuilder exponent = new StringBuilder("([eE][+-]?").append(digit) //$NON-NLS-1$
1821: .append("+)?"); //$NON-NLS-1$
1822:
1823: StringBuilder decimal = new StringBuilder("(([-+]?").append( //$NON-NLS-1$
1824: decimalNumeral)
1825: .append("(").append(exponent).append("?)") //$NON-NLS-1$ //$NON-NLS-2$
1826: .append(")|(").append(addPositiveSign(decimalNumeral)).append( //$NON-NLS-1$
1827: "(")
1828: .append(exponent)
1829: .append("?)").append(")|(") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1830: .append(addNegativeSign(decimalNumeral))
1831: .append("(").append( //$NON-NLS-1$
1832: exponent).append("?)").append("))"); //$NON-NLS-1$ //$NON-NLS-2$
1833:
1834: StringBuilder hexFloat = new StringBuilder(
1835: "([-+]?0[xX][0-9a-fA-F]*") //$NON-NLS-1$
1836: .append("\\.").append(//$NON-NLS-1$
1837: "[0-9a-fA-F]+([pP][-+]?[0-9]+)?)"); //$NON-NLS-1$
1838: String localNaN = decimalFormat.getDecimalFormatSymbols()
1839: .getNaN();
1840: String localeInfinity = decimalFormat.getDecimalFormatSymbols()
1841: .getInfinity();
1842: StringBuilder nonNumber = new StringBuilder("(NaN|\\Q").append(localNaN) //$NON-NLS-1$
1843: .append("\\E|Infinity|\\Q").append(localeInfinity).append("\\E)"); //$NON-NLS-1$ //$NON-NLS-2$
1844: StringBuilder singedNonNumber = new StringBuilder("((([-+]?(").append( //$NON-NLS-1$
1845: nonNumber)
1846: .append(")))|(").append(addPositiveSign(nonNumber)) //$NON-NLS-1$
1847: .append(")|(").append(addNegativeSign(nonNumber)).append("))"); //$NON-NLS-1$ //$NON-NLS-2$
1848:
1849: StringBuilder floatString = new StringBuilder()
1850: .append(decimal)
1851: .append("|").append(hexFloat).append("|").append(singedNonNumber); //$NON-NLS-1$ //$NON-NLS-2$
1852: Pattern floatPattern = Pattern.compile(floatString.toString());
1853: return floatPattern;
1854: }
1855:
1856: private StringBuilder getNumeral(StringBuilder digit,
1857: StringBuilder nonZeroDigit) {
1858: String groupSeparator = "\\"//$NON-NLS-1$
1859: + decimalFormat.getDecimalFormatSymbols()
1860: .getGroupingSeparator();
1861: StringBuilder groupedNumeral = new StringBuilder("(").append( //$NON-NLS-1$
1862: nonZeroDigit).append(digit)
1863: .append("?").append(digit).append( //$NON-NLS-1$
1864: "?(").append(groupSeparator).append(digit)
1865: .append(digit) //$NON-NLS-1$
1866: .append(digit).append(")+)"); //$NON-NLS-1$
1867: StringBuilder numeral = new StringBuilder("((").append(digit).append( //$NON-NLS-1$
1868: "++)|").append(groupedNumeral).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
1869: return numeral;
1870: }
1871:
1872: /*
1873: * Add the locale specific positive prefixes and suffixes to the pattern
1874: */
1875: private StringBuilder addPositiveSign(StringBuilder unSignNumeral) {
1876: String positivePrefix = ""; //$NON-NLS-1$
1877: String positiveSuffix = ""; //$NON-NLS-1$
1878: if (!decimalFormat.getPositivePrefix().equals("")) { //$NON-NLS-1$
1879: positivePrefix = "\\Q" + decimalFormat.getPositivePrefix() + "\\E"; //$NON-NLS-1$ //$NON-NLS-2$
1880: }
1881: if (!decimalFormat.getPositiveSuffix().equals("")) { //$NON-NLS-1$
1882: positiveSuffix = "\\Q" + decimalFormat.getPositiveSuffix() + "\\E"; //$NON-NLS-1$ //$NON-NLS-2$
1883: }
1884: StringBuilder signedNumeral = new StringBuilder().append(
1885: positivePrefix).append(unSignNumeral).append(
1886: positiveSuffix);
1887: return signedNumeral;
1888: }
1889:
1890: /*
1891: * Add the locale specific negative prefixes and suffixes to the pattern
1892: */
1893: private StringBuilder addNegativeSign(StringBuilder unSignNumeral) {
1894: String negativePrefix = ""; //$NON-NLS-1$
1895: String negativeSuffix = ""; //$NON-NLS-1$
1896: if (!decimalFormat.getNegativePrefix().equals("")) { //$NON-NLS-1$
1897: negativePrefix = "\\Q" + decimalFormat.getNegativePrefix() + "\\E"; //$NON-NLS-1$//$NON-NLS-2$
1898: }
1899: if (!decimalFormat.getNegativeSuffix().equals("")) { //$NON-NLS-1$
1900: negativeSuffix = "\\Q" + decimalFormat.getNegativeSuffix() + "\\E"; //$NON-NLS-1$//$NON-NLS-2$
1901: }
1902: StringBuilder signedNumeral = new StringBuilder().append(
1903: negativePrefix).append(unSignNumeral).append(
1904: negativeSuffix);
1905: return signedNumeral;
1906: }
1907:
1908: /*
1909: * Remove locale related information from float String
1910: */
1911: private String removeLocaleInfoFromFloat(String floatString) {
1912: // If the token is HexFloat
1913: if (-1 != floatString.indexOf('x')
1914: || -1 != floatString.indexOf('X')) {
1915: return floatString;
1916: }
1917:
1918: int exponentIndex;
1919: String decimalNumeralString;
1920: String exponentString;
1921: // If the token is scientific notation
1922: if (-1 != (exponentIndex = floatString.indexOf('e'))
1923: || -1 != (exponentIndex = floatString.indexOf('E'))) {
1924: decimalNumeralString = floatString.substring(0,
1925: exponentIndex);
1926: exponentString = floatString.substring(exponentIndex + 1,
1927: floatString.length());
1928: decimalNumeralString = removeLocaleInfo(
1929: decimalNumeralString, DataType.FLOAT);
1930: return decimalNumeralString + "e" + exponentString; //$NON-NLS-1$
1931: }
1932: return removeLocaleInfo(floatString, DataType.FLOAT);
1933: }
1934:
1935: /*
1936: * Remove the locale specific prefixes, group separators, and locale
1937: * specific suffixes from input string
1938: */
1939: private String removeLocaleInfo(String token, DataType type) {
1940: StringBuilder tokenBuilder = new StringBuilder(token);
1941: boolean negative = removeLocaleSign(tokenBuilder);
1942: // Remove group separator
1943: String groupSeparator = String.valueOf(decimalFormat
1944: .getDecimalFormatSymbols().getGroupingSeparator());
1945: int separatorIndex = -1;
1946: while (-1 != (separatorIndex = tokenBuilder
1947: .indexOf(groupSeparator))) {
1948: tokenBuilder.delete(separatorIndex, separatorIndex + 1);
1949: }
1950: // Remove decimal separator
1951: String decimalSeparator = String.valueOf(decimalFormat
1952: .getDecimalFormatSymbols().getDecimalSeparator());
1953: separatorIndex = tokenBuilder.indexOf(decimalSeparator);
1954: StringBuilder result = new StringBuilder(""); //$NON-NLS-1$
1955: if (DataType.INT == type) {
1956: for (int i = 0; i < tokenBuilder.length(); i++) {
1957: if (-1 != Character.digit(tokenBuilder.charAt(i),
1958: Character.MAX_RADIX)) {
1959: result.append(tokenBuilder.charAt(i));
1960: }
1961: }
1962: }
1963: if (DataType.FLOAT == type) {
1964: if (tokenBuilder.toString().equals(
1965: decimalFormat.getDecimalFormatSymbols().getNaN())) {
1966: result.append("NaN");//$NON-NLS-1$
1967: } else if (tokenBuilder.toString().equals(
1968: decimalFormat.getDecimalFormatSymbols()
1969: .getInfinity())) {
1970: result.append("Infinity");//$NON-NLS-1$
1971: } else {
1972: for (int i = 0; i < tokenBuilder.length(); i++) {
1973: if (-1 != Character.digit(tokenBuilder.charAt(i),
1974: 10)) {
1975: result.append(Character.digit(tokenBuilder
1976: .charAt(i), 10));
1977: }
1978: }
1979: }
1980: }
1981: // Token is NaN or Infinity
1982: if (0 == result.length()) {
1983: result = tokenBuilder;
1984: }
1985: if (-1 != separatorIndex) {
1986: result.insert(separatorIndex, "."); //$NON-NLS-1$
1987: }
1988: // If input is negative
1989: if (negative) {
1990: result.insert(0, '-');
1991: }
1992: return result.toString();
1993: }
1994:
1995: /*
1996: * Remove positive and negative sign from the parameter stringBuilder, and
1997: * return whether the input string is negative
1998: */
1999: private boolean removeLocaleSign(StringBuilder tokenBuilder) {
2000: String positivePrefix = decimalFormat.getPositivePrefix();
2001: String positiveSuffix = decimalFormat.getPositiveSuffix();
2002: String negativePrefix = decimalFormat.getNegativePrefix();
2003: String negativeSuffix = decimalFormat.getNegativeSuffix();
2004:
2005: if (0 == tokenBuilder.indexOf("+")) { //$NON-NLS-1$
2006: tokenBuilder.delete(0, 1);
2007: }
2008: if (!positivePrefix.equals("") //$NON-NLS-1$
2009: && 0 == tokenBuilder.indexOf(positivePrefix)) {
2010: tokenBuilder.delete(0, positivePrefix.length());
2011: }
2012: if (!positiveSuffix.equals("") //$NON-NLS-1$
2013: && -1 != tokenBuilder.indexOf(positiveSuffix)) {
2014: tokenBuilder.delete(tokenBuilder.length()
2015: - positiveSuffix.length(), tokenBuilder.length());
2016: }
2017: boolean negative = false;
2018: if (0 == tokenBuilder.indexOf("-")) { //$NON-NLS-1$
2019: tokenBuilder.delete(0, 1);
2020: negative = true;
2021: }
2022: if (!negativePrefix.equals("") //$NON-NLS-1$
2023: && 0 == tokenBuilder.indexOf(negativePrefix)) {
2024: tokenBuilder.delete(0, negativePrefix.length());
2025: negative = true;
2026: }
2027: if (!negativeSuffix.equals("") //$NON-NLS-1$
2028: && -1 != tokenBuilder.indexOf(negativeSuffix)) {
2029: tokenBuilder.delete(tokenBuilder.length()
2030: - negativeSuffix.length(), tokenBuilder.length());
2031: negative = true;
2032: }
2033: return negative;
2034: }
2035:
2036: /*
2037: * Find the prefixed delimiter and posefixed delimiter in the input resource
2038: * and set the start index and end index of Matcher region. If postfixed
2039: * delimiter does not exist, the end index is set to be end of input.
2040: */
2041: private boolean setTokenRegion() {
2042: // The position where token begins
2043: int tokenStartIndex = 0;
2044: // The position where token ends
2045: int tokenEndIndex = 0;
2046: // Use delimiter pattern
2047: matcher.usePattern(delimiter);
2048: matcher.region(findStartIndex, bufferLength);
2049:
2050: tokenStartIndex = findPreDelimiter();
2051: if (setHeadTokenRegion(tokenStartIndex)) {
2052: return true;
2053: }
2054: tokenEndIndex = findPostDelimiter();
2055: // If the second delimiter is not found
2056: if (-1 == tokenEndIndex) {
2057: // Just first Delimiter Exists
2058: if (findStartIndex == bufferLength) {
2059: return false;
2060: }
2061: tokenEndIndex = bufferLength;
2062: findStartIndex = bufferLength;
2063: }
2064:
2065: matcher.region(tokenStartIndex, tokenEndIndex);
2066: return true;
2067: }
2068:
2069: /*
2070: * Find prefix delimiter
2071: */
2072: private int findPreDelimiter() {
2073: int tokenStartIndex;
2074: boolean findComplete = false;
2075: while (!findComplete) {
2076: if (matcher.find()) {
2077: findComplete = true;
2078: // If just delimiter remains
2079: if (matcher.start() == findStartIndex
2080: && matcher.end() == bufferLength) {
2081: // If more input resource exists
2082: if (!inputExhausted) {
2083: readMore();
2084: resetMatcher();
2085: findComplete = false;
2086: }
2087: }
2088: } else {
2089: if (!inputExhausted) {
2090: readMore();
2091: resetMatcher();
2092: } else {
2093: return -1;
2094: }
2095: }
2096: }
2097: tokenStartIndex = matcher.end();
2098: findStartIndex = matcher.end();
2099: return tokenStartIndex;
2100: }
2101:
2102: /*
2103: * Handle some special cases
2104: */
2105: private boolean setHeadTokenRegion(int findIndex) {
2106: int tokenStartIndex;
2107: int tokenEndIndex;
2108: boolean setSuccess = false;
2109: // If no delimiter exists, but something exites in this scanner
2110: if (-1 == findIndex && preStartIndex != bufferLength) {
2111: tokenStartIndex = preStartIndex;
2112: tokenEndIndex = bufferLength;
2113: findStartIndex = bufferLength;
2114: matcher.region(tokenStartIndex, tokenEndIndex);
2115: setSuccess = true;
2116: }
2117: // If the first delimiter of scanner is not at the find start position
2118: if (-1 != findIndex && preStartIndex != matcher.start()) {
2119: tokenStartIndex = preStartIndex;
2120: tokenEndIndex = matcher.start();
2121: findStartIndex = matcher.start();
2122: // set match region and return
2123: matcher.region(tokenStartIndex, tokenEndIndex);
2124: setSuccess = true;
2125: }
2126: return setSuccess;
2127: }
2128:
2129: /*
2130: * Find postfix delimiter
2131: */
2132: private int findPostDelimiter() {
2133: int tokenEndIndex = 0;
2134: boolean findComplete = false;
2135: while (!findComplete) {
2136: if (matcher.find()) {
2137: findComplete = true;
2138: if (matcher.start() == findStartIndex
2139: && matcher.start() == matcher.end()) {
2140: findComplete = false;
2141: }
2142: } else {
2143: if (!inputExhausted) {
2144: readMore();
2145: resetMatcher();
2146: } else {
2147: return -1;
2148: }
2149: }
2150: }
2151: tokenEndIndex = matcher.start();
2152: findStartIndex = matcher.start();
2153: return tokenEndIndex;
2154: }
2155:
2156: /*
2157: * Read more data from underlying Readable. If nothing is available or I/O
2158: * operation fails, global boolean variable inputExhausted will be set to
2159: * true, otherwise set to false.
2160: */
2161: private void readMore() {
2162: int oldPosition = buffer.position();
2163: int oldBufferLength = bufferLength;
2164: // Increase capacity if empty space is not enough
2165: if (bufferLength >= buffer.capacity()) {
2166: expandBuffer();
2167: }
2168:
2169: // Read input resource
2170: int readCount = 0;
2171: try {
2172: buffer.limit(buffer.capacity());
2173: buffer.position(oldBufferLength);
2174: while ((readCount = input.read(buffer)) == 0) {
2175: // nothing to do here
2176: }
2177: } catch (IOException e) {
2178: // Consider the scenario: readable puts 4 chars into
2179: // buffer and then an IOException is thrown out. In this case,
2180: // buffer is
2181: // actually grown, but readable.read() will never return.
2182: bufferLength = buffer.position();
2183: /*
2184: * Uses -1 to record IOException occurring, and no more input can be
2185: * read.
2186: */
2187: readCount = -1;
2188: lastIOException = e;
2189: }
2190:
2191: buffer.flip();
2192: buffer.position(oldPosition);
2193: if (-1 == readCount) {
2194: inputExhausted = true;
2195: } else {
2196: bufferLength = readCount + bufferLength;
2197: }
2198: }
2199:
2200: // Expand the size of internal buffer.
2201: private void expandBuffer() {
2202: int oldPosition = buffer.position();
2203: int oldCapacity = buffer.capacity();
2204: int oldLimit = buffer.limit();
2205: int newCapacity = oldCapacity * DIPLOID;
2206: char[] newBuffer = new char[newCapacity];
2207: System.arraycopy(buffer.array(), 0, newBuffer, 0, oldLimit);
2208: buffer = CharBuffer.wrap(newBuffer, 0, newCapacity);
2209: buffer.position(oldPosition);
2210: buffer.limit(oldLimit);
2211: }
2212: }
|