diff options
author | Jesse Wilson <jessewilson@google.com> | 2009-09-14 15:30:14 -0700 |
---|---|---|
committer | Jesse Wilson <jessewilson@google.com> | 2009-09-14 15:30:14 -0700 |
commit | 77d58e2a1577a4992f4d81e9ca2807f7533725c6 (patch) | |
tree | 14cb10317acb854fee0a4aa2b7944a22aaa02e6b /regex | |
parent | 521d96b9155899a5cc94ba942d018e5cca465512 (diff) | |
download | libcore-77d58e2a1577a4992f4d81e9ca2807f7533725c6.zip libcore-77d58e2a1577a4992f4d81e9ca2807f7533725c6.tar.gz libcore-77d58e2a1577a4992f4d81e9ca2807f7533725c6.tar.bz2 |
Update regex to Harmony r802921.
Notable changes:
- Reordered methods to be consistent with Harmony. Although our
implementations are significantly different, this will make
it easier to track Javadoc and signature changes.
- Some unchecked exceptions removed from method signatures
- Changed StringBuffer use to StringBuilder
- Changed PatternSyntaxException description field to 'desc' for
serialization compatibility.
commit deba65caf92e9c5b77b29bcf9dcb26d637af90cb
Merge: b239d4a 457c6cc
Author: Jesse Wilson <jessewilson@google.com>
Date: Wed Aug 12 09:23:58 2009 -0700
Merge branch 'regex_802921' into regex_dalvik
Conflicts:
libcore/regex/.classpath
libcore/regex/.settings/org.eclipse.jdt.core.prefs
libcore/regex/build.xml
libcore/regex/src/main/java/java/util/regex/AbstractCharClass.java
libcore/regex/src/main/java/java/util/regex/AbstractSet.java
libcore/regex/src/main/java/java/util/regex/CIDecomposedCharSet.java
libcore/regex/src/main/java/java/util/regex/CharClass.java
libcore/regex/src/main/java/java/util/regex/DecomposedCharSet.java
libcore/regex/src/main/java/java/util/regex/IntArrHash.java
libcore/regex/src/main/java/java/util/regex/JointSet.java
libcore/regex/src/main/java/java/util/regex/Lexer.java
libcore/regex/src/main/java/java/util/regex/MatchResult.java
libcore/regex/src/main/java/java/util/regex/Matcher.java
libcore/regex/src/main/java/java/util/regex/Pattern.java
libcore/regex/src/main/java/java/util/regex/PatternSyntaxException.java
libcore/regex/src/main/java/java/util/regex/SequenceSet.java
libcore/regex/src/main/java/java/util/regex/UCIDecomposedCharSet.java
libcore/regex/src/main/java/java/util/regex/UCISequenceSet.java
libcore/regex/src/main/java/org/apache/harmony/regex/internal/nls/messages.properties
libcore/regex/src/test/java/org/apache/harmony/tests/java/util/regex/Matcher2Test.java
libcore/regex/src/test/java/org/apache/harmony/tests/java/util/regex/MatcherTest.java
libcore/regex/src/test/java/org/apache/harmony/tests/java/util/regex/ModeTest.java
libcore/regex/src/test/java/org/apache/harmony/tests/java/util/regex/Pattern2Test.java
libcore/regex/src/test/java/org/apache/harmony/tests/java/util/regex/PatternErrorTest.java
libcore/regex/src/test/java/org/apache/harmony/tests/java/util/regex/PatternSyntaxExceptionTest.java
libcore/regex/src/test/java/org/apache/harmony/tests/java/util/regex/PatternTest.java
libcore/regex/src/test/java/org/apache/harmony/tests/java/util/regex/ReplaceTest.java
libcore/regex/src/test/java/org/apache/harmony/tests/java/util/regex/SplitTest.java
commit b239d4a17905f9e0b609eeaa12de9dfba433c44a
Author: Jesse Wilson <jessewilson@google.com>
Date: Wed Aug 12 08:37:21 2009 -0700
Dalvik Regex
commit 457c6cca0629f20b118cd128353439763e40fe9e
Author: Jesse Wilson <jessewilson@google.com>
Date: Wed Aug 12 08:36:40 2009 -0700
Regex 802921
commit 51f4e67d71a8f92d8efa073fab32c540f6015594
Author: Jesse Wilson <jessewilson@google.com>
Date: Wed Aug 12 08:34:57 2009 -0700
Regex 527399
Diffstat (limited to 'regex')
-rw-r--r-- | regex/src/main/java/java/util/regex/MatchResult.java | 67 | ||||
-rw-r--r-- | regex/src/main/java/java/util/regex/Matcher.java | 752 | ||||
-rw-r--r-- | regex/src/main/java/java/util/regex/Pattern.java | 355 | ||||
-rw-r--r-- | regex/src/main/java/java/util/regex/PatternSyntaxException.java | 56 |
4 files changed, 567 insertions, 663 deletions
diff --git a/regex/src/main/java/java/util/regex/MatchResult.java b/regex/src/main/java/java/util/regex/MatchResult.java index fa67ba6..76c17a8 100644 --- a/regex/src/main/java/java/util/regex/MatchResult.java +++ b/regex/src/main/java/java/util/regex/MatchResult.java @@ -1,17 +1,18 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package java.util.regex; @@ -22,91 +23,75 @@ package java.util.regex; * pair of parentheses in the regular expression and an additional group for * the whole regular expression. The start, end, and contents of each group * can be queried. - * + * * @see Matcher * @see Matcher#toMatchResult() - * - * @since Android 1.0 */ public interface MatchResult { /** * Returns the index of the first character following the text that matched - * the whole regular expression. - * + * the whole regular expression. + * * @return the character index. - * - * @since Android 1.0 */ int end(); /** * Returns the index of the first character following the text that matched * a given group. - * + * * @param group * the group, ranging from 0 to groupCount() - 1, with 0 * representing the whole pattern. - * + * * @return the character index. - * - * @since Android 1.0 */ int end(int group); /** - * Returns the text that matched the whole regular expression. - * + * Returns the text that matched the whole regular expression. + * * @return the text. - * - * @since Android 1.0 */ String group(); /** * Returns the text that matched a given group of the regular expression. - * + * * @param group * the group, ranging from 0 to groupCount() - 1, with 0 * representing the whole pattern. - * + * * @return the text that matched the group. - * - * @since Android 1.0 */ String group(int group); /** * Returns the number of groups in the result, which is always equal to * the number of groups in the original regular expression. - * + * * @return the number of groups. - * - * @since Android 1.0 */ int groupCount(); /** * Returns the index of the first character of the text that matched - * the whole regular expression. - * + * the whole regular expression. + * * @return the character index. - * - * @since Android 1.0 */ int start(); /** * Returns the index of the first character of the text that matched a given * group. - * + * * @param group * the group, ranging from 0 to groupCount() - 1, with 0 * representing the whole pattern. - * + * * @return the character index. - * - * @since Android 1.0 */ int start(int group); } diff --git a/regex/src/main/java/java/util/regex/Matcher.java b/regex/src/main/java/java/util/regex/Matcher.java index e3e4874..be5c782 100644 --- a/regex/src/main/java/java/util/regex/Matcher.java +++ b/regex/src/main/java/java/util/regex/Matcher.java @@ -44,8 +44,6 @@ import com.ibm.icu4jni.regex.NativeRegEx; * {@code Pattern} was successful and at which position the next attempt would * resume the search. Depending on the application's needs, it may become * necessary to explicitly {@link #reset()} this state from time to time. - * - * @since Android 1.0 */ public final class Matcher implements MatchResult { @@ -128,39 +126,99 @@ public final class Matcher implements MatchResult { } /** + * Appends a literal part of the input plus a replacement for the current + * match to a given {@link StringBuffer}. The literal part is exactly the + * part of the input between the previous match and the current match. The + * method can be used in conjunction with {@link #find()} and + * {@link #appendTail(StringBuffer)} to walk through the input and replace + * all occurrences of the {@code Pattern} with something else. + * + * @param buffer + * the {@code StringBuffer} to append to. + * @param replacement + * the replacement text. + * @return the {@code Matcher} itself. + * @throws IllegalStateException + * if no successful match has been made. + */ + public Matcher appendReplacement(StringBuffer buffer, String replacement) { + buffer.append(input.substring(appendPos, start())); + appendEvaluated(buffer, replacement); + appendPos = end(); + + return this; + } + + /** + * Internal helper method to append a given string to a given string buffer. + * If the string contains any references to groups, these are replaced by + * the corresponding group's contents. + * + * @param buffer + * the string buffer. + * @param s + * the string to append. + */ + private void appendEvaluated(StringBuffer buffer, String s) { + boolean escape = false; + boolean dollar = false; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\\' && !escape) { + escape = true; + } else if (c == '$' && !escape) { + dollar = true; + } else if (c >= '0' && c <= '9' && dollar) { + buffer.append(group(c - '0')); + dollar = false; + } else { + buffer.append(c); + dollar = false; + escape = false; + } + } + + // This seemingly stupid piece of code reproduces a JDK bug. + if (escape) { + throw new ArrayIndexOutOfBoundsException(s.length()); + } + } + + /** * Resets the Matcher. A new input sequence and a new region can be * specified. Results of a previous find get lost. The next attempt to find * an occurrence of the Pattern in the string will start at the beginning of * the region. This is the internal version of reset() to which the several * public versions delegate. - * + * * @param input * the input sequence. * @param start * the start of the region. * @param end * the end of the region. - * + * * @return the matcher itself. */ private Matcher reset(CharSequence input, int start, int end) { if (input == null) { throw new IllegalArgumentException(); } - - if (start < 0 || end < 0 || start > input.length() || + + if (start < 0 || end < 0 || start > input.length() || end > input.length() || start > end) { throw new IllegalArgumentException(); } // Maybe should have a reset() here, but it makes thing worse... // NativeRegEx.reset(nativePattern, 0); - + if (!input.equals(this.input)) { this.input = input.toString(); - + NativeRegEx.setText(nativePattern, this.input); - + regionStart = 0; regionEnd = input.length(); } @@ -176,22 +234,8 @@ public final class Matcher implements MatchResult { matchFound = false; findPos = regionStart; appendPos = 0; - - return this; - } - /** - * Resets the {@code Matcher}. This results in the region being set to the - * whole input. Results of a previous find get lost. The next attempt to - * find an occurrence of the {@link Pattern} in the string will start at the - * beginning of the input. - * - * @return the {@code Matcher} itself. - * - * @since Android 1.0 - */ - public Matcher reset() { - return reset(input, 0, input.length()); + return this; } /** @@ -204,73 +248,24 @@ public final class Matcher implements MatchResult { * the new input sequence. * * @return the {@code Matcher} itself. - * - * @since Android 1.0 */ public Matcher reset(CharSequence input) { return reset(input, 0, input.length()); } /** - * Sets a new pattern for the {@code Matcher}. Results of a previous find - * get lost. The next attempt to find an occurrence of the {@link Pattern} - * in the string will start at the beginning of the input. - * - * @param pattern - * the new {@code Pattern}. - * + * Resets the {@code Matcher}. This results in the region being set to the + * whole input. Results of a previous find get lost. The next attempt to + * find an occurrence of the {@link Pattern} in the string will start at the + * beginning of the input. + * * @return the {@code Matcher} itself. - * - * @since Android 1.0 */ - public Matcher usePattern(Pattern pattern) { - if (pattern == null) { - throw new IllegalArgumentException(); - } - - this.pattern = pattern; - - if (nativePattern != 0) { - NativeRegEx.close(nativePattern); - } - nativePattern = NativeRegEx.clone(pattern.mNativePattern); - - if (input != null) { - NativeRegEx.setText(nativePattern, input); - NativeRegEx.setRegion(nativePattern, regionStart, regionEnd); - NativeRegEx.useAnchoringBounds(nativePattern, anchoringBounds); - NativeRegEx.useTransparentBounds(nativePattern, transparentBounds); - } - - matchOffsets = new int[(this.pattern.mGroupCount + 1) * 2]; - matchFound = false; - return this; - } - - /** - * Returns the {@link Pattern} instance used inside this matcher. - * - * @return the {@code Pattern} instance. - * - * @since Android 1.0 - */ - public Pattern pattern() { - return pattern; + public Matcher reset() { + return reset(input, 0, input.length()); } /** - * Returns the number of groups in the results, which is always equal to - * the number of groups in the original regular expression. - * - * @return the number of groups. - * - * @since Android 1.0 - */ - public int groupCount() { - return pattern.mGroupCount; - } - - /** * Resets this matcher and sets a region. Only characters inside the region * are considered for a match. * @@ -279,109 +274,150 @@ public final class Matcher implements MatchResult { * @param end * the first character after the end of the region. * @return the {@code Matcher} itself. - * @since Android 1.0 */ public Matcher region(int start, int end) { return reset(input, start, end); } + /** - * Returns this matcher's region start, that is, the first character that is - * considered for a match. - * - * @return the start of the region. - * @since Android 1.0 + * Appends the (unmatched) remainder of the input to the given + * {@link StringBuffer}. The method can be used in conjunction with + * {@link #find()} and {@link #appendReplacement(StringBuffer, String)} to + * walk through the input and replace all matches of the {@code Pattern} + * with something else. + * + * @param buffer + * the {@code StringBuffer} to append to. + * @return the {@code StringBuffer}. + * @throws IllegalStateException + * if no successful match has been made. */ - public int regionStart() { - return regionStart; + public StringBuffer appendTail(StringBuffer buffer) { + if (appendPos < regionEnd) { + buffer.append(input.substring(appendPos, regionEnd)); + } + + return buffer; } /** - * Returns this matcher's region end, that is, the first character that is - * not considered for a match. - * - * @return the end of the region. - * @since Android 1.0 + * Replaces the first occurrence of this matcher's pattern in the input with + * a given string. + * + * @param replacement + * the replacement text. + * @return the modified input string. */ - public int regionEnd() { - return regionEnd; + public String replaceFirst(String replacement) { + StringBuffer buffer = new StringBuffer(input.length()); + + findPos = 0; + appendPos = 0; + matchFound = false; + searching = false; + + if (find()) { + appendReplacement(buffer, replacement); + } + + return appendTail(buffer).toString(); } /** - * Determines whether this matcher has anchoring bounds enabled or not. When - * anchoring bounds are enabled, the start and end of the input match the - * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled - * by default. - * - * @param value - * the new value for anchoring bounds. - * @return the {@code Matcher} itself. - * @since Android 1.0 + * Replaces all occurrences of this matcher's pattern in the input with a + * given string. + * + * @param replacement + * the replacement text. + * @return the modified input string. */ - public Matcher useAnchoringBounds(boolean value) { - anchoringBounds = value; - NativeRegEx.useAnchoringBounds(nativePattern, value); - return this; + public String replaceAll(String replacement) { + StringBuffer buffer = new StringBuffer(input.length()); + + findPos = 0; + appendPos = 0; + matchFound = false; + searching = false; + + while (find()) { + appendReplacement(buffer, replacement); + } + + return appendTail(buffer).toString(); } - + /** - * Indicates whether this matcher has anchoring bounds enabled. When - * anchoring bounds are enabled, the start and end of the input match the - * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled - * by default. - * - * @return true if (and only if) the {@code Matcher} uses anchoring bounds. - * @since Android 1.0 + * Returns the {@link Pattern} instance used inside this matcher. + * + * @return the {@code Pattern} instance. */ - public boolean hasAnchoringBounds() { - return anchoringBounds; + public Pattern pattern() { + return pattern; } /** - * Determines whether this matcher has transparent bounds enabled or not. - * When transparent bounds are enabled, the parts of the input outside the - * region are subject to lookahead and lookbehind, otherwise they are not. - * Transparent bounds are disabled by default. - * - * @param value - * the new value for transparent bounds. - * @return the {@code Matcher} itself. - * @since Android 1.0 + * Returns the text that matched a given group of the regular expression. + * + * @param group + * the group, ranging from 0 to groupCount() - 1, with 0 + * representing the whole pattern. + * @return the text that matched the group. + * @throws IllegalStateException + * if no successful match has been made. */ - public Matcher useTransparentBounds(boolean value) { - transparentBounds = value; - NativeRegEx.useTransparentBounds(nativePattern, value); - return this; + public String group(int group) { + ensureMatch(); + int from = matchOffsets[group * 2]; + int to = matchOffsets[(group * 2) + 1]; + if (from == -1 || to == -1) { + return null; + } else { + return input.substring(from, to); + } } - + /** - * Indicates whether this matcher has transparent bounds enabled. When - * transparent bounds are enabled, the parts of the input outside the region - * are subject to lookahead and lookbehind, otherwise they are not. - * Transparent bounds are disabled by default. - * - * @return true if (and only if) the {@code Matcher} uses anchoring bounds. - * @since Android 1.0 + * Returns the text that matched the whole regular expression. + * + * @return the text. + * @throws IllegalStateException + * if no successful match has been made. */ - public boolean hasTransparentBounds() { - return transparentBounds; + public String group() { + return group(0); } - + /** - * Makes sure that a successful match has been made. Is invoked internally - * from various places in the class. - * - * @throws IllegalStateException - * if no successful match has been made. - * - * @since Android 1.0 + * Returns the next occurrence of the {@link Pattern} in the input. The + * method starts the search from the given character in the input. + * + * @param start + * The index in the input at which the find operation is to + * begin. If this is less than the start of the region, it is + * automatically adjusted to that value. If it is beyond the end + * of the region, the method will fail. + * @return true if (and only if) a match has been found. */ - private void ensureMatch() throws IllegalStateException { - if (!matchFound) { - throw new IllegalStateException("No successful match so far"); + public boolean find(int start) { + findPos = start; + + if (findPos < regionStart) { + findPos = regionStart; + } else if (findPos >= regionEnd) { + matchFound = false; + return false; } + + matchFound = NativeRegEx.find(nativePattern, findPos); + if (matchFound) { + NativeRegEx.startEnd(nativePattern, matchOffsets); + findPos = matchOffsets[1]; + } + + return matchFound; } - + /** * Returns the next occurrence of the {@link Pattern} in the input. If a * previous match was successful, the method continues the search from the @@ -389,7 +425,6 @@ public final class Matcher implements MatchResult { * either from the region start (if one has been set), or from position 0. * * @return true if (and only if) a match has been found. - * @since Android 1.0 */ public boolean find() { if (!searching) { @@ -408,34 +443,35 @@ public final class Matcher implements MatchResult { } /** - * Returns the next occurrence of the {@link Pattern} in the input. The - * method starts the search from the given character in the input. - * - * @param start - * The index in the input at which the find operation is to - * begin. If this is less than the start of the region, it is - * automatically adjusted to that value. If it is beyond the end - * of the region, the method will fail. - * @return true if (and only if) a match has been found. - * @since Android 1.0 + * Returns the index of the first character of the text that matched a given + * group. + * + * @param group + * the group, ranging from 0 to groupCount() - 1, with 0 + * representing the whole pattern. + * @return the character index. + * @throws IllegalStateException + * if no successful match has been made. */ - public boolean find(int start) { - findPos = start; - - if (findPos < regionStart) { - findPos = regionStart; - } else if (findPos >= regionEnd) { - matchFound = false; - return false; - } - - matchFound = NativeRegEx.find(nativePattern, findPos); - if (matchFound) { - NativeRegEx.startEnd(nativePattern, matchOffsets); - findPos = matchOffsets[1]; - } - - return matchFound; + public int start(int group) throws IllegalStateException { + ensureMatch(); + return matchOffsets[group * 2]; + } + + /** + * Returns the index of the first character following the text that matched + * a given group. + * + * @param group + * the group, ranging from 0 to groupCount() - 1, with 0 + * representing the whole pattern. + * @return the character index. + * @throws IllegalStateException + * if no successful match has been made. + */ + public int end(int group) { + ensureMatch(); + return matchOffsets[(group * 2) + 1]; } /** @@ -444,8 +480,6 @@ public final class Matcher implements MatchResult { * * @return true if (and only if) the {@code Pattern} matches the entire * region. - * - * @since Android 1.0 */ public boolean matches() { matchFound = NativeRegEx.matches(nativePattern, -1); @@ -458,13 +492,34 @@ public final class Matcher implements MatchResult { } /** + * Returns a replacement string for the given one that has all backslashes + * and dollar signs escaped. + * + * @param s + * the input string. + * @return the input string, with all backslashes and dollar signs having + * been escaped. + */ + public static String quoteReplacement(String s) { + StringBuffer buffer = new StringBuffer(s.length()); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\\' || c == '$') { + buffer.append('\\'); + } + buffer.append(c); + } + + return buffer.toString(); + } + + /** * Tries to match the {@link Pattern}, starting from the beginning of the * region (or the beginning of the input, if no region has been set). * Doesn't require the {@code Pattern} to match against the whole region. * * @return true if (and only if) the {@code Pattern} matches. - * - * @since Android 1.0 */ public boolean lookingAt() { matchFound = NativeRegEx.lookingAt(nativePattern, -1); @@ -483,27 +538,19 @@ public final class Matcher implements MatchResult { * @return the character index. * @throws IllegalStateException * if no successful match has been made. - * @since Android 1.0 */ - public int start() throws IllegalStateException { + public int start() { return start(0); } /** - * Returns the index of the first character of the text that matched a given - * group. - * - * @param group - * the group, ranging from 0 to groupCount() - 1, with 0 - * representing the whole pattern. - * @return the character index. - * @throws IllegalStateException - * if no successful match has been made. - * @since Android 1.0 + * Returns the number of groups in the results, which is always equal to + * the number of groups in the original regular expression. + * + * @return the number of groups. */ - public int start(int group) throws IllegalStateException { - ensureMatch(); - return matchOffsets[group * 2]; + public int groupCount() { + return pattern.mGroupCount; } /** @@ -513,255 +560,166 @@ public final class Matcher implements MatchResult { * @return the character index. * @throws IllegalStateException * if no successful match has been made. - * @since Android 1.0 */ public int end() { return end(0); } /** - * Returns the index of the first character following the text that matched - * a given group. - * - * @param group - * the group, ranging from 0 to groupCount() - 1, with 0 - * representing the whole pattern. - * @return the character index. + * Converts the current match into a separate {@link MatchResult} instance + * that is independent from this matcher. The new object is unaffected when + * the state of this matcher changes. + * + * @return the new {@code MatchResult}. * @throws IllegalStateException * if no successful match has been made. - * @since Android 1.0 */ - public int end(int group) { + public MatchResult toMatchResult() { ensureMatch(); - return matchOffsets[(group * 2) + 1]; + return new MatchResultImpl(input, matchOffsets); } /** - * Returns the text that matched the whole regular expression. - * - * @return the text. - * @throws IllegalStateException - * if no successful match has been made. - * @since Android 1.0 + * Determines whether this matcher has anchoring bounds enabled or not. When + * anchoring bounds are enabled, the start and end of the input match the + * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled + * by default. + * + * @param value + * the new value for anchoring bounds. + * @return the {@code Matcher} itself. */ - public String group() { - return group(0); + public Matcher useAnchoringBounds(boolean value) { + anchoringBounds = value; + NativeRegEx.useAnchoringBounds(nativePattern, value); + return this; } /** - * Returns the text that matched a given group of the regular expression. - * - * @param group - * the group, ranging from 0 to groupCount() - 1, with 0 - * representing the whole pattern. - * @return the text that matched the group. - * @throws IllegalStateException - * if no successful match has been made. - * @since Android 1.0 + * Indicates whether this matcher has anchoring bounds enabled. When + * anchoring bounds are enabled, the start and end of the input match the + * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled + * by default. + * + * @return true if (and only if) the {@code Matcher} uses anchoring bounds. */ - public String group(int group) { - ensureMatch(); - int from = matchOffsets[group * 2]; - int to = matchOffsets[(group * 2) + 1]; - if (from == -1 || to == -1) { - return null; - } else { - return input.substring(from, to); - } + public boolean hasAnchoringBounds() { + return anchoringBounds; } /** - * Indicates whether the last match hit the end of the input. - * - * @return true if (and only if) the last match hit the end of the input. - * @since Android 1.0 - */ - public boolean hitEnd() { - return NativeRegEx.hitEnd(nativePattern); - } - - /** - * Indicates whether more input might change a successful match into an - * unsuccessful one. - * - * @return true if (and only if) more input might change a successful match - * into an unsuccessful one. - * @since Android 1.0 + * Determines whether this matcher has transparent bounds enabled or not. + * When transparent bounds are enabled, the parts of the input outside the + * region are subject to lookahead and lookbehind, otherwise they are not. + * Transparent bounds are disabled by default. + * + * @param value + * the new value for transparent bounds. + * @return the {@code Matcher} itself. */ - public boolean requireEnd() { - return NativeRegEx.requireEnd(nativePattern); + public Matcher useTransparentBounds(boolean value) { + transparentBounds = value; + NativeRegEx.useTransparentBounds(nativePattern, value); + return this; } - + /** - * Converts the current match into a separate {@link MatchResult} instance - * that is independent from this matcher. The new object is unaffected when - * the state of this matcher changes. - * - * @return the new {@code MatchResult}. + * Makes sure that a successful match has been made. Is invoked internally + * from various places in the class. + * * @throws IllegalStateException * if no successful match has been made. - * @since Android 1.0 */ - public MatchResult toMatchResult() { - ensureMatch(); - return new MatchResultImpl(input, matchOffsets); + private void ensureMatch() { + if (!matchFound) { + throw new IllegalStateException("No successful match so far"); + } } /** - * Appends a literal part of the input plus a replacement for the current - * match to a given {@link StringBuffer}. The literal part is exactly the - * part of the input between the previous match and the current match. The - * method can be used in conjunction with {@link #find()} and - * {@link #appendTail(StringBuffer)} to walk through the input and replace - * all occurrences of the {@code Pattern} with something else. - * - * @param buffer - * the {@code StringBuffer} to append to. - * @param replacement - * the replacement text. - * @return the {@code Matcher} itself. - * @throws IllegalStateException - * if no successful match has been made. - * @since Android 1.0 + * Indicates whether this matcher has transparent bounds enabled. When + * transparent bounds are enabled, the parts of the input outside the region + * are subject to lookahead and lookbehind, otherwise they are not. + * Transparent bounds are disabled by default. + * + * @return true if (and only if) the {@code Matcher} uses anchoring bounds. */ - public Matcher appendReplacement(StringBuffer buffer, String replacement) - throws IllegalStateException { - - buffer.append(input.substring(appendPos, start())); - appendEvaluated(buffer, replacement); - appendPos = end(); - - return this; + public boolean hasTransparentBounds() { + return transparentBounds; } /** - * Appends the (unmatched) remainder of the input to the given - * {@link StringBuffer}. The method can be used in conjunction with - * {@link #find()} and {@link #appendReplacement(StringBuffer, String)} to - * walk through the input and replace all matches of the {@code Pattern} - * with something else. - * - * @param buffer - * the {@code StringBuffer} to append to. - * @return the {@code StringBuffer}. - * @throws IllegalStateException - * if no successful match has been made. - * @since Android 1.0 + * Returns this matcher's region start, that is, the first character that is + * considered for a match. + * + * @return the start of the region. */ - public StringBuffer appendTail(StringBuffer buffer) { - if (appendPos < regionEnd) { - buffer.append(input.substring(appendPos, regionEnd)); - } - - return buffer; + public int regionStart() { + return regionStart; } /** - * Internal helper method to append a given string to a given string buffer. - * If the string contains any references to groups, these are replaced by - * the corresponding group's contents. - * - * @param buffer - * the string buffer. - * @param s - * the string to append. + * Returns this matcher's region end, that is, the first character that is + * not considered for a match. + * + * @return the end of the region. */ - private void appendEvaluated(StringBuffer buffer, String s) { - boolean escape = false; - boolean dollar = false; - - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '\\' && !escape) { - escape = true; - } else if (c == '$' && !escape) { - dollar = true; - } else if (c >= '0' && c <= '9' && dollar) { - buffer.append(group(c - '0')); - dollar = false; - } else { - buffer.append(c); - dollar = false; - escape = false; - } - } - - // This seemingly stupid piece of code reproduces a JDK bug. - if (escape) { - throw new ArrayIndexOutOfBoundsException(s.length()); - } + public int regionEnd() { + return regionEnd; } - + /** - * Replaces all occurrences of this matcher's pattern in the input with a - * given string. + * Indicates whether more input might change a successful match into an + * unsuccessful one. * - * @param replacement - * the replacement text. - * @return the modified input string. - * @since Android 1.0 + * @return true if (and only if) more input might change a successful match + * into an unsuccessful one. */ - public String replaceAll(String replacement) { - StringBuffer buffer = new StringBuffer(input.length()); - - findPos = 0; - appendPos = 0; - matchFound = false; - searching = false; - - while (find()) { - appendReplacement(buffer, replacement); - } - - return appendTail(buffer).toString(); + public boolean requireEnd() { + return NativeRegEx.requireEnd(nativePattern); } /** - * Replaces the first occurrence of this matcher's pattern in the input with - * a given string. - * - * @param replacement - * the replacement text. - * @return the modified input string. - * @since Android 1.0 + * Indicates whether the last match hit the end of the input. + * + * @return true if (and only if) the last match hit the end of the input. */ - public String replaceFirst(String replacement) { - StringBuffer buffer = new StringBuffer(input.length()); - - findPos = 0; - appendPos = 0; - matchFound = false; - searching = false; - - if (find()) { - appendReplacement(buffer, replacement); - } - - return appendTail(buffer).toString(); + public boolean hitEnd() { + return NativeRegEx.hitEnd(nativePattern); } /** - * Returns a replacement string for the given one that has all backslashes - * and dollar signs escaped. - * - * @param s - * the input string. - * @return the input string, with all backslashes and dollar signs having - * been escaped. - * @since Android 1.0 + * Sets a new pattern for the {@code Matcher}. Results of a previous find + * get lost. The next attempt to find an occurrence of the {@link Pattern} + * in the string will start at the beginning of the input. + * + * @param pattern + * the new {@code Pattern}. + * + * @return the {@code Matcher} itself. */ - public static String quoteReplacement(String s) { - StringBuffer buffer = new StringBuffer(s.length()); - - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '\\' || c == '$') { - buffer.append('\\'); - } - buffer.append(c); + public Matcher usePattern(Pattern pattern) { + if (pattern == null) { + throw new IllegalArgumentException(); } - - return buffer.toString(); + + this.pattern = pattern; + + if (nativePattern != 0) { + NativeRegEx.close(nativePattern); + } + nativePattern = NativeRegEx.clone(pattern.mNativePattern); + + if (input != null) { + NativeRegEx.setText(nativePattern, input); + NativeRegEx.setRegion(nativePattern, regionStart, regionEnd); + NativeRegEx.useAnchoringBounds(nativePattern, anchoringBounds); + NativeRegEx.useTransparentBounds(nativePattern, transparentBounds); + } + + matchOffsets = new int[(this.pattern.mGroupCount + 1) * 2]; + matchFound = false; + return this; } @Override diff --git a/regex/src/main/java/java/util/regex/Pattern.java b/regex/src/main/java/java/util/regex/Pattern.java index 2c71de1..db3bc21 100644 --- a/regex/src/main/java/java/util/regex/Pattern.java +++ b/regex/src/main/java/java/util/regex/Pattern.java @@ -47,7 +47,7 @@ import com.ibm.icu4jni.regex.NativeRegEx; * boolean b2 = Pattern.matches("Hello, A[a-z]*!", "Hello, Robot!"); // false * </pre> * <p/> - * Please consult the <a href="package-summary.html">package documentation</a> for an + * Please consult the <a href="package.html">package documentation</a> for an * overview of the regular expression syntax used in this class as well as * Android-specific implementation details. * @@ -61,8 +61,6 @@ public final class Pattern implements Serializable { /** * This constant specifies that a pattern matches Unix line endings ('\n') * only against the '.', '^', and '$' meta characters. - * - * @since Android 1.0 */ public static final int UNIX_LINES = 0x01; @@ -76,8 +74,6 @@ public final class Pattern implements Serializable { * constant. So if case insensitivity is enabled, this automatically extends * to all Unicode characters. The {@code UNICODE_CASE} constant itself has * no special consequences. - * - * @since Android 1.0 */ public static final int CASE_INSENSITIVE = 0x02; @@ -85,8 +81,6 @@ public final class Pattern implements Serializable { * This constant specifies that a {@code Pattern} may contain whitespace or * comments. Otherwise comments and whitespace are taken as literal * characters. - * - * @since Android 1.0 */ public static final int COMMENTS = 0x04; @@ -94,24 +88,18 @@ public final class Pattern implements Serializable { * This constant specifies that the meta characters '^' and '$' match only * the beginning and end end of an input line, respectively. Normally, they * match the beginning and the end of the complete input. - * - * @since Android 1.0 */ public static final int MULTILINE = 0x08; /** * This constant specifies that the whole {@code Pattern} is to be taken * literally, that is, all meta characters lose their meanings. - * - * @since Android 1.0 */ public static final int LITERAL = 0x10; /** * This constant specifies that the '.' meta character matches arbitrary * characters, including line endings, which is normally not the case. - * - * @since Android 1.0 */ public static final int DOTALL = 0x20; @@ -126,8 +114,6 @@ public final class Pattern implements Serializable { * constant. So if case insensitivity is enabled, this automatically extends * to all Unicode characters. The {@code UNICODE_CASE} constant then has no * special consequences. - * - * @since Android 1.0 */ public static final int UNICODE_CASE = 0x40; @@ -135,8 +121,6 @@ public final class Pattern implements Serializable { * This constant specifies that a character in a {@code Pattern} and a * character in the input string only match if they are canonically * equivalent. It is (currently) not supported in Android. - * - * @since Android 1.0 */ public static final int CANON_EQ = 0x80; @@ -159,24 +143,144 @@ public final class Pattern implements Serializable { * Holds the number of groups in the pattern. */ transient int mGroupCount; - + + /** - * Compiles a regular expression, creating a new Pattern instance in the - * process. This is actually a convenience method that calls {@link - * #compile(String, int)} with a {@code flags} value of zero. - * - * @param pattern - * the regular expression. - * - * @return the new {@code Pattern} instance. - * - * @throws PatternSyntaxException - * if the regular expression is syntactically incorrect. - * - * @since Android 1.0 + * Returns a {@link Matcher} for the {@code Pattern} and a given input. The + * {@code Matcher} can be used to match the {@code Pattern} against the + * whole input, find occurrences of the {@code Pattern} in the input, or + * replace parts of the input. + * + * @param input + * the input to process. + * + * @return the resulting {@code Matcher}. */ - public static Pattern compile(String pattern) throws PatternSyntaxException { - return new Pattern(pattern, 0); + public Matcher matcher(CharSequence input) { + return new Matcher(this, input); + } + + /** + * Splits the given input sequence at occurrences of this {@code Pattern}. + * + * <p>If this {@code Pattern} does not occur in the input, the result is an + * array containing the input (converted from a {@code CharSequence} to + * a {@code String}). + * + * <p>Otherwise, the {@code limit} parameter controls the contents of the + * returned array as described below. + * + * @param inputSeq + * the input sequence. + * @param limit + * Determines the maximum number of entries in the resulting + * array, and the treatment of trailing empty strings. + * <ul> + * <li>For n > 0, the resulting array contains at most n + * entries. If this is fewer than the number of matches, the + * final entry will contain all remaining input. + * <li>For n < 0, the length of the resulting array is + * exactly the number of occurrences of the {@code Pattern} + * plus one for the text after the final separator. + * All entries are included. + * <li>For n == 0, the result is as for n < 0, except + * trailing empty strings will not be returned. (Note that + * the case where the input is itself an empty string is + * special, as described above, and the limit parameter does + * not apply there.) + * </ul> + * + * @return the resulting array. + */ + public String[] split(CharSequence inputSeq, int limit) { + if (inputSeq.length() == 0) { + // Unlike Perl, which considers the result of splitting the empty + // string to be the empty array, Java returns an array containing + // the empty string. + return new String[] { "" }; + } + + int maxLength = limit <= 0 ? Integer.MAX_VALUE : limit; + + String input = inputSeq.toString(); + ArrayList<String> list = new ArrayList<String>(); + + Matcher matcher = new Matcher(this, inputSeq); + int savedPos = 0; + + // Add text preceding each occurrence, if enough space. + while(matcher.find() && list.size() + 1 < maxLength) { + list.add(input.substring(savedPos, matcher.start())); + savedPos = matcher.end(); + } + + // Add trailing text if enough space. + if (list.size() < maxLength) { + if (savedPos < input.length()) { + list.add(input.substring(savedPos)); + } else { + list.add(""); + } + } + + // Remove trailing empty matches in the limit == 0 case. + if (limit == 0) { + int i = list.size() - 1; + while (i >= 0 && "".equals(list.get(i))) { + list.remove(i); + i--; + } + } + + return list.toArray(new String[list.size()]); + } + + /** + * Splits a given input around occurrences of a regular expression. This is + * a convenience method that is equivalent to calling the method + * {@link #split(java.lang.CharSequence, int)} with a limit of 0. + * + * @param input + * the input sequence. + * + * @return the resulting array. + */ + public String[] split(CharSequence input) { + return split(input, 0); + } + + /** + * Returns the regular expression that was compiled into this + * {@code Pattern}. + * + * @return the regular expression. + */ + public String pattern() { + return pattern; + } + + @Override + public String toString() { + return pattern; + } + + /** + * Returns the flags that have been set for this {@code Pattern}. + * + * @return the flags that have been set. A combination of the constants + * defined in this class. + * + * @see #CANON_EQ + * @see #CASE_INSENSITIVE + * @see #COMMENTS + * @see #DOTALL + * @see #LITERAL + * @see #MULTILINE + * @see #UNICODE_CASE + * @see #UNIX_LINES + */ + public int flags() { + return flags; } /** @@ -208,8 +312,6 @@ public final class Pattern implements Serializable { * @see #MULTILINE * @see #UNICODE_CASE * @see #UNIX_LINES - * - * @since Android 1.0 */ public static Pattern compile(String pattern, int flags) throws PatternSyntaxException { return new Pattern(pattern, flags); @@ -238,7 +340,24 @@ public final class Pattern implements Serializable { compileImpl(pattern, flags); } - + + /** + * Compiles a regular expression, creating a new Pattern instance in the + * process. This is actually a convenience method that calls {@link + * #compile(String, int)} with a {@code flags} value of zero. + * + * @param pattern + * the regular expression. + * + * @return the new {@code Pattern} instance. + * + * @throws PatternSyntaxException + * if the regular expression is syntactically incorrect. + */ + public static Pattern compile(String pattern) { + return new Pattern(pattern, 0); + } + /** * Compiles the given regular expression using the given flags. Used * internally only. @@ -266,56 +385,6 @@ public final class Pattern implements Serializable { } /** - * Returns the regular expression that was compiled into this - * {@code Pattern}. - * - * @return the regular expression. - * - * @since Android 1.0 - */ - public String pattern() { - return pattern; - } - - /** - * Returns the flags that have been set for this {@code Pattern}. - * - * @return the flags that have been set. A combination of the constants - * defined in this class. - * - * @see #CANON_EQ - * @see #CASE_INSENSITIVE - * @see #COMMENTS - * @see #DOTALL - * @see #LITERAL - * @see #MULTILINE - * @see #UNICODE_CASE - * @see #UNIX_LINES - * - * @since Android 1.0 - */ - public int flags() { - return flags; - } - - /** - * Returns a {@link Matcher} for the {@code Pattern} and a given input. The - * {@code Matcher} can be used to match the {@code Pattern} against the - * whole input, find occurrences of the {@code Pattern} in the input, or - * replace parts of the input. - * - * @param input - * the input to process. - * - * @return the resulting {@code Matcher}. - * - * @since Android 1.0 - */ - public Matcher matcher(CharSequence input) { - return new Matcher(this, input); - } - - /** * Tries to match a given regular expression against a given input. This is * actually nothing but a convenience method that compiles the regular * expression into a {@code Pattern}, builds a {@link Matcher} for it, and @@ -332,107 +401,12 @@ public final class Pattern implements Serializable { * * @see Pattern#compile(java.lang.String, int) * @see Matcher#matches() - * - * @since Android 1.0 */ - static public boolean matches(String regex, CharSequence input) { + public static boolean matches(String regex, CharSequence input) { return new Matcher(new Pattern(regex, 0), input).matches(); } /** - * Splits a given input around occurrences of a regular expression. This is - * a convenience method that is equivalent to calling the method - * {@link #split(java.lang.CharSequence, int)} with a limit of 0. - * - * @param input - * the input sequence. - * - * @return the resulting array. - * - * @since Android 1.0 - */ - public String[] split(CharSequence input) { - return split(input, 0); - } - - /** - * Splits the given input sequence at occurrences of this {@code Pattern}. - * - * If this {@code Pattern} does not occur in the input, the result is an - * array containing the input (converted from a {@code CharSequence} to - * a {@code String}). - * - * Otherwise, the {@code limit} parameter controls the contents of the - * returned array as described below. - * - * @param inputSeq - * the input sequence. - * @param limit - * Determines the maximum number of entries in the resulting - * array, and the treatment of trailing empty strings. - * <ul> - * <li>For n > 0, the resulting array contains at most n - * entries. If this is fewer than the number of matches, the - * final entry will contain all remaining input. - * <li>For n < 0, the length of the resulting array is - * exactly the number of occurrences of the {@code Pattern} - * plus one for the text after the final separator. - * All entries are included. - * <li>For n == 0, the result is as for n < 0, except - * trailing empty strings will not be returned. (Note that - * the case where the input is itself an empty string is - * special, as described above, and the limit parameter does - * not apply there.) - * </ul> - * - * @return the resulting array. - * - * @since Android 1.0 - */ - public String[] split(CharSequence inputSeq, int limit) { - if (inputSeq.length() == 0) { - // Unlike Perl, which considers the result of splitting the empty - // string to be the empty array, Java returns an array containing - // the empty string. - return new String[] { "" }; - } - - int maxLength = limit <= 0 ? Integer.MAX_VALUE : limit; - - String input = inputSeq.toString(); - ArrayList<String> list = new ArrayList<String>(); - - Matcher matcher = new Matcher(this, inputSeq); - int savedPos = 0; - - // Add text preceding each occurrence, if enough space. - while(matcher.find() && list.size() + 1 < maxLength) { - list.add(input.substring(savedPos, matcher.start())); - savedPos = matcher.end(); - } - - // Add trailing text if enough space. - if (list.size() < maxLength) { - if (savedPos < input.length()) { - list.add(input.substring(savedPos)); - } else { - list.add(""); - } - } - - // Remove trailing empty matches in the limit == 0 case. - if (limit == 0) { - int i = list.size() - 1; - while (i >= 0 && "".equals(list.get(i))) { - list.remove(i); - i--; - } - } - - return list.toArray(new String[list.size()]); - } - - /** * Quotes a given string using "\Q" and "\E", so that all other * meta-characters lose their special meaning. If the string is used for a * {@code Pattern} afterwards, it can only be matched literally. @@ -441,27 +415,20 @@ public final class Pattern implements Serializable { * the string to quote. * * @return the quoted string. - * - * @since Android 1.0 */ public static String quote(String s) { - StringBuffer sb = new StringBuffer().append("\\Q"); + StringBuilder sb = new StringBuilder().append("\\Q"); //$NON-NLS-1$ int apos = 0; int k; - while ((k = s.indexOf("\\E", apos)) >= 0) { - sb.append(s.substring(apos, k + 2)).append("\\\\E\\Q"); + while ((k = s.indexOf("\\E", apos)) >= 0) { //$NON-NLS-1$ + sb.append(s.substring(apos, k + 2)).append("\\\\E\\Q"); //$NON-NLS-1$ apos = k + 2; } - return sb.append(s.substring(apos)).append("\\E").toString(); + return sb.append(s.substring(apos)).append("\\E").toString(); //$NON-NLS-1$ } @Override - public String toString() { - return pattern; - } - - @Override protected void finalize() throws Throwable { try { if (mNativePattern != 0) { @@ -474,7 +441,7 @@ public final class Pattern implements Serializable { } /** - * Provides serialization support + * Serialization support */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { diff --git a/regex/src/main/java/java/util/regex/PatternSyntaxException.java b/regex/src/main/java/java/util/regex/PatternSyntaxException.java index e4d5abd..d59bdd4 100644 --- a/regex/src/main/java/java/util/regex/PatternSyntaxException.java +++ b/regex/src/main/java/java/util/regex/PatternSyntaxException.java @@ -1,17 +1,18 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package java.util.regex; @@ -25,24 +26,22 @@ import java.util.Arrays; * * @see Pattern#compile(String) * @see Pattern#compile(java.lang.String,int) - * - * @since Android 1.0 */ public class PatternSyntaxException extends IllegalArgumentException { private static final long serialVersionUID = -3864639126226059218L; - - /** - * Holds the syntactically incorrect regular expression, or null if the - * regular expression is not known. - */ - private String pattern; /** * Holds the description of the syntax error, or null if the description is * not known. */ - private String description; + private String desc; + + /** + * Holds the syntactically incorrect regular expression, or null if the + * regular expression is not known. + */ + private String pattern; /** * Holds the index around which the error occured, or -1, in case it is @@ -63,11 +62,10 @@ public class PatternSyntaxException extends IllegalArgumentException { * @param index * the character index around which the error occurred, or -1 if * the index is not known. - * @since Android 1.0 */ public PatternSyntaxException(String description, String pattern, int index) { + this.desc = description; this.pattern = pattern; - this.description = description; this.index = index; } @@ -76,7 +74,6 @@ public class PatternSyntaxException extends IllegalArgumentException { * * @return the regular expression. * - * @since Android 1.0 */ public String getPattern() { return pattern; @@ -88,16 +85,15 @@ public class PatternSyntaxException extends IllegalArgumentException { * original regular expression, and the index at which the error occured. * * @return the error message. - * - * @since Android 1.0 */ @Override public String getMessage() { + // BEGIN android-changed StringBuilder builder = new StringBuilder("Syntax error"); - if (description != null) { + if (desc != null) { builder.append(' '); - builder.append(description); + builder.append(desc); } if (index >= 0) { @@ -118,6 +114,7 @@ public class PatternSyntaxException extends IllegalArgumentException { } return builder.toString(); + // END android-changed } /** @@ -125,10 +122,9 @@ public class PatternSyntaxException extends IllegalArgumentException { * description is not known. * * @return the description. - * @since Android 1.0 */ public String getDescription() { - return description; + return desc; } /** @@ -137,10 +133,8 @@ public class PatternSyntaxException extends IllegalArgumentException { * * @return the index. * - * @since Android 1.0 */ public int getIndex() { return index; } - } |