8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util;
27
28 import java.nio.file.Path;
29 import java.nio.file.Files;
30 import java.util.regex.*;
31 import java.io.*;
32 import java.math.*;
33 import java.nio.*;
34 import java.nio.channels.*;
35 import java.nio.charset.*;
36 import java.text.*;
37 import java.util.Locale;
38
39 import sun.misc.LRUCache;
40
41 /**
42 * A simple text scanner which can parse primitive types and strings using
43 * regular expressions.
44 *
45 * <p>A <code>Scanner</code> breaks its input into tokens using a
46 * delimiter pattern, which by default matches whitespace. The resulting
47 * tokens may then be converted into values of different types using the
48 * various <tt>next</tt> methods.
49 *
50 * <p>For example, this code allows a user to read a number from
51 * <tt>System.in</tt>:
52 * <blockquote><pre>{@code
53 * Scanner sc = new Scanner(System.in);
54 * int i = sc.nextInt();
55 * }</pre></blockquote>
56 *
57 * <p>As another example, this code allows <code>long</code> types to be
978 }
979 // Last token; Match the pattern here or throw
980 matcher.usePattern(pattern);
981 matcher.region(position, buf.limit());
982 if (matcher.matches()) {
983 String s = matcher.group();
984 position = matcher.end();
985 return s;
986 }
987 // Last piece does not match
988 return null;
989 }
990
991 // There is a partial token in the buffer; must read more
992 // to complete it
993 needInput = true;
994 return null;
995 }
996
997 // Finds the specified pattern in the buffer up to horizon.
998 // Returns a match for the specified input pattern.
999 private String findPatternInBuffer(Pattern pattern, int horizon) {
1000 matchValid = false;
1001 matcher.usePattern(pattern);
1002 int bufferLimit = buf.limit();
1003 int horizonLimit = -1;
1004 int searchLimit = bufferLimit;
1005 if (horizon > 0) {
1006 horizonLimit = position + horizon;
1007 if (horizonLimit < bufferLimit)
1008 searchLimit = horizonLimit;
1009 }
1010 matcher.region(position, searchLimit);
1011 if (matcher.find()) {
1012 if (matcher.hitEnd() && (!sourceClosed)) {
1013 // The match may be longer if didn't hit horizon or real end
1014 if (searchLimit != horizonLimit) {
1015 // Hit an artificial end; try to extend the match
1016 needInput = true;
1017 return null;
1018 }
1019 // The match could go away depending on what is next
1020 if ((searchLimit == horizonLimit) && matcher.requireEnd()) {
1021 // Rare case: we hit the end of input and it happens
1022 // that it is at the horizon and the end of input is
1023 // required for the match.
1024 needInput = true;
1025 return null;
1026 }
1027 }
1028 // Did not hit end, or hit real end, or hit horizon
1029 position = matcher.end();
1030 return matcher.group();
1031 }
1032
1033 if (sourceClosed)
1034 return null;
1035
1036 // If there is no specified horizon, or if we have not searched
1037 // to the specified horizon yet, get more input
1038 if ((horizon == 0) || (searchLimit != horizonLimit))
1039 needInput = true;
1040 return null;
1041 }
1042
1043 // Returns a match for the specified input pattern anchored at
1044 // the current position
1045 private String matchPatternInBuffer(Pattern pattern) {
1046 matchValid = false;
1047 matcher.usePattern(pattern);
1048 matcher.region(position, buf.limit());
1049 if (matcher.lookingAt()) {
1050 if (matcher.hitEnd() && (!sourceClosed)) {
1051 // Get more input and try again
1052 needInput = true;
1053 return null;
1054 }
1055 position = matcher.end();
1056 return matcher.group();
1057 }
1058
1059 if (sourceClosed)
1060 return null;
1061
1062 // Read more to find pattern
1063 needInput = true;
1064 return null;
1065 }
1066
1067 // Throws if the scanner is closed
1068 private void ensureOpen() {
1069 if (closed)
1070 throw new IllegalStateException("Scanner closed");
1071 }
1072
1073 // Public methods
1074
1075 /**
1076 * Closes this scanner.
1077 *
1078 * <p> If this scanner has not yet been closed then if its underlying
1079 * {@linkplain java.lang.Readable readable} also implements the {@link
1080 * java.io.Closeable} interface then the readable's <tt>close</tt> method
1081 * will be invoked. If this scanner is already closed then invoking this
1082 * method will have no effect.
1083 *
1084 * <p>Attempting to perform search operations after a scanner has
1576 * position is unchanged. This method may block waiting for input that
1577 * matches the pattern.
1578 *
1579 * <p>Since this method continues to search through the input looking
1580 * for the specified pattern, it may buffer all of the input searching for
1581 * the desired token if no line separators are present.
1582 *
1583 * @param pattern the pattern to scan for
1584 * @return the text that matched the specified pattern
1585 * @throws IllegalStateException if this scanner is closed
1586 */
1587 public String findInLine(Pattern pattern) {
1588 ensureOpen();
1589 if (pattern == null)
1590 throw new NullPointerException();
1591 clearCaches();
1592 // Expand buffer to include the next newline or end of input
1593 int endPosition = 0;
1594 saveState();
1595 while (true) {
1596 String token = findPatternInBuffer(separatorPattern(), 0);
1597 if (token != null) {
1598 endPosition = matcher.start();
1599 break; // up to next newline
1600 }
1601 if (needInput) {
1602 readInput();
1603 } else {
1604 endPosition = buf.limit();
1605 break; // up to end of input
1606 }
1607 }
1608 revertState();
1609 int horizonForLine = endPosition - position;
1610 // If there is nothing between the current pos and the next
1611 // newline simply return null, invoking findWithinHorizon
1612 // with "horizon=0" will scan beyond the line bound.
1613 if (horizonForLine == 0)
1614 return null;
1615 // Search for the pattern
1616 return findWithinHorizon(pattern, horizonForLine);
1617 }
1659 *
1660 * <p>If horizon is negative, then an IllegalArgumentException is
1661 * thrown.
1662 *
1663 * @param pattern the pattern to scan for
1664 * @param horizon the search horizon
1665 * @return the text that matched the specified pattern
1666 * @throws IllegalStateException if this scanner is closed
1667 * @throws IllegalArgumentException if horizon is negative
1668 */
1669 public String findWithinHorizon(Pattern pattern, int horizon) {
1670 ensureOpen();
1671 if (pattern == null)
1672 throw new NullPointerException();
1673 if (horizon < 0)
1674 throw new IllegalArgumentException("horizon < 0");
1675 clearCaches();
1676
1677 // Search for the pattern
1678 while (true) {
1679 String token = findPatternInBuffer(pattern, horizon);
1680 if (token != null) {
1681 matchValid = true;
1682 return token;
1683 }
1684 if (needInput)
1685 readInput();
1686 else
1687 break; // up to end of input
1688 }
1689 return null;
1690 }
1691
1692 /**
1693 * Skips input that matches the specified pattern, ignoring delimiters.
1694 * This method will skip input if an anchored match of the specified
1695 * pattern succeeds.
1696 *
1697 * <p>If a match to the specified pattern is not found at the
1698 * current position, then no input is skipped and a
1699 * <tt>NoSuchElementException</tt> is thrown.
1700 *
1701 * <p>Since this method seeks to match the specified pattern starting at
1702 * the scanner's current position, patterns that can match a lot of
1703 * input (".*", for example) may cause the scanner to buffer a large
1704 * amount of input.
1705 *
1706 * <p>Note that it is possible to skip something without risking a
1707 * <code>NoSuchElementException</code> by using a pattern that can
1708 * match nothing, e.g., <code>sc.skip("[ \t]*")</code>.
1709 *
1710 * @param pattern a string specifying the pattern to skip over
1711 * @return this scanner
1712 * @throws NoSuchElementException if the specified pattern is not found
1713 * @throws IllegalStateException if this scanner is closed
1714 */
1715 public Scanner skip(Pattern pattern) {
1716 ensureOpen();
1717 if (pattern == null)
1718 throw new NullPointerException();
1719 clearCaches();
1720
1721 // Search for the pattern
1722 while (true) {
1723 String token = matchPatternInBuffer(pattern);
1724 if (token != null) {
1725 matchValid = true;
1726 position = matcher.end();
1727 return this;
1728 }
1729 if (needInput)
1730 readInput();
1731 else
1732 throw new NoSuchElementException();
1733 }
1734 }
1735
1736 /**
1737 * Skips input that matches a pattern constructed from the specified
1738 * string.
1739 *
1740 * <p> An invocation of this method of the form <tt>skip(pattern)</tt>
1741 * behaves in exactly the same way as the invocation
1742 * <tt>skip(Pattern.compile(pattern))</tt>.
1743 *
1744 * @param pattern a string specifying the pattern to skip over
|
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util;
27
28 import java.io.*;
29 import java.math.*;
30 import java.nio.*;
31 import java.nio.channels.*;
32 import java.nio.charset.*;
33 import java.nio.file.Path;
34 import java.nio.file.Files;
35 import java.text.*;
36 import java.util.regex.*;
37
38 import sun.misc.LRUCache;
39
40 /**
41 * A simple text scanner which can parse primitive types and strings using
42 * regular expressions.
43 *
44 * <p>A <code>Scanner</code> breaks its input into tokens using a
45 * delimiter pattern, which by default matches whitespace. The resulting
46 * tokens may then be converted into values of different types using the
47 * various <tt>next</tt> methods.
48 *
49 * <p>For example, this code allows a user to read a number from
50 * <tt>System.in</tt>:
51 * <blockquote><pre>{@code
52 * Scanner sc = new Scanner(System.in);
53 * int i = sc.nextInt();
54 * }</pre></blockquote>
55 *
56 * <p>As another example, this code allows <code>long</code> types to be
977 }
978 // Last token; Match the pattern here or throw
979 matcher.usePattern(pattern);
980 matcher.region(position, buf.limit());
981 if (matcher.matches()) {
982 String s = matcher.group();
983 position = matcher.end();
984 return s;
985 }
986 // Last piece does not match
987 return null;
988 }
989
990 // There is a partial token in the buffer; must read more
991 // to complete it
992 needInput = true;
993 return null;
994 }
995
996 // Finds the specified pattern in the buffer up to horizon.
997 // Returns true if the specified input pattern was matched,
998 // and leaves the matcher field with the current match state.
999 private boolean findPatternInBuffer(Pattern pattern, int horizon) {
1000 matchValid = false;
1001 matcher.usePattern(pattern);
1002 int bufferLimit = buf.limit();
1003 int horizonLimit = -1;
1004 int searchLimit = bufferLimit;
1005 if (horizon > 0) {
1006 horizonLimit = position + horizon;
1007 if (horizonLimit < bufferLimit)
1008 searchLimit = horizonLimit;
1009 }
1010 matcher.region(position, searchLimit);
1011 if (matcher.find()) {
1012 if (matcher.hitEnd() && (!sourceClosed)) {
1013 // The match may be longer if didn't hit horizon or real end
1014 if (searchLimit != horizonLimit) {
1015 // Hit an artificial end; try to extend the match
1016 needInput = true;
1017 return false;
1018 }
1019 // The match could go away depending on what is next
1020 if ((searchLimit == horizonLimit) && matcher.requireEnd()) {
1021 // Rare case: we hit the end of input and it happens
1022 // that it is at the horizon and the end of input is
1023 // required for the match.
1024 needInput = true;
1025 return false;
1026 }
1027 }
1028 // Did not hit end, or hit real end, or hit horizon
1029 position = matcher.end();
1030 return true;
1031 }
1032
1033 if (sourceClosed)
1034 return false;
1035
1036 // If there is no specified horizon, or if we have not searched
1037 // to the specified horizon yet, get more input
1038 if ((horizon == 0) || (searchLimit != horizonLimit))
1039 needInput = true;
1040 return false;
1041 }
1042
1043 // Attempts to match a pattern anchored at the current position.
1044 // Returns true if the specified input pattern was matched,
1045 // and leaves the matcher field with the current match state.
1046 private boolean matchPatternInBuffer(Pattern pattern) {
1047 matchValid = false;
1048 matcher.usePattern(pattern);
1049 matcher.region(position, buf.limit());
1050 if (matcher.lookingAt()) {
1051 if (matcher.hitEnd() && (!sourceClosed)) {
1052 // Get more input and try again
1053 needInput = true;
1054 return false;
1055 }
1056 position = matcher.end();
1057 return true;
1058 }
1059
1060 if (sourceClosed)
1061 return false;
1062
1063 // Read more to find pattern
1064 needInput = true;
1065 return false;
1066 }
1067
1068 // Throws if the scanner is closed
1069 private void ensureOpen() {
1070 if (closed)
1071 throw new IllegalStateException("Scanner closed");
1072 }
1073
1074 // Public methods
1075
1076 /**
1077 * Closes this scanner.
1078 *
1079 * <p> If this scanner has not yet been closed then if its underlying
1080 * {@linkplain java.lang.Readable readable} also implements the {@link
1081 * java.io.Closeable} interface then the readable's <tt>close</tt> method
1082 * will be invoked. If this scanner is already closed then invoking this
1083 * method will have no effect.
1084 *
1085 * <p>Attempting to perform search operations after a scanner has
1577 * position is unchanged. This method may block waiting for input that
1578 * matches the pattern.
1579 *
1580 * <p>Since this method continues to search through the input looking
1581 * for the specified pattern, it may buffer all of the input searching for
1582 * the desired token if no line separators are present.
1583 *
1584 * @param pattern the pattern to scan for
1585 * @return the text that matched the specified pattern
1586 * @throws IllegalStateException if this scanner is closed
1587 */
1588 public String findInLine(Pattern pattern) {
1589 ensureOpen();
1590 if (pattern == null)
1591 throw new NullPointerException();
1592 clearCaches();
1593 // Expand buffer to include the next newline or end of input
1594 int endPosition = 0;
1595 saveState();
1596 while (true) {
1597 if (findPatternInBuffer(separatorPattern(), 0)) {
1598 endPosition = matcher.start();
1599 break; // up to next newline
1600 }
1601 if (needInput) {
1602 readInput();
1603 } else {
1604 endPosition = buf.limit();
1605 break; // up to end of input
1606 }
1607 }
1608 revertState();
1609 int horizonForLine = endPosition - position;
1610 // If there is nothing between the current pos and the next
1611 // newline simply return null, invoking findWithinHorizon
1612 // with "horizon=0" will scan beyond the line bound.
1613 if (horizonForLine == 0)
1614 return null;
1615 // Search for the pattern
1616 return findWithinHorizon(pattern, horizonForLine);
1617 }
1659 *
1660 * <p>If horizon is negative, then an IllegalArgumentException is
1661 * thrown.
1662 *
1663 * @param pattern the pattern to scan for
1664 * @param horizon the search horizon
1665 * @return the text that matched the specified pattern
1666 * @throws IllegalStateException if this scanner is closed
1667 * @throws IllegalArgumentException if horizon is negative
1668 */
1669 public String findWithinHorizon(Pattern pattern, int horizon) {
1670 ensureOpen();
1671 if (pattern == null)
1672 throw new NullPointerException();
1673 if (horizon < 0)
1674 throw new IllegalArgumentException("horizon < 0");
1675 clearCaches();
1676
1677 // Search for the pattern
1678 while (true) {
1679 if (findPatternInBuffer(pattern, horizon)) {
1680 matchValid = true;
1681 return matcher.group();
1682 }
1683 if (needInput)
1684 readInput();
1685 else
1686 break; // up to end of input
1687 }
1688 return null;
1689 }
1690
1691 /**
1692 * Skips input that matches the specified pattern, ignoring delimiters.
1693 * This method will skip input if an anchored match of the specified
1694 * pattern succeeds.
1695 *
1696 * <p>If a match to the specified pattern is not found at the
1697 * current position, then no input is skipped and a
1698 * <tt>NoSuchElementException</tt> is thrown.
1699 *
1700 * <p>Since this method seeks to match the specified pattern starting at
1701 * the scanner's current position, patterns that can match a lot of
1702 * input (".*", for example) may cause the scanner to buffer a large
1703 * amount of input.
1704 *
1705 * <p>Note that it is possible to skip something without risking a
1706 * <code>NoSuchElementException</code> by using a pattern that can
1707 * match nothing, e.g., <code>sc.skip("[ \t]*")</code>.
1708 *
1709 * @param pattern a string specifying the pattern to skip over
1710 * @return this scanner
1711 * @throws NoSuchElementException if the specified pattern is not found
1712 * @throws IllegalStateException if this scanner is closed
1713 */
1714 public Scanner skip(Pattern pattern) {
1715 ensureOpen();
1716 if (pattern == null)
1717 throw new NullPointerException();
1718 clearCaches();
1719
1720 // Search for the pattern
1721 while (true) {
1722 if (matchPatternInBuffer(pattern)) {
1723 matchValid = true;
1724 position = matcher.end();
1725 return this;
1726 }
1727 if (needInput)
1728 readInput();
1729 else
1730 throw new NoSuchElementException();
1731 }
1732 }
1733
1734 /**
1735 * Skips input that matches a pattern constructed from the specified
1736 * string.
1737 *
1738 * <p> An invocation of this method of the form <tt>skip(pattern)</tt>
1739 * behaves in exactly the same way as the invocation
1740 * <tt>skip(Pattern.compile(pattern))</tt>.
1741 *
1742 * @param pattern a string specifying the pattern to skip over
|