forked from google/re2j
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPattern.java
More file actions
376 lines (328 loc) · 10.7 KB
/
Copy pathPattern.java
File metadata and controls
376 lines (328 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
/*
* Copyright (c) 2020 The Go Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package com.google.re2j;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* A compiled representation of an RE2 regular expression, mimicking the
* {@code java.util.regex.Pattern} API.
*
* <p>
* The matching functions take {@code String} arguments instead of the more general Java
* {@code CharSequence} since the latter doesn't provide UTF-16 decoding.
*
* <p>
* See the <a href='package.html'>package-level documentation</a> for an overview of how to use this
* API.
* </p>
*
* @author rsc@google.com (Russ Cox)
*/
public final class Pattern implements Serializable {
/** Flag: case insensitive matching. */
public static final int CASE_INSENSITIVE = 1;
/** Flag: dot ({@code .}) matches all characters, including newline. */
public static final int DOTALL = 2;
/**
* Flag: multiline matching: {@code ^} and {@code $} match at beginning and end of line, not just
* beginning and end of input.
*/
public static final int MULTILINE = 4;
/**
* Flag: Unicode groups (e.g. {@code \p\ Greek\} ) will be syntax errors.
*/
public static final int DISABLE_UNICODE_GROUPS = 8;
/**
* Flag: matches longest possible string.
*/
public static final int LONGEST_MATCH = 16;
// The pattern string at construction time.
private final String pattern;
// The flags at construction time.
private final int flags;
// The compiled RE2 regexp.
private transient final RE2 re2;
// This is visible for testing.
Pattern(String pattern, int flags, RE2 re2) {
if (pattern == null) {
throw new NullPointerException("pattern is null");
}
if (re2 == null) {
throw new NullPointerException("re2 is null");
}
this.pattern = pattern;
this.flags = flags;
this.re2 = re2;
}
/**
* Releases memory used by internal caches associated with this pattern. Does not change the
* observable behaviour. Useful for tests that detect memory leaks via allocation tracking.
*/
public void reset() {
re2.reset();
}
/**
* Returns the flags used in the constructor.
*/
public int flags() {
return flags;
}
/**
* Returns the pattern used in the constructor.
*/
public String pattern() {
return pattern;
}
RE2 re2() {
return re2;
}
/**
* Creates and returns a new {@code Pattern} corresponding to compiling {@code regex} with the
* default flags (0).
*
* @param regex the regular expression
* @throws PatternSyntaxException if the pattern is malformed
*/
public static Pattern compile(String regex) {
return compile(regex, regex, 0);
}
public static Pattern compile(String regex, boolean boundaryUnicode) {
return compile(regex, regex, 0, boundaryUnicode);
}
/**
* Creates and returns a new {@code Pattern} corresponding to compiling {@code regex} with the
* given {@code flags}.
*
* @param regex the regular expression
* @param flags bitwise OR of the flag constants {@code CASE_INSENSITIVE}, {@code DOTALL}, and
* {@code MULTILINE}
* @throws PatternSyntaxException if the regular expression is malformed
* @throws IllegalArgumentException if an unknown flag is given
*/
public static Pattern compile(String regex, int flags) {
String flregex = regex;
if ((flags & CASE_INSENSITIVE) != 0) {
flregex = "(?i)" + flregex;
}
if ((flags & DOTALL) != 0) {
flregex = "(?s)" + flregex;
}
if ((flags & MULTILINE) != 0) {
flregex = "(?m)" + flregex;
}
if ((flags & ~(MULTILINE | DOTALL | CASE_INSENSITIVE | DISABLE_UNICODE_GROUPS | LONGEST_MATCH))
!= 0) {
throw new IllegalArgumentException(
"Flags should only be a combination "
+ "of MULTILINE, DOTALL, CASE_INSENSITIVE, DISABLE_UNICODE_GROUPS, LONGEST_MATCH");
}
return compile(flregex, regex, flags);
}
/**
* Helper: create new Pattern with given regex and flags. Flregex is the regex with flags applied.
*/
private static Pattern compile(String flregex, String regex, int flags) {
int re2Flags = RE2.PERL;
if ((flags & DISABLE_UNICODE_GROUPS) != 0) {
re2Flags &= ~RE2.UNICODE_GROUPS;
}
return new Pattern(
regex, flags, RE2.compileImpl(flregex, re2Flags, (flags & LONGEST_MATCH) != 0));
}
private static Pattern compile(String flregex, String regex, int flags, boolean boundaryUnicode) {
int re2Flags = RE2.PERL;
if ((flags & DISABLE_UNICODE_GROUPS) != 0) {
re2Flags &= ~RE2.UNICODE_GROUPS;
}
return new Pattern(
regex, flags, RE2.compileImpl(flregex, re2Flags, (flags & LONGEST_MATCH) != 0, boundaryUnicode));
}
/**
* Matches a string against a regular expression.
*
* @param regex the regular expression
* @param input the input
* @return true if the regular expression matches the entire input
* @throws PatternSyntaxException if the regular expression is malformed
*/
public static boolean matches(String regex, CharSequence input) {
return compile(regex).matcher(input).matches();
}
public static boolean matches(String regex, byte[] input) {
return compile(regex).matcher(input).matches();
}
public boolean matches(String input) {
return this.matcher(input).matches();
}
public boolean matches(byte[] input) {
return this.matcher(input).matches();
}
/**
* Creates a new {@code Matcher} matching the pattern against the input.
*
* @param input the input string
*/
public Matcher matcher(CharSequence input) {
return new Matcher(this, input);
}
public Matcher matcher(byte[] input) {
return new Matcher(this, MatcherInput.utf8(input));
}
// This is visible for testing.
Matcher matcher(MatcherInput input) {
return new Matcher(this, input);
}
/**
* Splits input around instances of the regular expression. It returns an array giving the strings
* that occur before, between, and after instances of the regular expression. Empty strings that
* would occur at the end of the array are omitted.
*
* @param input the input string to be split
* @return the split strings
*/
public String[] split(String input) {
return split(input, 0);
}
/**
* Splits input around instances of the regular expression. It returns an array giving the strings
* that occur before, between, and after instances of the regular expression.
*
* <p>
* If {@code limit <= 0}, there is no limit on the size of the returned array. If
* {@code limit == 0}, empty strings that would occur at the end of the array are omitted. If
* {@code limit > 0}, at most limit strings are returned. The final string contains the remainder
* of the input, possibly including additional matches of the pattern.
*
* @param input the input string to be split
* @param limit the limit
* @return the split strings
*/
public String[] split(String input, int limit) {
return split(new Matcher(this, input), limit);
}
/** Helper: run split on m's input. */
private String[] split(Matcher m, int limit) {
List<String> result = new ArrayList<String>();
int emptiesSkipped = 0;
int last = 0;
while (m.find()) {
if (last == 0 && m.end() == 0) {
// Zero-width match at the beginning, skip (JDK8+ behavior).
last = m.end();
continue;
}
if (limit > 0 && result.size() == limit - 1) {
// no more room for matches
break;
}
if (last == m.start()) {
if (limit == 0) {
// Empty match, may or may not be trailing.
emptiesSkipped++;
last = m.end();
continue;
}
} else {
// If emptiesSkipped > 0 then limit == 0 and we have non-trailing empty matches to add before
// this non-empty match.
while (emptiesSkipped > 0) {
result.add("");
emptiesSkipped--;
}
}
result.add(m.substring(last, m.start()));
last = m.end();
}
if (limit == 0 && last != m.inputLength()) {
// Unlimited match, no more delimiters but we have a non-empty input at the end. Catch up any skipped empty
// matches, then emit the final match.
while (emptiesSkipped > 0) {
result.add("");
emptiesSkipped--;
}
result.add(m.substring(last, m.inputLength()));
}
if (limit != 0 || result.isEmpty()) {
result.add(m.substring(last, m.inputLength()));
}
return result.toArray(new String[0]);
}
/**
* Returns a literal pattern string for the specified string.
*
* <p>
* This method produces a string that can be used to create a <code>Pattern</code> that would
* match the string <code>s</code> as if it were a literal pattern.
* </p>
* Metacharacters or escape sequences in the input sequence will be given no special meaning.
*
* @param s The string to be literalized
* @return A literal string replacement
*/
public static String quote(String s) {
return RE2.quoteMeta(s);
}
@Override
public String toString() {
return pattern;
}
/**
* Returns the program size of this pattern.
*
* <p>
* Similar to the C++ implementation, the program size is a very approximate measure of a regexp's
* "cost". Larger numbers are more expensive than smaller numbers.
* </p>
*
* @return the program size of this pattern
*/
public int programSize() {
return re2.numberOfInstructions();
}
/**
* Returns the number of capturing groups in this matcher's pattern. Group zero denotes the entire
* pattern and is excluded from this count.
*
* @return the number of capturing groups in this pattern
*/
public int groupCount() {
return re2.numberOfCapturingGroups();
}
/**
* Return a map of the capturing groups in this matcher's pattern, where key is the name and value
* is the index of the group in the pattern.
*/
public Map<String, Integer> namedGroups() {
return Collections.unmodifiableMap(re2.namedGroups);
}
Object readResolve() {
// The deserialized version will be missing the RE2 instance, so we need to create a new,
// compiled version.
return Pattern.compile(pattern, flags);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Pattern other = (Pattern) o;
return flags == other.flags && pattern.equals(other.pattern);
}
@Override
public int hashCode() {
int result = pattern.hashCode();
result = 31 * result + flags;
return result;
}
private static final long serialVersionUID = 0;
}