Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/debugtools/DDR_VM/src/com/ibm/j9ddr/util/PatternString.java
6005 views
1
/*******************************************************************************
2
* Copyright (c) 2001, 2014 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* 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-exception
21
*******************************************************************************/
22
package com.ibm.j9ddr.util;
23
24
import java.io.Serializable;
25
import java.util.LinkedList;
26
import java.util.List;
27
import java.util.StringTokenizer;
28
29
/**
30
* A string containing wildcards and other characters that enable it
31
* to represent many strings at once.
32
* @author sfoley
33
*/
34
public class PatternString implements Serializable {
35
/** Pattern string's <code>serialVersionUID</code> */
36
private static final long serialVersionUID = 3257289149408162611L;
37
38
/*
39
* the following flags indicate special cases, the pattern
40
* with no wildcards and the pattern "*" which matches everything
41
*/
42
private boolean isRegularString;
43
private boolean alwaysMatches;
44
45
final String pattern;
46
final String tokens[];
47
48
public static final char wildCard = '*';
49
public static final char singleCharWildCard = '?';
50
private static int nullArray[] = new int[0];
51
public static final PatternString ALWAYS_MATCHES = new PatternString(String.valueOf(wildCard));
52
53
/**
54
* Constructor for creating a PatternString class which can contain wildcard expressions and
55
* other characters to represent many strings at once.
56
*
57
* @param pattern a String which can contain wildcards
58
*/
59
public PatternString(String pattern) {
60
this.pattern = pattern;
61
if(pattern.equals(String.valueOf(wildCard))) {
62
alwaysMatches = true;
63
tokens = null;
64
return;
65
}
66
67
StringTokenizer tokenizer = new StringTokenizer(pattern,
68
String.valueOf(wildCard) + singleCharWildCard, true);
69
List tokenList = new LinkedList();
70
isRegularString = true;
71
while(tokenizer.hasMoreTokens()) {
72
String token = tokenizer.nextToken();
73
if(isRegularString) {
74
switch(token.charAt(0)) {
75
case wildCard:
76
case singleCharWildCard:
77
isRegularString = false;
78
}
79
}
80
tokenList.add(token);
81
}
82
tokens = (String[]) tokenList.toArray(new String[tokenList.size()]);
83
}
84
85
/**
86
* Indicates the location in the PatternString of the first instance of a wildcard
87
*
88
* @return int returns the index where the wildcard can be found, if no wildcards present returns -1, if pattern is '*' returns 0
89
*/
90
int indexOfWildcard() {
91
if(isRegularString) {
92
return -1;
93
}
94
if(alwaysMatches) {
95
return 0;
96
}
97
int index = 0;
98
for(int i=0; i<tokens.length; i++) {
99
String token = tokens[i];
100
switch(token.charAt(0)) {
101
case wildCard:
102
case singleCharWildCard:
103
return index;
104
default:
105
index += token.length();
106
}
107
}
108
//should never reach here
109
return -1;
110
}
111
112
/**
113
* A class to represent the PatternString in two separate patterns
114
*
115
*/
116
public static class PatternStringPair{
117
public final PatternString first;
118
public final PatternString second;
119
120
/**
121
* Constructor for PatternStringPair, which represents a PatternString in two separate patterns
122
*
123
* @param first a PatternString which is the first pattern of the two that represent the PatternString
124
* @param second a PatternString which is the second pattern of the two that represent the PatternString
125
*/
126
PatternStringPair(PatternString first, PatternString second) {
127
this.first = first;
128
this.second = second;
129
}
130
131
/**
132
* Constructor for PatternStringPair, which represents a PatternString in two separate patterns
133
*
134
* @param first a String which is first pattern of the two that represent the PatternString
135
* @param second a String which is second pattern of the two that represent the PatternString
136
*/
137
PatternStringPair(String first, String second) {
138
this(new PatternString(first), new PatternString(second));
139
}
140
}
141
142
/**
143
* Splits the patterns into two separate patterns, the splitting being done by the
144
* character at the indicated index, so this character is not included in either string.
145
* If such a character is a variable length wildcard then the
146
* new patterns will begin and end with wildcards as needed.
147
*
148
* @param index the index at which to split the PatternString in two parts
149
*/
150
public PatternStringPair split(int index) {
151
int c = pattern.charAt(index);
152
if(c == wildCard) {
153
return new PatternStringPair(pattern.substring(0, index + 1),
154
pattern.substring(index));
155
}
156
return new PatternStringPair(pattern.substring(0, index), pattern.substring(index + 1));
157
}
158
159
160
/**
161
* Finds the indices of the possible first locations of the indicated character.
162
* The results are returned in ascending order.
163
*
164
* @param c index of the pattern within this PatternString
165
* @return int[] an array indicating the possible first locations of the character indicated at the index provided.
166
*/
167
public int[] indexOf(int c) {
168
int results[] = new int[pattern.length()];
169
int totalLocations;
170
int index = pattern.indexOf(c);
171
if(index == -1) {
172
totalLocations = findAdditionalLocations(results, pattern, c, true);
173
}
174
else {
175
totalLocations = findAdditionalLocations(results, pattern.substring(0, index), c, true);
176
results[totalLocations++] = index;
177
}
178
if(totalLocations == 0) {
179
return nullArray;
180
}
181
if(totalLocations == pattern.length()) {
182
return results;
183
}
184
int newResults[] = new int[totalLocations];
185
System.arraycopy(results, 0, newResults, 0, totalLocations);
186
return newResults;
187
}
188
189
190
private int findAdditionalLocations(int results[], String sub, int c, boolean forward) {
191
int resultIndex = 0;
192
int len = sub.length();
193
for(int i=0; i<len; i++) {
194
int j = (forward ? i : (len - i - 1));
195
char ch = sub.charAt(j);
196
if(ch == wildCard || ch == singleCharWildCard) {
197
results[resultIndex++] = j;
198
}
199
}
200
return resultIndex;
201
}
202
203
/**
204
* Finds the indices of the possible last locations of the indicated character.
205
* The results are returned in descending order.
206
*
207
* @param c index of the pattern within this PatternString
208
* @return int[] an array indicating the possible last locations of the character indicated at the index provided.
209
*/
210
public int[] lastIndexOf(int c) {
211
int maxResults = pattern.length();
212
int results[] = new int[maxResults];
213
int totalLocations;
214
int index = pattern.lastIndexOf(c);
215
if(index == -1) {
216
totalLocations = findAdditionalLocations(results, pattern, c, false);
217
}
218
else {
219
int offset = index + 1;
220
totalLocations = findAdditionalLocations(results, pattern.substring(offset), c, false);
221
for(int i=0; i<totalLocations; i++) {
222
results[i] += offset;
223
}
224
results[totalLocations++] = index;
225
}
226
if(totalLocations == 0) {
227
return nullArray;
228
}
229
if(totalLocations == maxResults) {
230
return results;
231
}
232
int newResults[] = new int[totalLocations];
233
System.arraycopy(results, 0, newResults, 0, totalLocations);
234
return newResults;
235
}
236
237
238
/**
239
* Checks if this PatternString contains no wildcards.
240
*
241
* @return boolean true if this PatternString contains no wildcards, false otherwise
242
*/
243
public boolean isRegularString() {
244
return isRegularString;
245
}
246
247
/**
248
* Checks if the provided character is a wildcard
249
*
250
* @param c the character to check whether it is a wildcard or not
251
* @return boolean true if the character given is a wildcard
252
*/
253
public static boolean isWildcard(char c) {
254
return c == wildCard || c == singleCharWildCard;
255
}
256
257
/**
258
* Determine if there is a match to the pattern that ends with the given string
259
*
260
* @param string the String to match
261
* @return boolean returns true if there is a match, false if not
262
*/
263
public boolean endsWith(String string) {
264
String reversePattern = new StringBuffer(pattern).reverse().toString();
265
String reverseString = new StringBuffer(string).reverse().toString();
266
return new PatternString(reversePattern).startsWith(reverseString);
267
}
268
269
/**
270
* Determines if the pattern matches with a given string.
271
*
272
* @param string the String to match
273
* @return boolean returns true if there is a match, false if not
274
*/
275
public boolean isMatch(String string) {
276
return alwaysMatches || (isRegularString ? pattern.equals(string) : isMatch(string, false));
277
}
278
279
/**
280
* Determine if there is a match to the pattern that starts with the given string
281
*
282
* @param string the String to match
283
* @return boolean returns true if there is a match, false if not
284
*/
285
public boolean startsWith(String string) {
286
return alwaysMatches || (isRegularString ? pattern.startsWith(string) : isMatch(string, true));
287
}
288
289
290
/**
291
* A class to manage the state of a match between two strings.
292
*
293
*/
294
class MatchState implements Cloneable {
295
boolean onWildCard;
296
boolean onSingleCharWildCard;
297
int numChars;
298
299
void reset() {
300
onWildCard = onSingleCharWildCard = false;
301
numChars = 0;
302
}
303
304
boolean matchesRemainder(String string, int stringStartIndex) {
305
return ((string.length() == stringStartIndex + numChars)
306
|| (onWildCard && (string.length() >= stringStartIndex + numChars)));
307
}
308
309
void matchWildcard(char c) {
310
switch(c) {
311
case wildCard:
312
onWildCard = true;
313
return;
314
case singleCharWildCard:
315
onSingleCharWildCard = true;
316
numChars++;
317
return;
318
default:
319
}
320
}
321
322
boolean startsWithRemainder(String string, int stringStartIndex, String token) {
323
String remainderToMatch = string.substring(stringStartIndex + numChars);
324
return token.startsWith(remainderToMatch);
325
}
326
327
int matchToken(String string, int stringStartIndex, int searchIndex, String token) {
328
if(onSingleCharWildCard) {
329
//we add this here because the onDotExcludingSingleCharWildCard must consume at least one character
330
searchIndex = Math.max(stringStartIndex + numChars, searchIndex);
331
}
332
int index = string.indexOf(token, searchIndex);
333
if(index < 0) {
334
return -1;
335
}
336
switch(index - stringStartIndex) {
337
default: //index > 1
338
if(!onWildCard) {
339
if(!onSingleCharWildCard || stringStartIndex + numChars != index) {
340
return -1;
341
}
342
}
343
//fall through
344
case 0:
345
break;
346
}
347
return index;
348
}
349
350
public Object clone() {
351
try {
352
return super.clone();
353
} catch(CloneNotSupportedException e) {}
354
return null;
355
}
356
}
357
358
/* in a multi-threaded environment this field would need to go */
359
private MatchState savedState = new MatchState();
360
361
private boolean isMatch(String string, boolean startsWith) {
362
savedState.reset();
363
return isMatch(string, 0, startsWith, 0, savedState);
364
}
365
366
private boolean isMatch(String string, int stringStartIndex, boolean startsWith, int startTokenIndex, MatchState state) {
367
for(int i=startTokenIndex; i<tokens.length; i++) {
368
if(startsWith && state.matchesRemainder(string, stringStartIndex)) {
369
return true;
370
}
371
String currentToken = tokens[i];
372
char c = currentToken.charAt(0);
373
if(isWildcard(c)) {
374
state.matchWildcard(currentToken.charAt(0));
375
}
376
else {
377
int stringTokenIndex = state.matchToken(string, stringStartIndex, stringStartIndex, currentToken);
378
if(stringTokenIndex < 0) {
379
if(startsWith) {
380
return state.startsWithRemainder(string, stringStartIndex, currentToken);
381
//return currentToken.startsWith(string.substring(stringStartIndex));
382
}
383
return false;
384
}
385
else {
386
//we have found the token in the string
387
//now we check if we can find it again further along the string
388
int searchIndex = stringTokenIndex + 1;
389
while(true) {
390
int nextIndex = state.matchToken(string, stringStartIndex, searchIndex, currentToken);
391
if(nextIndex < 0) {
392
break;
393
}
394
MatchState newState = (MatchState) state.clone();
395
newState.reset();
396
if(isMatch(string, nextIndex + currentToken.length(), startsWith, i+1, newState)) {
397
return true;
398
}
399
searchIndex = nextIndex + 1;
400
}
401
state.reset();
402
stringStartIndex = stringTokenIndex + currentToken.length();
403
}
404
}
405
}
406
return state.matchesRemainder(string, stringStartIndex);
407
}
408
409
/**
410
* Gives the pattern used to create this PatternString class
411
*
412
* @return String returns the pattern used to construct this PatternString
413
*/
414
public String toString() {
415
return pattern;
416
}
417
418
/*
419
static int failures = 0;
420
public static void main(String args[]) {
421
checkMatch("", "ind", "abc", false);
422
checkMatch("", "x", "abc", false);
423
checkMatch("", "", "abc", true);
424
checkMatch("", "ind", "indind", false);
425
checkMatch("", "indind", "indind", false);
426
checkMatch("", "ind", "ind", false);
427
checkMatch("", "x", "ind", false);
428
checkMatch("", "", "ind", true);
429
430
checkMatch("" + wildCard, "ind", "abc", true);
431
checkMatch("" + wildCard, "x", "abc", true);
432
checkMatch("" + wildCard, "", "abc", true);
433
checkMatch("" + wildCard, "ind", "indind", true);
434
checkMatch("" + wildCard, "indind", "indind", true);
435
checkMatch("" + wildCard, "ind", "ind", true);
436
checkMatch("" + wildCard, "x", "ind", true);
437
checkMatch("" + wildCard, "", "ind", true);
438
439
440
checkMatch("" + singleCharWildCard, "ind", "abc", false);
441
checkMatch("" + singleCharWildCard, "x", "abc", true);
442
checkMatch("" + singleCharWildCard, "x", "x", true);
443
checkMatch("" + singleCharWildCard, "", "abc", false);
444
445
checkMatch("" + singleCharWildCard + singleCharWildCard, "ind", "abc", false);
446
checkMatch("" + singleCharWildCard + singleCharWildCard, "x", "abc", false);
447
checkMatch("" + singleCharWildCard + singleCharWildCard, "x", "x", false);
448
checkMatch("" + singleCharWildCard + singleCharWildCard, "xx", "abc", true);
449
checkMatch("" + singleCharWildCard + singleCharWildCard, "xx", "x", true);
450
checkMatch("" + singleCharWildCard + singleCharWildCard, "", "abc", false);
451
452
453
checkMatch("" + singleCharWildCard + wildCard, "ind", "abc", true);
454
checkMatch("" + singleCharWildCard + wildCard, "x", "abc", true);
455
checkMatch("" + singleCharWildCard + wildCard, "", "abc", false);
456
checkMatch("" + singleCharWildCard + wildCard, "ind", "indind", true);
457
checkMatch("" + singleCharWildCard + wildCard, "indind", "indind", true);
458
checkMatch("" + singleCharWildCard + wildCard, "ind", "ind", true);
459
checkMatch("" + singleCharWildCard + wildCard, "x", "ind", true);
460
checkMatch("" + singleCharWildCard + wildCard, "", "ind", false);
461
462
checkMatch("" + wildCard + singleCharWildCard, "ind", "abc", true);
463
checkMatch("" + wildCard + singleCharWildCard, "x", "abc", true);
464
checkMatch("" + wildCard + singleCharWildCard, "", "abc", false);
465
checkMatch("" + wildCard + singleCharWildCard, "ind", "indind", true);
466
checkMatch("" + wildCard + singleCharWildCard, "indind", "indind", true);
467
checkMatch("" + wildCard + singleCharWildCard, "ind", "ind", true);
468
checkMatch("" + wildCard + singleCharWildCard, "x", "ind", true);
469
checkMatch("" + wildCard + singleCharWildCard, "", "ind", false);
470
471
472
System.out.println("failures: " + failures);
473
}
474
475
static void checkMatch(String wildcard, String match, String pattern, boolean wildcardMatchesMatch) {
476
checkMatch(wildcard + pattern, match + pattern, wildcardMatchesMatch);
477
checkMatch(pattern + wildcard, pattern + match, wildcardMatchesMatch);
478
checkMatch(pattern + wildcard + pattern, pattern + match + pattern, wildcardMatchesMatch);
479
}
480
481
static void checkMatch(String pattern, String string, boolean expectedResult) {
482
CopyOfPatternString p = new CopyOfPatternString(pattern);
483
boolean result = p.isMatch(string);
484
if(result != expectedResult) {
485
System.out.print("FAIL");
486
failures++;
487
}
488
else {
489
System.out.print("PASS");
490
}
491
System.out.println(": " + pattern + ", " + string + ", matches: " + result);
492
493
494
if(expectedResult) {
495
String newPattern = pattern + "end";
496
p = new CopyOfPatternString(newPattern);
497
//String newString = string + "end";
498
result = p.startsWith(string);
499
if(result) {
500
System.out.print("PASS: ");
501
}
502
else {
503
System.out.print("FAIL: ");
504
failures++;
505
}
506
System.out.println(newPattern + ", " + string + ", starts with: " + result);
507
}
508
509
}
510
*/
511
512
}
513
514