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 package org.eclipse.jgit.fnmatch;
46
47 import java.text.MessageFormat;
48 import java.util.ArrayList;
49 import java.util.List;
50 import java.util.regex.Matcher;
51 import java.util.regex.Pattern;
52
53 import org.eclipse.jgit.errors.InvalidPatternException;
54 import org.eclipse.jgit.internal.JGitText;
55
56 final class GroupHead extends AbstractHead {
57 private final List<CharacterPattern> characterClasses;
58
59 private static final Pattern REGEX_PATTERN = Pattern
60 .compile("([^-][-][^-]|\\[[.:=].*?[.:=]\\])");
61
62 private final boolean inverse;
63
64 GroupHead(String pattern, String wholePattern)
65 throws InvalidPatternException {
66 super(false);
67 this.characterClasses = new ArrayList<>();
68 this.inverse = pattern.startsWith("!");
69 if (inverse) {
70 pattern = pattern.substring(1);
71 }
72 final Matcher matcher = REGEX_PATTERN.matcher(pattern);
73 while (matcher.find()) {
74 final String characterClass = matcher.group(0);
75 if (characterClass.length() == 3 && characterClass.charAt(1) == '-') {
76 final char start = characterClass.charAt(0);
77 final char end = characterClass.charAt(2);
78 characterClasses.add(new CharacterRange(start, end));
79 } else if (characterClass.equals("[:alnum:]")) {
80 characterClasses.add(LetterPattern.INSTANCE);
81 characterClasses.add(DigitPattern.INSTANCE);
82 } else if (characterClass.equals("[:alpha:]")) {
83 characterClasses.add(LetterPattern.INSTANCE);
84 } else if (characterClass.equals("[:blank:]")) {
85 characterClasses.add(new OneCharacterPattern(' '));
86 characterClasses.add(new OneCharacterPattern('\t'));
87 } else if (characterClass.equals("[:cntrl:]")) {
88 characterClasses.add(new CharacterRange('\u0000', '\u001F'));
89 characterClasses.add(new OneCharacterPattern('\u007F'));
90 } else if (characterClass.equals("[:digit:]")) {
91 characterClasses.add(DigitPattern.INSTANCE);
92 } else if (characterClass.equals("[:graph:]")) {
93 characterClasses.add(new CharacterRange('\u0021', '\u007E'));
94 characterClasses.add(LetterPattern.INSTANCE);
95 characterClasses.add(DigitPattern.INSTANCE);
96 } else if (characterClass.equals("[:lower:]")) {
97 characterClasses.add(LowerPattern.INSTANCE);
98 } else if (characterClass.equals("[:print:]")) {
99 characterClasses.add(new CharacterRange('\u0020', '\u007E'));
100 characterClasses.add(LetterPattern.INSTANCE);
101 characterClasses.add(DigitPattern.INSTANCE);
102 } else if (characterClass.equals("[:punct:]")) {
103 characterClasses.add(PunctPattern.INSTANCE);
104 } else if (characterClass.equals("[:space:]")) {
105 characterClasses.add(WhitespacePattern.INSTANCE);
106 } else if (characterClass.equals("[:upper:]")) {
107 characterClasses.add(UpperPattern.INSTANCE);
108 } else if (characterClass.equals("[:xdigit:]")) {
109 characterClasses.add(new CharacterRange('0', '9'));
110 characterClasses.add(new CharacterRange('a', 'f'));
111 characterClasses.add(new CharacterRange('A', 'F'));
112 } else if (characterClass.equals("[:word:]")) {
113 characterClasses.add(new OneCharacterPattern('_'));
114 characterClasses.add(LetterPattern.INSTANCE);
115 characterClasses.add(DigitPattern.INSTANCE);
116 } else {
117 final String message = MessageFormat.format(
118 JGitText.get().characterClassIsNotSupported,
119 characterClass);
120 throw new InvalidPatternException(message, wholePattern);
121 }
122
123 pattern = matcher.replaceFirst("");
124 matcher.reset(pattern);
125 }
126
127 for (int i = 0; i < pattern.length(); i++) {
128 final char c = pattern.charAt(i);
129 characterClasses.add(new OneCharacterPattern(c));
130 }
131 }
132
133
134 @Override
135 protected final boolean matches(char c) {
136 for (CharacterPattern pattern : characterClasses) {
137 if (pattern.matches(c)) {
138 return !inverse;
139 }
140 }
141 return inverse;
142 }
143
144 private interface CharacterPattern {
145
146
147
148
149
150 boolean matches(char c);
151 }
152
153 private static final class CharacterRange implements CharacterPattern {
154 private final char start;
155
156 private final char end;
157
158 CharacterRange(char start, char end) {
159 this.start = start;
160 this.end = end;
161 }
162
163 @Override
164 public final boolean matches(char c) {
165 return start <= c && c <= end;
166 }
167 }
168
169 private static final class DigitPattern implements CharacterPattern {
170 static final GroupHead.DigitPattern INSTANCE = new DigitPattern();
171
172 @Override
173 public final boolean matches(char c) {
174 return Character.isDigit(c);
175 }
176 }
177
178 private static final class LetterPattern implements CharacterPattern {
179 static final GroupHead.LetterPattern INSTANCE = new LetterPattern();
180
181 @Override
182 public final boolean matches(char c) {
183 return Character.isLetter(c);
184 }
185 }
186
187 private static final class LowerPattern implements CharacterPattern {
188 static final GroupHead.LowerPattern INSTANCE = new LowerPattern();
189
190 @Override
191 public final boolean matches(char c) {
192 return Character.isLowerCase(c);
193 }
194 }
195
196 private static final class UpperPattern implements CharacterPattern {
197 static final GroupHead.UpperPattern INSTANCE = new UpperPattern();
198
199 @Override
200 public final boolean matches(char c) {
201 return Character.isUpperCase(c);
202 }
203 }
204
205 private static final class WhitespacePattern implements CharacterPattern {
206 static final GroupHead.WhitespacePattern INSTANCE = new WhitespacePattern();
207
208 @Override
209 public final boolean matches(char c) {
210 return Character.isWhitespace(c);
211 }
212 }
213
214 private static final class OneCharacterPattern implements CharacterPattern {
215 private char expectedCharacter;
216
217 OneCharacterPattern(char c) {
218 this.expectedCharacter = c;
219 }
220
221 @Override
222 public final boolean matches(char c) {
223 return this.expectedCharacter == c;
224 }
225 }
226
227 private static final class PunctPattern implements CharacterPattern {
228 static final GroupHead.PunctPattern INSTANCE = new PunctPattern();
229
230 private static String punctCharacters = "-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~";
231
232 @Override
233 public boolean matches(char c) {
234 return punctCharacters.indexOf(c) != -1;
235 }
236 }
237
238 }