Copyright (c) 2000, 2015 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation Lars Vogel - Bug 473427
/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427 *******************************************************************************/
package org.eclipse.core.internal.propertytester; import java.util.ArrayList;
A string pattern matcher, supporting "*" and "?" wild cards.
Since:3.2
/** * A string pattern matcher, supporting "*" and "?" wild cards. * * @since 3.2 */
public class StringMatcher { private static final char SINGLE_WILD_CARD = '\u0000';
Boundary value beyond which we don't need to search in the text
/** * Boundary value beyond which we don't need to search in the text */
private int bound = 0; private boolean hasLeadingStar; private boolean hasTrailingStar; private final String pattern; private final int patternLength;
The pattern split into segments separated by *
/** * The pattern split into segments separated by * */
private String segments[];
StringMatcher constructor takes in a String object that is a simple pattern which may contain '*' for 0 and many characters and '?' for exactly one character. Literal '*' and '?' characters must be escaped in the pattern e.g., "\*" means literal "*", etc. Escaping any other character (including the escape character itself), just results in that character in the pattern. e.g., "\a" means "a" and "\\" means "\" If invoking the StringMatcher with string literals in Java, don't forget escape characters are represented by "\\".
Params:
  • pattern – the pattern to match text against
/** * StringMatcher constructor takes in a String object that is a simple * pattern which may contain '*' for 0 and many characters and * '?' for exactly one character. * * Literal '*' and '?' characters must be escaped in the pattern * e.g., "\*" means literal "*", etc. * * Escaping any other character (including the escape character itself), * just results in that character in the pattern. * e.g., "\a" means "a" and "\\" means "\" * * If invoking the StringMatcher with string literals in Java, don't forget * escape characters are represented by "\\". * * @param pattern the pattern to match text against */
public StringMatcher(String pattern) { if (pattern == null) throw new IllegalArgumentException(); this.pattern = pattern; patternLength = pattern.length(); parseWildCards(); }
Params:
  • text – a simple regular expression that may only contain '?'(s)
  • start – the starting index in the text for search, inclusive
  • end – the stopping point of search, exclusive
  • p – a simple regular expression that may contain '?'
Returns:the starting index in the text of the pattern , or -1 if not found
/** * @param text a simple regular expression that may only contain '?'(s) * @param start the starting index in the text for search, inclusive * @param end the stopping point of search, exclusive * @param p a simple regular expression that may contain '?' * @return the starting index in the text of the pattern , or -1 if not found */
private int findPosition(String text, int start, int end, String p) { boolean hasWildCard = p.indexOf(SINGLE_WILD_CARD) >= 0; int plen = p.length(); for (int i = start, max = end - plen; i <= max; ++i) { if (hasWildCard) { if (regExpRegionMatches(text, i, p, 0, plen)) return i; } else { if (text.regionMatches(true, i, p, 0, plen)) return i; } } return -1; }
Given the starting (inclusive) and the ending (exclusive) positions in the text, determine if the given substring matches with aPattern
Params:
  • text – a String object that contains the substring to match
Returns:true if the specified portion of the text matches the pattern
/** * Given the starting (inclusive) and the ending (exclusive) positions in the * <code>text</code>, determine if the given substring matches with aPattern * @return true if the specified portion of the text matches the pattern * @param text a String object that contains the substring to match */
public boolean match(String text) { if (text == null) return false; final int end = text.length(); final int segmentCount = segments.length; if (segmentCount == 0 && (hasLeadingStar || hasTrailingStar)) // pattern contains only '*'(s) return true; if (end == 0) return patternLength == 0; if (patternLength == 0) return false; int currentTextPosition = 0; if ((end - bound) < 0) return false; int segmentIndex = 0; String current = segments[segmentIndex]; /* process first segment */ if (!hasLeadingStar) { int currentLength = current.length(); if (!regExpRegionMatches(text, 0, current, 0, currentLength)) return false; segmentIndex++; currentTextPosition = currentTextPosition + currentLength; } if ((segmentCount == 1) && (!hasLeadingStar) && (!hasTrailingStar)) { // only one segment to match, no wild cards specified return currentTextPosition == end; } /* process middle segments */ while (segmentIndex < segmentCount) { current = segments[segmentIndex]; int currentMatch = findPosition(text, currentTextPosition, end, current); if (currentMatch < 0) return false; currentTextPosition = currentMatch + current.length(); segmentIndex++; } /* process final segment */ if (!hasTrailingStar && currentTextPosition != end) { int currentLength = current.length(); return regExpRegionMatches(text, end - currentLength, current, 0, currentLength); } return segmentIndex == segmentCount; }
Parses the pattern into segments separated by wildcard '*' characters.
/** * Parses the pattern into segments separated by wildcard '*' characters. */
private void parseWildCards() { if (pattern.startsWith("*")) //$NON-NLS-1$ hasLeadingStar = true; if (pattern.endsWith("*")) {//$NON-NLS-1$ /* make sure it's not an escaped wildcard */ if (patternLength > 1 && pattern.charAt(patternLength - 2) != '\\') { hasTrailingStar = true; } } ArrayList<String> temp = new ArrayList<>(); int pos = 0; StringBuilder buf = new StringBuilder(); while (pos < patternLength) { char c = pattern.charAt(pos++); switch (c) { case '\\' : if (pos >= patternLength) { buf.append(c); } else { char next = pattern.charAt(pos++); /* if it's an escape sequence */ if (next == '*' || next == '?' || next == '\\') { buf.append(next); } else { /* not an escape sequence, just insert literally */ buf.append(c); buf.append(next); } } break; case '*' : if (buf.length() > 0) { /* new segment */ temp.add(buf.toString()); bound += buf.length(); buf.setLength(0); } break; case '?' : /* append special character representing single match wildcard */ buf.append(SINGLE_WILD_CARD); break; default : buf.append(c); } } /* add last buffer to segment list */ if (buf.length() > 0) { temp.add(buf.toString()); bound += buf.length(); } segments = temp.toArray(new String[temp.size()]); }
Params:
  • text – a String to match
  • tStart – the starting index of match, inclusive
  • p – a simple regular expression that may contain '?'
  • pStart – The start position in the pattern
  • plen – The length of the pattern
Returns:boolean
/** * * @return boolean * @param text a String to match * @param tStart the starting index of match, inclusive * @param p a simple regular expression that may contain '?' * @param pStart The start position in the pattern * @param plen The length of the pattern */
private boolean regExpRegionMatches(String text, int tStart, String p, int pStart, int plen) { while (plen-- > 0) { char tchar = text.charAt(tStart++); char pchar = p.charAt(pStart++); // process wild cards, skipping single wild cards if (pchar == SINGLE_WILD_CARD) continue; if (pchar == tchar) continue; if (Character.toUpperCase(tchar) == Character.toUpperCase(pchar)) continue; // comparing after converting to upper case doesn't handle all cases; // also compare after converting to lower case if (Character.toLowerCase(tchar) == Character.toLowerCase(pchar)) continue; return false; } return true; } }