Add StrMatcher and update StrBuilder and test cases to use it, plus fix other bugs

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@232652 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2005-08-14 21:45:47 +00:00
parent 512574a908
commit 810e69f7a4
6 changed files with 2990 additions and 1435 deletions

View File

@ -242,6 +242,8 @@ public void clear() {
/**
* Gets the character at the specified index.
*
* @see #setCharAt(int, char)
* @see #deleteCharAt(int)
* @param index the index to retrieve, must be valid
* @return the character at the index
* @throws IndexOutOfBoundsException if the index is invalid
@ -256,6 +258,8 @@ public char charAt(int index) {
/**
* Sets the character at the specified index.
*
* @see #charAt(int)
* @see #deleteCharAt(int)
* @param index the index to set
* @param ch the new character
* @throws IndexOutOfBoundsException if the index is invalid
@ -267,6 +271,23 @@ public void setCharAt(int index, char ch) {
buffer[index] = ch;
}
/**
* Deletes the character at the specified index.
*
* @see #charAt(int)
* @see #setCharAt(int, char)
* @param index the index to delete
* @return this, to enable chaining
* @throws IndexOutOfBoundsException if the index is invalid
*/
public StrBuilder deleteCharAt(int index) {
if (index < 0 || index >= size) {
throw new StringIndexOutOfBoundsException(index);
}
deleteImpl(index, index + 1, 1);
return this;
}
//-----------------------------------------------------------------------
/**
* Copies the builder's character array into a new character array.
@ -1024,6 +1045,19 @@ public StrBuilder insert(int index, double value) {
}
//-----------------------------------------------------------------------
/**
* Internal method to delete a range without validation.
*
* @param startIndex the start index, must be valid
* @param endIndex the end index (exclusive), must be valid
* @param len the length, must be valid
* @throws IndexOutOfBoundsException if any index is invalid
*/
private void deleteImpl(int startIndex, int endIndex, int len) {
System.arraycopy(buffer, endIndex, buffer, startIndex, size - endIndex);
size -= len;
}
/**
* Deletes the characters between the two specified indices.
*
@ -1037,35 +1071,19 @@ public StrBuilder delete(int startIndex, int endIndex) {
endIndex = validateRange(startIndex, endIndex);
int len = endIndex - startIndex;
if (len > 0) {
System.arraycopy(buffer, endIndex, buffer, startIndex, size - endIndex);
size -= len;
deleteImpl(startIndex, endIndex, len);
}
return this;
}
/**
* Deletes the character at the specified index.
*
* @param index the index to delete
* @return this, to enable chaining
* @throws IndexOutOfBoundsException if the index is invalid
*/
public StrBuilder deleteCharAt(int index) {
if (index < 0 || index >= size) {
throw new StringIndexOutOfBoundsException(index);
}
System.arraycopy(buffer, index + 1, buffer, index, size - index - 1);
size--;
return this;
}
//-----------------------------------------------------------------------
/**
* Deletes the character wherever it occurs in the builder.
*
*
* @param ch the character to delete
* @return this, to enable chaining
*/
public StrBuilder delete(char ch) {
public StrBuilder deleteAll(char ch) {
for (int i = 0; i < size; i++) {
if (buffer[i] == ch) {
int start = i;
@ -1074,26 +1092,25 @@ public StrBuilder delete(char ch) {
break;
}
}
System.arraycopy(buffer, i, buffer, start, size - i);
size -= (i - start);
int len = i - start;
deleteImpl(start, i, len);
i -= len;
}
}
return this;
}
/**
* Deletes the string wherever it occurs in the builder.
*
* @param str the string to delete, null causes no action
* Deletes the character wherever it occurs in the builder.
*
* @param ch the character to delete
* @return this, to enable chaining
*/
public StrBuilder delete(String str) {
int len = (str == null ? 0 : str.length());
if (len > 0) {
int index = indexOf(str, 0);
while (index >= 0) {
delete(index, index + len);
index = indexOf(str, index);
public StrBuilder deleteFirst(char ch) {
for (int i = 0; i < size; i++) {
if (buffer[i] == ch) {
deleteImpl(i, i + 1, 1);
break;
}
}
return this;
@ -1101,68 +1118,121 @@ public StrBuilder delete(String str) {
//-----------------------------------------------------------------------
/**
* Replaces a portion of the string builder with another string.
* The length of the inserted string does not have to match the removed length.
*
* @param startIndex the start index, inclusive, must be valid
* @param endIndex the end index, exclusive, must be valid except
* that if too large it is treated as end of string
* @param str the string to replace with
* Deletes the string wherever it occurs in the builder.
*
* @param str the string to delete, null causes no action
* @return this, to enable chaining
* @throws IndexOutOfBoundsException if the index is invalid
*/
public StrBuilder replace(int startIndex, int endIndex, String str) {
endIndex = validateRange(startIndex, endIndex);
int insertLen = str.length();
int removeLen = endIndex - startIndex;
int newSize = size - removeLen + insertLen;
if (insertLen > removeLen) {
ensureCapacity(newSize);
public StrBuilder deleteAll(String str) {
int len = (str == null ? 0 : str.length());
if (len > 0) {
int index = indexOf(str, 0);
while (index >= 0) {
deleteImpl(index, index + len, len);
index = indexOf(str, index);
}
}
return this;
}
/**
* Deletes the string wherever it occurs in the builder.
*
* @param str the string to delete, null causes no action
* @return this, to enable chaining
*/
public StrBuilder deleteFirst(String str) {
int len = (str == null ? 0 : str.length());
if (len > 0) {
int index = indexOf(str, 0);
if (index >= 0) {
deleteImpl(index, index + len, len);
}
}
return this;
}
//-----------------------------------------------------------------------
/**
* Deletes all parts of the builder that the matcher matches.
* <p>
* Matchers can be used to perform advanced deletion behaviour.
* For example you could write a matcher to delete all occurances
* where the character 'a' is followed by a number.
*
* @param matcher the matcher to use to find the deletion, null causes no action
* @return this, to enable chaining
*/
public StrBuilder deleteAll(StrMatcher matcher) {
return replace(matcher, null, 0, size, -1);
}
/**
* Deletes the first match within the builder using the specified matcher.
* <p>
* Matchers can be used to perform advanced deletion behaviour.
* For example you could write a matcher to delete
* where the character 'a' is followed by a number.
*
* @param matcher the matcher to use to find the deletion, null causes no action
* @return this, to enable chaining
*/
public StrBuilder deleteFirst(StrMatcher matcher) {
return replace(matcher, null, 0, size, 1);
}
//-----------------------------------------------------------------------
/**
* Internal method to delete a range without validation.
*
* @param startIndex the start index, must be valid
* @param endIndex the end index (exclusive), must be valid
* @param removeLen the length to remove (endIndex - startIndex), must be valid
* @param insertStr the string to replace with, null means delete range
* @param insertLen the length of the insert string, must be valid
* @param len the length, must be valid
* @throws IndexOutOfBoundsException if any index is invalid
*/
private void replaceImpl(int startIndex, int endIndex, int removeLen, String insertStr, int insertLen) {
int newSize = size - removeLen + insertLen;
if (insertLen != removeLen) {
ensureCapacity(newSize);
System.arraycopy(buffer, endIndex, buffer, startIndex + insertLen, size - endIndex);
size = newSize;
}
str.getChars(0, insertLen, buffer, startIndex);
return this;
if (insertLen > 0) {
insertStr.getChars(0, insertLen, buffer, startIndex);
}
}
/**
* Replaces a portion of the string builder with another string builder.
* Replaces a portion of the string builder with another string.
* The length of the inserted string does not have to match the removed length.
*
*
* @param startIndex the start index, inclusive, must be valid
* @param endIndex the end index, exclusive, must be valid except
* that if too large it is treated as end of string
* @param builder the string builder to replace with
* @param replaceStr the string to replace with, null means delete range
* @return this, to enable chaining
* @throws IndexOutOfBoundsException if the index is invalid
*/
public StrBuilder replace(int startIndex, int endIndex, StrBuilder builder) {
public StrBuilder replace(int startIndex, int endIndex, String replaceStr) {
endIndex = validateRange(startIndex, endIndex);
int insertLen = builder.length();
int removeLen = endIndex - startIndex;
if (insertLen > removeLen) {
ensureCapacity(size - removeLen + insertLen);
}
if (insertLen != removeLen) {
//shift the current characters to the right
System.arraycopy(buffer, endIndex, buffer, startIndex + insertLen, size - endIndex);
//adjust the size accordingly
size += (insertLen - removeLen);
}
builder.getChars(0, insertLen, buffer, startIndex);
int insertLen = (replaceStr == null ? 0 : replaceStr.length());
replaceImpl(startIndex, endIndex, endIndex - startIndex, replaceStr, insertLen);
return this;
}
//-----------------------------------------------------------------------
/**
* Replaces the search character with the replace character throughout the builder.
*
* @param search the search string, null causes no action to occur
* @param replace the replace string, null is equivalent to an empty string
* Replaces the search character with the replace character
* throughout the builder.
*
* @param search the search character
* @param replace the replace character
* @return this, to enable chaining
*/
public StrBuilder replace(char search, char replace) {
public StrBuilder replaceAll(char search, char replace) {
if (search != replace) {
for (int i = 0; i < size; i++) {
if (buffer[i] == search) {
@ -1173,21 +1243,153 @@ public StrBuilder replace(char search, char replace) {
return this;
}
/**
* Replaces the first instance of the search character with the
* replace character in the builder.
*
* @param search the search character
* @param replace the replace character
* @return this, to enable chaining
*/
public StrBuilder replaceFirst(char search, char replace) {
if (search != replace) {
for (int i = 0; i < size; i++) {
if (buffer[i] == search) {
buffer[i] = replace;
break;
}
}
}
return this;
}
//-----------------------------------------------------------------------
/**
* Replaces the search string with the replace string throughout the builder.
*
*
* @param searchStr the search string, null causes no action to occur
* @param replaceStr the replace string, null is equivalent to an empty string
* @return this, to enable chaining
*/
public StrBuilder replace(String searchStr, String replaceStr) {
public StrBuilder replaceAll(String searchStr, String replaceStr) {
int searchLen = (searchStr == null ? 0 : searchStr.length());
if (searchLen > 0) {
replaceStr = (replaceStr == null ? "" : replaceStr);
int replaceLen = (replaceStr == null ? 0 : replaceStr.length());
int index = indexOf(searchStr, 0);
while (index >= 0) {
replace(index, index + searchLen, replaceStr);
index = indexOf(searchStr, index);
replaceImpl(index, index + searchLen, searchLen, replaceStr, replaceLen);
index = indexOf(searchStr, index + replaceLen);
}
}
return this;
}
/**
* Replaces the first instance of the search string with the replace string.
*
* @param searchStr the search string, null causes no action to occur
* @param replaceStr the replace string, null is equivalent to an empty string
* @return this, to enable chaining
*/
public StrBuilder replaceFirst(String searchStr, String replaceStr) {
int searchLen = (searchStr == null ? 0 : searchStr.length());
if (searchLen > 0) {
int index = indexOf(searchStr, 0);
if (index >= 0) {
int replaceLen = (replaceStr == null ? 0 : replaceStr.length());
replaceImpl(index, index + searchLen, searchLen, replaceStr, replaceLen);
}
}
return this;
}
//-----------------------------------------------------------------------
/**
* Replaces all matches within the builder with the replace string.
* <p>
* Matchers can be used to perform advanced replace behaviour.
* For example you could write a matcher to replace all occurances
* where the character 'a' is followed by a number.
*
* @param matcher the matcher to use to find the deletion, null causes no action
* @param replaceStr the replace string, null is equivalent to an empty string
* @return this, to enable chaining
*/
public StrBuilder replaceAll(StrMatcher matcher, String replaceStr) {
return replace(matcher, replaceStr, 0, size, -1);
}
/**
* Replaces the first match within the builder with the replace string.
* <p>
* Matchers can be used to perform advanced replace behaviour.
* For example you could write a matcher to replace
* where the character 'a' is followed by a number.
*
* @param matcher the matcher to use to find the deletion, null causes no action
* @param replaceStr the replace string, null is equivalent to an empty string
* @return this, to enable chaining
*/
public StrBuilder replaceFirst(StrMatcher matcher, String replaceStr) {
return replace(matcher, replaceStr, 0, size, 1);
}
// -----------------------------------------------------------------------
/**
* Advanced search and replaces within the builder using a matcher.
* <p>
* Matchers can be used to perform advanced behaviour.
* For example you could write a matcher to delete all occurances
* where the character 'a' is followed by a number.
*
* @param matcher the matcher to use to find the deletion, null causes no action
* @param replaceStr the string to replace the match with, null is a delete
* @param startIndex the start index, inclusive, must be valid
* @param endIndex the end index, exclusive, must be valid except
* that if too large it is treated as end of string
* @param replaceCount the number of times to replace, -1 for replace all
* @return this, to enable chaining
* @throws IndexOutOfBoundsException if start index is invalid
*/
public StrBuilder replace(
StrMatcher matcher, String replaceStr,
int startIndex, int endIndex, int replaceCount) {
endIndex = validateRange(startIndex, endIndex);
return replaceImpl(matcher, replaceStr, startIndex, endIndex, replaceCount);
}
/**
* Replaces within the builder using a matcher.
* <p>
* Matchers can be used to perform advanced behaviour.
* For example you could write a matcher to delete all occurances
* where the character 'a' is followed by a number.
*
* @param matcher the matcher to use to find the deletion, null causes no action
* @param replaceStr the string to replace the match with, null is a delete
* @param from the start index, must be valid
* @param to the end index (exclusive), must be valid
* @param replaceCount the number of times to replace, -1 for replace all
* @return this, to enable chaining
* @throws IndexOutOfBoundsException if any index is invalid
*/
private StrBuilder replaceImpl(
StrMatcher matcher, String replaceStr,
int from, int to, int replaceCount) {
if (matcher == null || size == 0) {
return this;
}
int replaceLen = (replaceStr == null ? 0 : replaceStr.length());
char[] buf = buffer;
for (int i = from; i < to && replaceCount != 0; i++) {
int removeLen = matcher.isMatch(buf, i, from, to);
if (removeLen > 0) {
replaceImpl(i, i + removeLen, removeLen, replaceStr, replaceLen);
to = to - removeLen + replaceLen;
i = i + replaceLen - 1;
if (replaceCount > 0) {
replaceCount--;
}
}
}
return this;
@ -1375,8 +1577,8 @@ public String midString(int index, int length) {
//-----------------------------------------------------------------------
/**
* Checks of the string builder contains the specified char.
*
* Checks if the string builder contains the specified char.
*
* @param ch the character to find
* @return true if the builder contains the character
*/
@ -1391,8 +1593,8 @@ public boolean contains(char ch) {
}
/**
* Checks of the string builder contains the specified string.
*
* Checks if the string builder contains the specified string.
*
* @param str the string to find
* @return true if the builder contains the string
*/
@ -1400,6 +1602,21 @@ public boolean contains(String str) {
return indexOf(str, 0) >= 0;
}
/**
* Checks if the string builder contains a string matched using the
* specified matcher.
* <p>
* Matchers can be used to perform advanced searching behaviour.
* For example you could write a matcher to search for the character
* 'a' followed by a number.
*
* @param matcher the matcher to use, null returns -1
* @return true if the matcher finds a match in the builder
*/
public boolean contains(StrMatcher matcher) {
return indexOf(matcher, 0) >= 0;
}
//-----------------------------------------------------------------------
/**
* Searches the string builder to find the first reference to the specified char.
@ -1415,7 +1632,7 @@ public int indexOf(char ch) {
* Searches the string builder to find the first reference to the specified char.
*
* @param ch the character to find
* @param startIndex the index to start at, must be valid
* @param startIndex the index to start at, invalid index rounded to edge
* @return the first index of the character, or -1 if not found
*/
public int indexOf(char ch, int startIndex) {
@ -1451,7 +1668,7 @@ public int indexOf(String str) {
* Note that a null input string will return -1, whereas the JDK throws an exception.
*
* @param str the string to find, null returns -1
* @param startIndex the index to start at, must be valid
* @param startIndex the index to start at, invalid index rounded to edge
* @return the first index of the string, or -1 if not found
*/
public int indexOf(String str, int startIndex) {
@ -1481,6 +1698,49 @@ public int indexOf(String str, int startIndex) {
return -1;
}
/**
* Searches the string builder using the matcher to find the first match.
* <p>
* Matchers can be used to perform advanced searching behaviour.
* For example you could write a matcher to find the character 'a'
* followed by a number.
*
* @param matcher the matcher to use, null returns -1
* @return the first index matched, or -1 if not found
*/
public int indexOf(StrMatcher matcher) {
return indexOf(matcher, 0);
}
/**
* Searches the string builder using the matcher to find the first
* match searching from the given index.
* <p>
* Matchers can be used to perform advanced searching behaviour.
* For example you could write a matcher to find the character 'a'
* followed by a number.
*
* @param matcher the matcher to use, null returns -1
* @param startIndex the index to start at, invalid index rounded to edge
* @return the first index matched, or -1 if not found
*/
public int indexOf(StrMatcher matcher, int startIndex) {
startIndex = (startIndex < 0 ? 0 : startIndex);
if (matcher == null || startIndex >= size) {
return -1;
}
int len = size;
if (len > 0) {
char[] buf = buffer;
for (int i = startIndex; i < len; i++) {
if (matcher.isMatch(buf, i, startIndex, len) > 0) {
return i;
}
}
}
return -1;
}
//-----------------------------------------------------------------------
/**
* Searches the string builder to find the last reference to the specified char.
@ -1561,6 +1821,50 @@ public int lastIndexOf(String str, int startIndex) {
return -1;
}
/**
* Searches the string builder using the matcher to find the last match.
* <p>
* Matchers can be used to perform advanced searching behaviour.
* For example you could write a matcher to find the character 'a'
* followed by a number.
*
* @param matcher the matcher to use, null returns -1
* @return the last index matched, or -1 if not found
*/
public int lastIndexOf(StrMatcher matcher) {
return lastIndexOf(matcher, size);
}
/**
* Searches the string builder using the matcher to find the last
* match searching from the given index.
* <p>
* Matchers can be used to perform advanced searching behaviour.
* For example you could write a matcher to find the character 'a'
* followed by a number.
*
* @param matcher the matcher to use, null returns -1
* @param startIndex the index to start at, invalid index rounded to edge
* @return the last index matched, or -1 if not found
*/
public int lastIndexOf(StrMatcher matcher, int startIndex) {
startIndex = (startIndex >= size ? size - 1 : startIndex);
if (matcher == null || startIndex < 0) {
return -1;
}
int len = size;
if (len > 0) {
char[] buf = buffer;
int endIndex = startIndex + 1;
for (int i = startIndex; i >= 0; i--) {
if (matcher.isMatch(buf, i, 0, endIndex) > 0) {
return i;
}
}
}
return -1;
}
//-----------------------------------------------------------------------
/**
* Gets the contents of this builder as a Reader.

View File

@ -0,0 +1,405 @@
/*
* Copyright 2003-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang.text;
import java.util.Arrays;
/**
* A matcher class that can be queried to determine if a character array
* portion matches.
* <p>
* This class comes complete with various constants and factory methods.
* If these do not suffice, you can subclass and implement your own matcher.
*
* @author Stephen Colebourne
* @since 2.2
* @version $Id$
*/
public abstract class StrMatcher {
/**
* Matches the comma character.
*/
private static final StrMatcher COMMA_MATCHER = new CharMatcher(',');
/**
* Matches the tab character.
*/
private static final StrMatcher TAB_MATCHER = new CharMatcher('\t');
/**
* Matches the space character.
*/
private static final StrMatcher SPACE_MATCHER = new CharMatcher(' ');
/**
* Matches the same characters as StringTokenizer,
* namely space, tab, newline, formfeed.
*/
private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray());
/**
* Matches the String trim() whitespace characters.
*/
private static final StrMatcher TRIM_MATCHER = new TrimMatcher();
/**
* Matches the double quote character.
*/
private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher('\'');
/**
* Matches the double quote character.
*/
private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher('"');
/**
* Matches the single or double quote character.
*/
private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray());
/**
* Matches no characters.
*/
private static final StrMatcher NONE_MATCHER = new NoMatcher();
// -----------------------------------------------------------------------
/**
* Returns a matcher which matches the comma character.
*
* @return a matcher for a comma
*/
public static StrMatcher commaMatcher() {
return COMMA_MATCHER;
}
/**
* Returns a matcher which matches the tab character.
*
* @return a matcher for a tab
*/
public static StrMatcher tabMatcher() {
return TAB_MATCHER;
}
/**
* Returns a matcher which matches the space character.
*
* @return a matcher for a space
*/
public static StrMatcher spaceMatcher() {
return SPACE_MATCHER;
}
/**
* Matches the same characters as StringTokenizer,
* namely space, tab, newline and formfeed.
*
* @return the split matcher
*/
public static StrMatcher splitMatcher() {
return SPLIT_MATCHER;
}
/**
* Matches the String trim() whitespace characters.
*
* @return the trim matcher
*/
public static StrMatcher trimMatcher() {
return TRIM_MATCHER;
}
/**
* Returns a matcher which matches the single quote character.
*
* @return a matcher for a single quote
*/
public static StrMatcher singleQuoteMatcher() {
return SINGLE_QUOTE_MATCHER;
}
/**
* Returns a matcher which matches the double quote character.
*
* @return a matcher for a double quote
*/
public static StrMatcher doubleQuoteMatcher() {
return DOUBLE_QUOTE_MATCHER;
}
/**
* Returns a matcher which matches the single or double quote character.
*
* @return a matcher for a single or double quote
*/
public static StrMatcher quoteMatcher() {
return QUOTE_MATCHER;
}
/**
* Matches no characters.
*
* @return a matcher that matches nothing
*/
public static StrMatcher noneMatcher() {
return NONE_MATCHER;
}
/**
* Constructor that creates a matcher from a character.
*
* @param ch the character to match, must not be null
* @return a new Matcher for the given char
*/
public static StrMatcher charMatcher(char ch) {
return new CharMatcher(ch);
}
/**
* Constructor that creates a matcher from a set of characters.
*
* @param chars the characters to match, null or empty matches nothing
* @return a new matcher for the given char[]
*/
public static StrMatcher charSetMatcher(char[] chars) {
if (chars == null || chars.length == 0) {
return NONE_MATCHER;
}
if (chars.length == 1) {
return new CharMatcher(chars[0]);
}
return new CharSetMatcher(chars);
}
/**
* Constructor that creates a matcher from a string representing a set of characters.
*
* @param chars the characters to match, null or empty matches nothing
* @return a new Matcher for the given characters
*/
public static StrMatcher charSetMatcher(String chars) {
if (chars == null || chars.length() == 0) {
return NONE_MATCHER;
}
if (chars.length() == 1) {
return new CharMatcher(chars.charAt(0));
}
return new CharSetMatcher(chars.toCharArray());
}
/**
* Constructor that creates a matcher from a string.
*
* @param str the string to match, null or empty matches nothing
* @return a new Matcher for the given String
*/
public static StrMatcher stringMatcher(String str) {
if (str == null || str.length() == 0) {
return NONE_MATCHER;
}
return new StringMatcher(str);
}
//-----------------------------------------------------------------------
/**
* Constructor.
*/
protected StrMatcher() {
super();
}
/**
* Returns the number of matching characters, zero for no match.
* <p>
* This method is called to check for a match.
* The parameter <code>pos</code> represents the current position to be
* checked in the string <code>buffer</code> (a character array which must
* not be changed).
* The API guarantees that <code>pos</code> is a valid index for <code>buffer</code>.
* <p>
* The character array may be larger than the active area to be matched.
* Only values in the buffer between the specifed indices may be accessed.
* <p>
* The matching code may check one character or many.
* It may check characters preceeding <code>pos</code> as well as those
* after, so long as no checks exceed the bounds specified.
* <p>
* It must return zero for no match, or a positive number if a match was found.
* The number indicates the number of characters that matched.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index (exclusive) of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
public abstract int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd);
//-----------------------------------------------------------------------
/**
* Class used to define a set of characters for matching purposes.
*/
static final class CharSetMatcher extends StrMatcher {
/** The set of characters to match. */
private char[] chars;
/**
* Constructor that creates a matcher from a character array.
*
* @param chars the characters to match, must not be null
*/
CharSetMatcher(char chars[]) {
super();
this.chars = (char[]) chars.clone();
Arrays.sort(this.chars);
}
/**
* Returns whether or not the given charatcer matches.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) {
return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0;
}
}
//-----------------------------------------------------------------------
/**
* Class used to define a character for matching purposes.
*/
static final class CharMatcher extends StrMatcher {
/** The character to match. */
private char ch;
/**
* Constructor that creates a matcher that matches a single character.
*
* @param ch the character to match
*/
CharMatcher(char ch) {
super();
this.ch = ch;
}
/**
* Returns whether or not the given character matches.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) {
return ch == buffer[pos] ? 1 : 0;
}
}
//-----------------------------------------------------------------------
/**
* Class used to define a set of characters for matching purposes.
*/
static final class StringMatcher extends StrMatcher {
/** The string to match, as a character array. */
private char[] chars;
/**
* Constructor that creates a matcher from a String.
*
* @param str the string to match, must not be null
*/
StringMatcher(String str) {
super();
chars = str.toCharArray();
}
/**
* Returns whether or not the given text matches the stored string.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) {
int len = chars.length;
if (pos + len > bufferEnd) {
return 0;
}
for (int i = 0; i < chars.length; i++, pos++) {
if (chars[i] != buffer[pos]) {
return 0;
}
}
return len;
}
}
//-----------------------------------------------------------------------
/**
* Class used to match no characters.
*/
static final class NoMatcher extends StrMatcher {
/**
* Constructs a new instance of <code>NoMatcher</code>.
*/
NoMatcher() {
super();
}
/**
* Always returns <code>false</code>.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) {
return 0;
}
}
//-----------------------------------------------------------------------
/**
* Class used to match whitespace as per trim().
*/
static final class TrimMatcher extends StrMatcher {
/**
* Constructs a new instance of <code>TrimMatcher</code>.
*/
TrimMatcher() {
super();
}
/**
* Returns whether or not the given charatcer matches.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) {
return buffer[pos] <= 32 ? 1 : 0;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,227 @@
/*
* Copyright 2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang.text;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
/**
* Unit tests for {@link org.apache.commons.lang.text.StrMatcher}.
*
* @version $Id$
*/
public class StrMatcherTest extends TestCase {
private static final char[] BUFFER1 = "0,1\t2 3\n\r\f\u0000'\"".toCharArray();
private static final char[] BUFFER2 = "abcdef".toCharArray();
/**
* Main method.
*
* @param args command line arguments, ignored
*/
public static void main(String[] args) {
TestRunner.run(suite());
}
/**
* Return a new test suite containing this test case.
*
* @return a new test suite containing this test case
*/
public static Test suite() {
TestSuite suite = new TestSuite(StrMatcherTest.class);
suite.setName("StrMatcher Tests");
return suite;
}
/**
* Create a new test case with the specified name.
*
* @param name the name
*/
public StrMatcherTest(String name) {
super(name);
}
//-----------------------------------------------------------------------
public void testCommaMatcher() {
StrMatcher matcher = StrMatcher.commaMatcher();
assertSame(matcher, StrMatcher.commaMatcher());
assertEquals(0, matcher.isMatch(BUFFER1, 0, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 1, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 2, 0, BUFFER1.length));
}
//-----------------------------------------------------------------------
public void testTabMatcher() {
StrMatcher matcher = StrMatcher.tabMatcher();
assertSame(matcher, StrMatcher.tabMatcher());
assertEquals(0, matcher.isMatch(BUFFER1, 2, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 3, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 4, 0, BUFFER1.length));
}
//-----------------------------------------------------------------------
public void testSpaceMatcher() {
StrMatcher matcher = StrMatcher.spaceMatcher();
assertSame(matcher, StrMatcher.spaceMatcher());
assertEquals(0, matcher.isMatch(BUFFER1, 4, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 5, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 6, 0, BUFFER1.length));
}
//-----------------------------------------------------------------------
public void testSplitMatcher() {
StrMatcher matcher = StrMatcher.splitMatcher();
assertSame(matcher, StrMatcher.splitMatcher());
assertEquals(0, matcher.isMatch(BUFFER1, 2, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 3, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 4, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 5, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 6, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 7, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 8, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 9, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 10, 0, BUFFER1.length));
}
//-----------------------------------------------------------------------
public void testTrimMatcher() {
StrMatcher matcher = StrMatcher.trimMatcher();
assertSame(matcher, StrMatcher.trimMatcher());
assertEquals(0, matcher.isMatch(BUFFER1, 2, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 3, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 4, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 5, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 6, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 7, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 8, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 9, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 10, 0, BUFFER1.length));
}
//-----------------------------------------------------------------------
public void testSingleQuoteMatcher() {
StrMatcher matcher = StrMatcher.singleQuoteMatcher();
assertSame(matcher, StrMatcher.singleQuoteMatcher());
assertEquals(0, matcher.isMatch(BUFFER1, 10, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 11, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 12, 0, BUFFER1.length));
}
//-----------------------------------------------------------------------
public void testDoubleQuoteMatcher() {
StrMatcher matcher = StrMatcher.doubleQuoteMatcher();
assertSame(matcher, StrMatcher.doubleQuoteMatcher());
assertEquals(0, matcher.isMatch(BUFFER1, 11, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 12, 0, BUFFER1.length));
}
//-----------------------------------------------------------------------
public void testQuoteMatcher() {
StrMatcher matcher = StrMatcher.quoteMatcher();
assertSame(matcher, StrMatcher.quoteMatcher());
assertEquals(0, matcher.isMatch(BUFFER1, 10, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 11, 0, BUFFER1.length));
assertEquals(1, matcher.isMatch(BUFFER1, 12, 0, BUFFER1.length));
}
//-----------------------------------------------------------------------
public void testNoneMatcher() {
StrMatcher matcher = StrMatcher.noneMatcher();
assertSame(matcher, StrMatcher.noneMatcher());
assertEquals(0, matcher.isMatch(BUFFER1, 0, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 1, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 2, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 3, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 4, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 5, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 6, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 7, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 8, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 9, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 10, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 11, 0, BUFFER1.length));
assertEquals(0, matcher.isMatch(BUFFER1, 12, 0, BUFFER1.length));
}
//-----------------------------------------------------------------------
public void testCharMatcher_char() {
StrMatcher matcher = StrMatcher.charMatcher('c');
assertEquals(0, matcher.isMatch(BUFFER2, 0, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 1, 0, BUFFER2.length));
assertEquals(1, matcher.isMatch(BUFFER2, 2, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 3, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 4, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 5, 0, BUFFER2.length));
}
//-----------------------------------------------------------------------
public void testCharSetMatcher_String() {
StrMatcher matcher = StrMatcher.charSetMatcher("ace");
assertEquals(1, matcher.isMatch(BUFFER2, 0, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 1, 0, BUFFER2.length));
assertEquals(1, matcher.isMatch(BUFFER2, 2, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 3, 0, BUFFER2.length));
assertEquals(1, matcher.isMatch(BUFFER2, 4, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 5, 0, BUFFER2.length));
assertSame(StrMatcher.noneMatcher(), StrMatcher.charSetMatcher(""));
assertSame(StrMatcher.noneMatcher(), StrMatcher.charSetMatcher((String) null));
}
//-----------------------------------------------------------------------
public void testCharSetMatcher_charArray() {
StrMatcher matcher = StrMatcher.charSetMatcher("ace".toCharArray());
assertEquals(1, matcher.isMatch(BUFFER2, 0, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 1, 0, BUFFER2.length));
assertEquals(1, matcher.isMatch(BUFFER2, 2, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 3, 0, BUFFER2.length));
assertEquals(1, matcher.isMatch(BUFFER2, 4, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 5, 0, BUFFER2.length));
assertSame(StrMatcher.noneMatcher(), StrMatcher.charSetMatcher(new char[0]));
assertSame(StrMatcher.noneMatcher(), StrMatcher.charSetMatcher((char[]) null));
}
//-----------------------------------------------------------------------
public void testStringMatcher_String() {
StrMatcher matcher = StrMatcher.stringMatcher("bc");
assertEquals(0, matcher.isMatch(BUFFER2, 0, 0, BUFFER2.length));
assertEquals(2, matcher.isMatch(BUFFER2, 1, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 2, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 3, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 4, 0, BUFFER2.length));
assertEquals(0, matcher.isMatch(BUFFER2, 5, 0, BUFFER2.length));
assertSame(StrMatcher.noneMatcher(), StrMatcher.stringMatcher(""));
assertSame(StrMatcher.noneMatcher(), StrMatcher.stringMatcher((String) null));
}
//-----------------------------------------------------------------------
public void testMatcherIndices() {
// remember that the API contract is tight for the isMatch() method
// all the onus is on the caller, so invalid inputs are not
// the concern of StrMatcher, and are not bugs
StrMatcher matcher = StrMatcher.stringMatcher("bc");
assertEquals(2, matcher.isMatch(BUFFER2, 1, 1, BUFFER2.length));
assertEquals(2, matcher.isMatch(BUFFER2, 1, 0, 3));
assertEquals(0, matcher.isMatch(BUFFER2, 1, 0, 2));
}
}

View File

@ -49,6 +49,8 @@ public static Test suite() {
TestSuite suite = new TestSuite();
suite.setName("Commons-Lang-Text Tests");
suite.addTest(StrBuilderTest.suite());
suite.addTest(StrBuilderAppendInsertTest.suite());
suite.addTest(StrMatcherTest.suite());
suite.addTest(StrTokenizerTest.suite());
suite.addTestSuite(VariableFormatterTest.class);
return suite;