Path: blob/master/debugtools/DDR_VM/src/com/ibm/j9ddr/util/PatternString.java
6005 views
/*******************************************************************************1* Copyright (c) 2001, 2014 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/21package com.ibm.j9ddr.util;2223import java.io.Serializable;24import java.util.LinkedList;25import java.util.List;26import java.util.StringTokenizer;2728/**29* A string containing wildcards and other characters that enable it30* to represent many strings at once.31* @author sfoley32*/33public class PatternString implements Serializable {34/** Pattern string's <code>serialVersionUID</code> */35private static final long serialVersionUID = 3257289149408162611L;3637/*38* the following flags indicate special cases, the pattern39* with no wildcards and the pattern "*" which matches everything40*/41private boolean isRegularString;42private boolean alwaysMatches;4344final String pattern;45final String tokens[];4647public static final char wildCard = '*';48public static final char singleCharWildCard = '?';49private static int nullArray[] = new int[0];50public static final PatternString ALWAYS_MATCHES = new PatternString(String.valueOf(wildCard));5152/**53* Constructor for creating a PatternString class which can contain wildcard expressions and54* other characters to represent many strings at once.55*56* @param pattern a String which can contain wildcards57*/58public PatternString(String pattern) {59this.pattern = pattern;60if(pattern.equals(String.valueOf(wildCard))) {61alwaysMatches = true;62tokens = null;63return;64}6566StringTokenizer tokenizer = new StringTokenizer(pattern,67String.valueOf(wildCard) + singleCharWildCard, true);68List tokenList = new LinkedList();69isRegularString = true;70while(tokenizer.hasMoreTokens()) {71String token = tokenizer.nextToken();72if(isRegularString) {73switch(token.charAt(0)) {74case wildCard:75case singleCharWildCard:76isRegularString = false;77}78}79tokenList.add(token);80}81tokens = (String[]) tokenList.toArray(new String[tokenList.size()]);82}8384/**85* Indicates the location in the PatternString of the first instance of a wildcard86*87* @return int returns the index where the wildcard can be found, if no wildcards present returns -1, if pattern is '*' returns 088*/89int indexOfWildcard() {90if(isRegularString) {91return -1;92}93if(alwaysMatches) {94return 0;95}96int index = 0;97for(int i=0; i<tokens.length; i++) {98String token = tokens[i];99switch(token.charAt(0)) {100case wildCard:101case singleCharWildCard:102return index;103default:104index += token.length();105}106}107//should never reach here108return -1;109}110111/**112* A class to represent the PatternString in two separate patterns113*114*/115public static class PatternStringPair{116public final PatternString first;117public final PatternString second;118119/**120* Constructor for PatternStringPair, which represents a PatternString in two separate patterns121*122* @param first a PatternString which is the first pattern of the two that represent the PatternString123* @param second a PatternString which is the second pattern of the two that represent the PatternString124*/125PatternStringPair(PatternString first, PatternString second) {126this.first = first;127this.second = second;128}129130/**131* Constructor for PatternStringPair, which represents a PatternString in two separate patterns132*133* @param first a String which is first pattern of the two that represent the PatternString134* @param second a String which is second pattern of the two that represent the PatternString135*/136PatternStringPair(String first, String second) {137this(new PatternString(first), new PatternString(second));138}139}140141/**142* Splits the patterns into two separate patterns, the splitting being done by the143* character at the indicated index, so this character is not included in either string.144* If such a character is a variable length wildcard then the145* new patterns will begin and end with wildcards as needed.146*147* @param index the index at which to split the PatternString in two parts148*/149public PatternStringPair split(int index) {150int c = pattern.charAt(index);151if(c == wildCard) {152return new PatternStringPair(pattern.substring(0, index + 1),153pattern.substring(index));154}155return new PatternStringPair(pattern.substring(0, index), pattern.substring(index + 1));156}157158159/**160* Finds the indices of the possible first locations of the indicated character.161* The results are returned in ascending order.162*163* @param c index of the pattern within this PatternString164* @return int[] an array indicating the possible first locations of the character indicated at the index provided.165*/166public int[] indexOf(int c) {167int results[] = new int[pattern.length()];168int totalLocations;169int index = pattern.indexOf(c);170if(index == -1) {171totalLocations = findAdditionalLocations(results, pattern, c, true);172}173else {174totalLocations = findAdditionalLocations(results, pattern.substring(0, index), c, true);175results[totalLocations++] = index;176}177if(totalLocations == 0) {178return nullArray;179}180if(totalLocations == pattern.length()) {181return results;182}183int newResults[] = new int[totalLocations];184System.arraycopy(results, 0, newResults, 0, totalLocations);185return newResults;186}187188189private int findAdditionalLocations(int results[], String sub, int c, boolean forward) {190int resultIndex = 0;191int len = sub.length();192for(int i=0; i<len; i++) {193int j = (forward ? i : (len - i - 1));194char ch = sub.charAt(j);195if(ch == wildCard || ch == singleCharWildCard) {196results[resultIndex++] = j;197}198}199return resultIndex;200}201202/**203* Finds the indices of the possible last locations of the indicated character.204* The results are returned in descending order.205*206* @param c index of the pattern within this PatternString207* @return int[] an array indicating the possible last locations of the character indicated at the index provided.208*/209public int[] lastIndexOf(int c) {210int maxResults = pattern.length();211int results[] = new int[maxResults];212int totalLocations;213int index = pattern.lastIndexOf(c);214if(index == -1) {215totalLocations = findAdditionalLocations(results, pattern, c, false);216}217else {218int offset = index + 1;219totalLocations = findAdditionalLocations(results, pattern.substring(offset), c, false);220for(int i=0; i<totalLocations; i++) {221results[i] += offset;222}223results[totalLocations++] = index;224}225if(totalLocations == 0) {226return nullArray;227}228if(totalLocations == maxResults) {229return results;230}231int newResults[] = new int[totalLocations];232System.arraycopy(results, 0, newResults, 0, totalLocations);233return newResults;234}235236237/**238* Checks if this PatternString contains no wildcards.239*240* @return boolean true if this PatternString contains no wildcards, false otherwise241*/242public boolean isRegularString() {243return isRegularString;244}245246/**247* Checks if the provided character is a wildcard248*249* @param c the character to check whether it is a wildcard or not250* @return boolean true if the character given is a wildcard251*/252public static boolean isWildcard(char c) {253return c == wildCard || c == singleCharWildCard;254}255256/**257* Determine if there is a match to the pattern that ends with the given string258*259* @param string the String to match260* @return boolean returns true if there is a match, false if not261*/262public boolean endsWith(String string) {263String reversePattern = new StringBuffer(pattern).reverse().toString();264String reverseString = new StringBuffer(string).reverse().toString();265return new PatternString(reversePattern).startsWith(reverseString);266}267268/**269* Determines if the pattern matches with a given string.270*271* @param string the String to match272* @return boolean returns true if there is a match, false if not273*/274public boolean isMatch(String string) {275return alwaysMatches || (isRegularString ? pattern.equals(string) : isMatch(string, false));276}277278/**279* Determine if there is a match to the pattern that starts with the given string280*281* @param string the String to match282* @return boolean returns true if there is a match, false if not283*/284public boolean startsWith(String string) {285return alwaysMatches || (isRegularString ? pattern.startsWith(string) : isMatch(string, true));286}287288289/**290* A class to manage the state of a match between two strings.291*292*/293class MatchState implements Cloneable {294boolean onWildCard;295boolean onSingleCharWildCard;296int numChars;297298void reset() {299onWildCard = onSingleCharWildCard = false;300numChars = 0;301}302303boolean matchesRemainder(String string, int stringStartIndex) {304return ((string.length() == stringStartIndex + numChars)305|| (onWildCard && (string.length() >= stringStartIndex + numChars)));306}307308void matchWildcard(char c) {309switch(c) {310case wildCard:311onWildCard = true;312return;313case singleCharWildCard:314onSingleCharWildCard = true;315numChars++;316return;317default:318}319}320321boolean startsWithRemainder(String string, int stringStartIndex, String token) {322String remainderToMatch = string.substring(stringStartIndex + numChars);323return token.startsWith(remainderToMatch);324}325326int matchToken(String string, int stringStartIndex, int searchIndex, String token) {327if(onSingleCharWildCard) {328//we add this here because the onDotExcludingSingleCharWildCard must consume at least one character329searchIndex = Math.max(stringStartIndex + numChars, searchIndex);330}331int index = string.indexOf(token, searchIndex);332if(index < 0) {333return -1;334}335switch(index - stringStartIndex) {336default: //index > 1337if(!onWildCard) {338if(!onSingleCharWildCard || stringStartIndex + numChars != index) {339return -1;340}341}342//fall through343case 0:344break;345}346return index;347}348349public Object clone() {350try {351return super.clone();352} catch(CloneNotSupportedException e) {}353return null;354}355}356357/* in a multi-threaded environment this field would need to go */358private MatchState savedState = new MatchState();359360private boolean isMatch(String string, boolean startsWith) {361savedState.reset();362return isMatch(string, 0, startsWith, 0, savedState);363}364365private boolean isMatch(String string, int stringStartIndex, boolean startsWith, int startTokenIndex, MatchState state) {366for(int i=startTokenIndex; i<tokens.length; i++) {367if(startsWith && state.matchesRemainder(string, stringStartIndex)) {368return true;369}370String currentToken = tokens[i];371char c = currentToken.charAt(0);372if(isWildcard(c)) {373state.matchWildcard(currentToken.charAt(0));374}375else {376int stringTokenIndex = state.matchToken(string, stringStartIndex, stringStartIndex, currentToken);377if(stringTokenIndex < 0) {378if(startsWith) {379return state.startsWithRemainder(string, stringStartIndex, currentToken);380//return currentToken.startsWith(string.substring(stringStartIndex));381}382return false;383}384else {385//we have found the token in the string386//now we check if we can find it again further along the string387int searchIndex = stringTokenIndex + 1;388while(true) {389int nextIndex = state.matchToken(string, stringStartIndex, searchIndex, currentToken);390if(nextIndex < 0) {391break;392}393MatchState newState = (MatchState) state.clone();394newState.reset();395if(isMatch(string, nextIndex + currentToken.length(), startsWith, i+1, newState)) {396return true;397}398searchIndex = nextIndex + 1;399}400state.reset();401stringStartIndex = stringTokenIndex + currentToken.length();402}403}404}405return state.matchesRemainder(string, stringStartIndex);406}407408/**409* Gives the pattern used to create this PatternString class410*411* @return String returns the pattern used to construct this PatternString412*/413public String toString() {414return pattern;415}416417/*418static int failures = 0;419public static void main(String args[]) {420checkMatch("", "ind", "abc", false);421checkMatch("", "x", "abc", false);422checkMatch("", "", "abc", true);423checkMatch("", "ind", "indind", false);424checkMatch("", "indind", "indind", false);425checkMatch("", "ind", "ind", false);426checkMatch("", "x", "ind", false);427checkMatch("", "", "ind", true);428429checkMatch("" + wildCard, "ind", "abc", true);430checkMatch("" + wildCard, "x", "abc", true);431checkMatch("" + wildCard, "", "abc", true);432checkMatch("" + wildCard, "ind", "indind", true);433checkMatch("" + wildCard, "indind", "indind", true);434checkMatch("" + wildCard, "ind", "ind", true);435checkMatch("" + wildCard, "x", "ind", true);436checkMatch("" + wildCard, "", "ind", true);437438439checkMatch("" + singleCharWildCard, "ind", "abc", false);440checkMatch("" + singleCharWildCard, "x", "abc", true);441checkMatch("" + singleCharWildCard, "x", "x", true);442checkMatch("" + singleCharWildCard, "", "abc", false);443444checkMatch("" + singleCharWildCard + singleCharWildCard, "ind", "abc", false);445checkMatch("" + singleCharWildCard + singleCharWildCard, "x", "abc", false);446checkMatch("" + singleCharWildCard + singleCharWildCard, "x", "x", false);447checkMatch("" + singleCharWildCard + singleCharWildCard, "xx", "abc", true);448checkMatch("" + singleCharWildCard + singleCharWildCard, "xx", "x", true);449checkMatch("" + singleCharWildCard + singleCharWildCard, "", "abc", false);450451452checkMatch("" + singleCharWildCard + wildCard, "ind", "abc", true);453checkMatch("" + singleCharWildCard + wildCard, "x", "abc", true);454checkMatch("" + singleCharWildCard + wildCard, "", "abc", false);455checkMatch("" + singleCharWildCard + wildCard, "ind", "indind", true);456checkMatch("" + singleCharWildCard + wildCard, "indind", "indind", true);457checkMatch("" + singleCharWildCard + wildCard, "ind", "ind", true);458checkMatch("" + singleCharWildCard + wildCard, "x", "ind", true);459checkMatch("" + singleCharWildCard + wildCard, "", "ind", false);460461checkMatch("" + wildCard + singleCharWildCard, "ind", "abc", true);462checkMatch("" + wildCard + singleCharWildCard, "x", "abc", true);463checkMatch("" + wildCard + singleCharWildCard, "", "abc", false);464checkMatch("" + wildCard + singleCharWildCard, "ind", "indind", true);465checkMatch("" + wildCard + singleCharWildCard, "indind", "indind", true);466checkMatch("" + wildCard + singleCharWildCard, "ind", "ind", true);467checkMatch("" + wildCard + singleCharWildCard, "x", "ind", true);468checkMatch("" + wildCard + singleCharWildCard, "", "ind", false);469470471System.out.println("failures: " + failures);472}473474static void checkMatch(String wildcard, String match, String pattern, boolean wildcardMatchesMatch) {475checkMatch(wildcard + pattern, match + pattern, wildcardMatchesMatch);476checkMatch(pattern + wildcard, pattern + match, wildcardMatchesMatch);477checkMatch(pattern + wildcard + pattern, pattern + match + pattern, wildcardMatchesMatch);478}479480static void checkMatch(String pattern, String string, boolean expectedResult) {481CopyOfPatternString p = new CopyOfPatternString(pattern);482boolean result = p.isMatch(string);483if(result != expectedResult) {484System.out.print("FAIL");485failures++;486}487else {488System.out.print("PASS");489}490System.out.println(": " + pattern + ", " + string + ", matches: " + result);491492493if(expectedResult) {494String newPattern = pattern + "end";495p = new CopyOfPatternString(newPattern);496//String newString = string + "end";497result = p.startsWith(string);498if(result) {499System.out.print("PASS: ");500}501else {502System.out.print("FAIL: ");503failures++;504}505System.out.println(newPattern + ", " + string + ", starts with: " + result);506}507508}509*/510511}512513514