452 lines
14 KiB
Java
452 lines
14 KiB
Java
/*
|
|
* 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
|
|
*
|
|
* 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.text.matcher;
|
|
|
|
import java.util.Arrays;
|
|
|
|
/**
|
|
* A matcher that determines if a character array portion matches.
|
|
* <p>
|
|
* Thread=safe.
|
|
* </p>
|
|
*
|
|
* @since 1.3
|
|
*/
|
|
abstract class AbstractStringMatcher implements StringMatcher {
|
|
|
|
/**
|
|
* Matches all of the given matchers in order.
|
|
*
|
|
* @since 1.9
|
|
*/
|
|
static final class AndStringMatcher extends AbstractStringMatcher {
|
|
|
|
/**
|
|
* Matchers in order.
|
|
*/
|
|
private final StringMatcher[] stringMatchers;
|
|
|
|
/**
|
|
* Constructs a new initialized instance.
|
|
*
|
|
* @param stringMatchers Matchers in order. Never null since the {@link StringMatcherFactory} uses the
|
|
* {@link NoneMatcher} instead.
|
|
*/
|
|
AndStringMatcher(final StringMatcher... stringMatchers) {
|
|
this.stringMatchers = stringMatchers.clone();
|
|
}
|
|
|
|
@Override
|
|
public int isMatch(final char[] buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
int total = 0;
|
|
int curStart = start;
|
|
for (final StringMatcher stringMatcher : stringMatchers) {
|
|
if (stringMatcher != null) {
|
|
final int len = stringMatcher.isMatch(buffer, curStart, bufferStart, bufferEnd);
|
|
if (len == 0) {
|
|
return 0;
|
|
}
|
|
total += len;
|
|
curStart += len;
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
@Override
|
|
public int isMatch(final CharSequence buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
int total = 0;
|
|
int curStart = start;
|
|
for (final StringMatcher stringMatcher : stringMatchers) {
|
|
if (stringMatcher != null) {
|
|
final int len = stringMatcher.isMatch(buffer, curStart, bufferStart, bufferEnd);
|
|
if (len == 0) {
|
|
return 0;
|
|
}
|
|
total += len;
|
|
curStart += len;
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
@Override
|
|
public int size() {
|
|
int total = 0;
|
|
for (final StringMatcher stringMatcher : stringMatchers) {
|
|
if (stringMatcher != null) {
|
|
total += stringMatcher.size();
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Matches out of a set of characters.
|
|
* <p>
|
|
* Thread=safe.
|
|
* </p>
|
|
*/
|
|
static final class CharArrayMatcher extends AbstractStringMatcher {
|
|
|
|
/** The string to match, as a character array, implementation treats as immutable. */
|
|
private final char[] chars;
|
|
|
|
/** The string to match. */
|
|
private final String string;
|
|
|
|
/**
|
|
* Constructs a matcher from a String.
|
|
*
|
|
* @param chars the string to match, must not be null
|
|
*/
|
|
CharArrayMatcher(final char... chars) {
|
|
this.string = String.valueOf(chars);
|
|
this.chars = chars.clone();
|
|
}
|
|
|
|
/**
|
|
* Returns the number of matching characters, {@code 0} if there is no match.
|
|
*
|
|
* @param buffer the text content to match against, do not change
|
|
* @param start the starting position for the match, valid for buffer
|
|
* @param bufferStart unused
|
|
* @param bufferEnd the end index of the active buffer, valid for buffer
|
|
* @return The number of matching characters, zero for no match
|
|
*/
|
|
@Override
|
|
public int isMatch(final char[] buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
final int len = size();
|
|
if (start + len > bufferEnd) {
|
|
return 0;
|
|
}
|
|
int j = start;
|
|
for (int i = 0; i < len; i++, j++) {
|
|
if (chars[i] != buffer[j]) {
|
|
return 0;
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of matching characters, {@code 0} if there is no match.
|
|
*
|
|
* @param buffer the text content to match against, do not change
|
|
* @param start the starting position for the match, valid for buffer
|
|
* @param bufferStart unused
|
|
* @param bufferEnd the end index of the active buffer, valid for buffer
|
|
* @return The number of matching characters, zero for no match
|
|
*/
|
|
@Override
|
|
public int isMatch(final CharSequence buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
final int len = size();
|
|
if (start + len > bufferEnd) {
|
|
return 0;
|
|
}
|
|
int j = start;
|
|
for (int i = 0; i < len; i++, j++) {
|
|
if (chars[i] != buffer.charAt(j)) {
|
|
return 0;
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* Returns the size of the string to match given in the constructor.
|
|
*
|
|
* @since 1.9
|
|
*/
|
|
@Override
|
|
public int size() {
|
|
return chars.length;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return super.toString() + "[\"" + string + "\"]";
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Matches a character.
|
|
* <p>
|
|
* Thread=safe.
|
|
* </p>
|
|
*/
|
|
static final class CharMatcher extends AbstractStringMatcher {
|
|
|
|
/** The character to match. */
|
|
private final char ch;
|
|
|
|
/**
|
|
* Constructs a matcher for a single character.
|
|
*
|
|
* @param ch the character to match
|
|
*/
|
|
CharMatcher(final char ch) {
|
|
this.ch = ch;
|
|
}
|
|
|
|
/**
|
|
* Returns {@code 1} if there is a match, or {@code 0} if there is no match.
|
|
*
|
|
* @param buffer the text content to match against, do not change
|
|
* @param start the starting position for the match, valid for buffer
|
|
* @param bufferStart unused
|
|
* @param bufferEnd unused
|
|
* @return The number of matching characters, zero for no match
|
|
*/
|
|
@Override
|
|
public int isMatch(final char[] buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
return ch == buffer[start] ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Returns {@code 1} if there is a match, or {@code 0} if there is no match.
|
|
*
|
|
* @param buffer the text content to match against, do not change
|
|
* @param start the starting position for the match, valid for buffer
|
|
* @param bufferStart unused
|
|
* @param bufferEnd unused
|
|
* @return The number of matching characters, zero for no match
|
|
*/
|
|
@Override
|
|
public int isMatch(final CharSequence buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
return ch == buffer.charAt(start) ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Returns 1.
|
|
*
|
|
* @since 1.9
|
|
*/
|
|
@Override
|
|
public int size() {
|
|
return 1;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return super.toString() + "['" + ch + "']";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Matches a set of characters.
|
|
* <p>
|
|
* Thread=safe.
|
|
* </p>
|
|
*/
|
|
static final class CharSetMatcher extends AbstractStringMatcher {
|
|
|
|
/** The set of characters to match. */
|
|
private final char[] chars;
|
|
|
|
/**
|
|
* Constructs a matcher from a character array.
|
|
*
|
|
* @param chars the characters to match, must not be null
|
|
*/
|
|
CharSetMatcher(final char[] chars) {
|
|
this.chars = chars.clone();
|
|
Arrays.sort(this.chars);
|
|
}
|
|
|
|
/**
|
|
* Returns {@code 1} if there is a match, or {@code 0} if there is no match.
|
|
*
|
|
* @param buffer the text content to match against, do not change
|
|
* @param start the starting position for the match, valid for buffer
|
|
* @param bufferStart unused
|
|
* @param bufferEnd unused
|
|
* @return The number of matching characters, zero for no match
|
|
*/
|
|
@Override
|
|
public int isMatch(final char[] buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
return Arrays.binarySearch(chars, buffer[start]) >= 0 ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Returns {@code 1} if there is a match, or {@code 0} if there is no match.
|
|
*
|
|
* @param buffer the text content to match against, do not change
|
|
* @param start the starting position for the match, valid for buffer
|
|
* @param bufferStart unused
|
|
* @param bufferEnd unused
|
|
* @return The number of matching characters, zero for no match
|
|
*/
|
|
@Override
|
|
public int isMatch(final CharSequence buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
return Arrays.binarySearch(chars, buffer.charAt(start)) >= 0 ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Returns 1.
|
|
*
|
|
* @since 1.9
|
|
*/
|
|
@Override
|
|
public int size() {
|
|
return 1;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return super.toString() + Arrays.toString(chars);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Matches nothing.
|
|
* <p>
|
|
* Thread=safe.
|
|
* </p>
|
|
*/
|
|
static final class NoneMatcher extends AbstractStringMatcher {
|
|
|
|
/**
|
|
* Constructs a new instance of {@code NoMatcher}.
|
|
*/
|
|
NoneMatcher() {
|
|
}
|
|
|
|
/**
|
|
* Always returns {@code 0}.
|
|
*
|
|
* @param buffer unused
|
|
* @param start unused
|
|
* @param bufferStart unused
|
|
* @param bufferEnd unused
|
|
* @return The number of matching characters, zero for no match
|
|
*/
|
|
@Override
|
|
public int isMatch(final char[] buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Always returns {@code 0}.
|
|
*
|
|
* @param buffer unused
|
|
* @param start unused
|
|
* @param bufferStart unused
|
|
* @param bufferEnd unused
|
|
* @return The number of matching characters, zero for no match
|
|
*/
|
|
@Override
|
|
public int isMatch(final CharSequence buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns 0.
|
|
*
|
|
* @since 1.9
|
|
*/
|
|
@Override
|
|
public int size() {
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Matches whitespace as per trim().
|
|
* <p>
|
|
* Thread=safe.
|
|
* </p>
|
|
*/
|
|
static final class TrimMatcher extends AbstractStringMatcher {
|
|
|
|
/**
|
|
* The space character.
|
|
*/
|
|
private static final int SPACE_INT = 32;
|
|
|
|
/**
|
|
* Constructs a new instance of {@code TrimMatcher}.
|
|
*/
|
|
TrimMatcher() {
|
|
}
|
|
|
|
/**
|
|
* Returns {@code 1} if there is a match, or {@code 0} if there is no match.
|
|
*
|
|
* @param buffer the text content to match against, do not change
|
|
* @param start the starting position for the match, valid for buffer
|
|
* @param bufferStart unused
|
|
* @param bufferEnd unused
|
|
* @return The number of matching characters, zero for no match
|
|
*/
|
|
@Override
|
|
public int isMatch(final char[] buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
return buffer[start] <= SPACE_INT ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Returns {@code 1} if there is a match, or {@code 0} if there is no match.
|
|
*
|
|
* @param buffer the text content to match against, do not change
|
|
* @param start the starting position for the match, valid for buffer
|
|
* @param bufferStart unused
|
|
* @param bufferEnd unused
|
|
* @return The number of matching characters, zero for no match
|
|
*/
|
|
@Override
|
|
public int isMatch(final CharSequence buffer, final int start, final int bufferStart, final int bufferEnd) {
|
|
return buffer.charAt(start) <= SPACE_INT ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Returns 1.
|
|
*
|
|
* @since 1.9
|
|
*/
|
|
@Override
|
|
public int size() {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
protected AbstractStringMatcher() {
|
|
}
|
|
|
|
// /**
|
|
// * Validates indices for {@code bufferStart <= start < bufferEnd}.
|
|
// *
|
|
// * @param start the starting position for the match, valid in {@code buffer}.
|
|
// * @param bufferStart the first active index in the buffer, valid in {@code buffer}.
|
|
// * @param bufferEnd the end index (exclusive) of the active buffer, valid in {@code buffer}.
|
|
// */
|
|
// void validate(final int start, final int bufferStart, final int bufferEnd) {
|
|
// if (((bufferStart > start) || (start >= bufferEnd))) {
|
|
// throw new IndexOutOfBoundsException(
|
|
// String.format("bufferStart(%,d) <= start(%,d) < bufferEnd(%,d)", bufferStart, start, bufferEnd));
|
|
// }
|
|
// }
|
|
|
|
}
|