1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.linecorp.centraldogma.internal;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static java.util.Objects.requireNonNull;
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.StringReader;
26 import java.nio.file.Files;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31
32 import com.jayway.jsonpath.JsonPath;
33
34
35
36
37
38 public final class Util {
39
40 private static final Pattern FILE_NAME_PATTERN = Pattern.compile(
41 "^(?:[-_0-9a-zA-Z](?:[-_.0-9a-zA-Z]*[-_0-9a-zA-Z])?)+$");
42 private static final Pattern FILE_PATH_PATTERN = Pattern.compile(
43 "^(?:/[-_0-9a-zA-Z](?:[-_.0-9a-zA-Z]*[-_0-9a-zA-Z])?)+$");
44 private static final Pattern JSON_FILE_PATH_PATTERN = Pattern.compile(
45 "^(?:/[-_0-9a-zA-Z](?:[-_.0-9a-zA-Z]*[-_0-9a-zA-Z])?)+\\.(?i)json$");
46 private static final Pattern DIR_PATH_PATTERN = Pattern.compile(
47 "^(?:/[-_0-9a-zA-Z](?:[-_.0-9a-zA-Z]*[-_0-9a-zA-Z])?)*/?$");
48 private static final Pattern PATH_PATTERN_PATTERN = Pattern.compile("^[- /*_.,0-9a-zA-Z]+$");
49 private static final Pattern EMAIL_PATTERN = Pattern.compile(
50 "^[_A-Za-z0-9-+]+(?:\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(?:\\.[A-Za-z0-9]+)*(?:\\.[A-Za-z]{2,})$");
51 private static final Pattern GENERAL_EMAIL_PATTERN = Pattern.compile(
52 "^[_A-Za-z0-9-+]+(?:\\.[_A-Za-z0-9-]+)*@(.+)$");
53
54
55
56
57
58
59 private static final Pattern PROJECT_AND_REPO_NAME_PATTERN =
60 Pattern.compile("^[0-9A-Za-z](?:[-+_0-9A-Za-z.]*[0-9A-Za-z])?$");
61
62 public static String validateFileName(String name, String paramName) {
63 requireNonNull(name, paramName);
64 checkArgument(isValidFileName(name),
65 "%s: %s (expected: %s)", paramName, name, FILE_NAME_PATTERN);
66 return name;
67 }
68
69 public static boolean isValidFileName(String name) {
70 requireNonNull(name, "name");
71 return !name.isEmpty() && FILE_NAME_PATTERN.matcher(name).matches();
72 }
73
74 public static String validateFilePath(String path, String paramName) {
75 requireNonNull(path, paramName);
76 checkArgument(isValidFilePath(path),
77 "%s: %s (expected: %s)", paramName, path, FILE_PATH_PATTERN);
78 return path;
79 }
80
81 public static boolean isValidFilePath(String path) {
82 requireNonNull(path, "path");
83 return !path.isEmpty() && path.charAt(0) == '/' &&
84 FILE_PATH_PATTERN.matcher(path).matches();
85 }
86
87 public static String validateJsonFilePath(String path, String paramName) {
88 requireNonNull(path, paramName);
89 checkArgument(isValidJsonFilePath(path),
90 "%s: %s (expected: %s)", paramName, path, JSON_FILE_PATH_PATTERN);
91 return path;
92 }
93
94 public static boolean isValidJsonFilePath(String path) {
95 requireNonNull(path, "path");
96 return !path.isEmpty() && path.charAt(0) == '/' &&
97 JSON_FILE_PATH_PATTERN.matcher(path).matches();
98 }
99
100 public static String validateJsonPath(String jsonPath, String paramName) {
101 requireNonNull(jsonPath, paramName);
102 checkArgument(isValidJsonPath(jsonPath),
103 "%s: %s (expected: a valid JSON path)", paramName, jsonPath);
104 return jsonPath;
105 }
106
107 public static boolean isValidJsonPath(String jsonPath) {
108 try {
109 JsonPath.compile(jsonPath);
110 return true;
111 } catch (Exception e) {
112 return false;
113 }
114 }
115
116 public static String validateDirPath(String path, String paramName) {
117 requireNonNull(path, paramName);
118 checkArgument(isValidDirPath(path),
119 "%s: %s (expected: %s)", paramName, path, DIR_PATH_PATTERN);
120 return path;
121 }
122
123 public static boolean isValidDirPath(String path) {
124 return isValidDirPath(path, false);
125 }
126
127 public static boolean isValidDirPath(String path, boolean mustEndWithSlash) {
128 requireNonNull(path);
129 if (mustEndWithSlash && !path.endsWith("/")) {
130 return false;
131 }
132 return !path.isEmpty() && path.charAt(0) == '/' &&
133 DIR_PATH_PATTERN.matcher(path).matches();
134 }
135
136 public static String validatePathPattern(String pathPattern, String paramName) {
137 requireNonNull(pathPattern, paramName);
138 checkArgument(isValidPathPattern(pathPattern),
139 "%s: %s (expected: %s)", paramName, pathPattern, PATH_PATTERN_PATTERN);
140 return pathPattern;
141 }
142
143 public static boolean isValidPathPattern(String pathPattern) {
144 requireNonNull(pathPattern, "pathPattern");
145 return PATH_PATTERN_PATTERN.matcher(pathPattern).matches();
146 }
147
148 public static String validateProjectName(String projectName, String paramName) {
149 requireNonNull(projectName, paramName);
150 checkArgument(isValidProjectName(projectName),
151 "%s: %s (expected: %s)", paramName, projectName, PROJECT_AND_REPO_NAME_PATTERN);
152 return projectName;
153 }
154
155 public static boolean isValidProjectName(String projectName) {
156 requireNonNull(projectName, "projectName");
157 return PROJECT_AND_REPO_NAME_PATTERN.matcher(projectName).matches();
158 }
159
160 public static String validateRepositoryName(String repoName, String paramName) {
161 requireNonNull(repoName, paramName);
162 checkArgument(isValidRepositoryName(repoName),
163 "%s: %s (expected: %s)", paramName, repoName, PROJECT_AND_REPO_NAME_PATTERN);
164 return repoName;
165 }
166
167 public static boolean isValidRepositoryName(String repoName) {
168 requireNonNull(repoName, "repoName");
169 return PROJECT_AND_REPO_NAME_PATTERN.matcher(repoName).matches();
170 }
171
172 public static String validateEmailAddress(String emailAddr, String paramName) {
173 requireNonNull(emailAddr, paramName);
174 checkArgument(isValidEmailAddress(emailAddr),
175 "%s: %s (expected: a valid e-mail address)", paramName, emailAddr);
176 return emailAddr;
177 }
178
179 public static boolean isValidEmailAddress(String emailAddr) {
180 requireNonNull(emailAddr, "emailAddr");
181 if (EMAIL_PATTERN.matcher(emailAddr).matches()) {
182 return true;
183 }
184
185 final Matcher m = GENERAL_EMAIL_PATTERN.matcher(emailAddr);
186 if (m.matches()) {
187 final String domainPart = m.group(1);
188 return isValidIpV4Address(domainPart) ||
189 isValidIpV6Address(domainPart);
190 }
191 return false;
192 }
193
194 public static String toEmailAddress(String emailAddr, String paramName) {
195 requireNonNull(emailAddr, paramName);
196 if (isValidEmailAddress(emailAddr)) {
197 return emailAddr;
198 }
199 return emailAddr + "@localhost.localdomain";
200 }
201
202 public static String emailToUsername(String emailAddr, String paramName) {
203 validateEmailAddress(emailAddr, paramName);
204 return emailAddr.substring(0, emailAddr.indexOf('@'));
205 }
206
207 public static List<String> stringToLines(String str) {
208 final BufferedReader reader = new BufferedReader(new StringReader(str));
209 final List<String> lines = new ArrayList<>(128);
210 try {
211 String line;
212 while ((line = reader.readLine()) != null) {
213 lines.add(line);
214 }
215 } catch (IOException ignored) {
216
217 }
218 return lines;
219 }
220
221
222
223
224 public static String simpleTypeName(Object obj) {
225 if (obj == null) {
226 return "null";
227 }
228
229 return simpleTypeName(obj.getClass(), false);
230 }
231
232
233
234
235 public static String simpleTypeName(Class<?> clazz) {
236 return simpleTypeName(clazz, false);
237 }
238
239
240
241
242 public static String simpleTypeName(Class<?> clazz, boolean decapitalize) {
243 if (clazz == null) {
244 return "null";
245 }
246
247 String className = clazz.getName();
248 final int lastDotIdx = className.lastIndexOf('.');
249 if (lastDotIdx >= 0) {
250 className = className.substring(lastDotIdx + 1);
251 }
252
253 if (!decapitalize) {
254 return className;
255 }
256
257 final StringBuilder buf = new StringBuilder(className.length());
258 boolean lowercase = true;
259 for (int i = 0; i < className.length(); i++) {
260 final char c1 = className.charAt(i);
261 final char c2;
262 if (lowercase) {
263 c2 = Character.toLowerCase(c1);
264 if (c1 == c2) {
265 lowercase = false;
266 }
267 } else {
268 c2 = c1;
269 }
270 buf.append(c2);
271 }
272
273 return buf.toString();
274 }
275
276
277
278
279 @SuppressWarnings("unchecked")
280 public static <T> T unsafeCast(Object o) {
281 return (T) o;
282 }
283
284
285
286
287 public static <T> Iterable<T> requireNonNullElements(Iterable<T> values, String name) {
288 requireNonNull(values, name);
289
290 int i = 0;
291 for (T v : values) {
292 if (v == null) {
293 throw new NullPointerException(name + '[' + i + ']');
294 }
295 i++;
296 }
297
298 return values;
299 }
300
301 private static boolean isValidIpV4Address(String ip) {
302 return isValidIpV4Address(ip, 0, ip.length());
303 }
304
305 @SuppressWarnings("DuplicateBooleanBranch")
306 private static boolean isValidIpV4Address(String ip, int from, int toExcluded) {
307 final int len = toExcluded - from;
308 int i;
309 return len <= 15 && len >= 7 &&
310 (i = ip.indexOf('.', from + 1)) > 0 && isValidIpV4Word(ip, from, i) &&
311 (i = ip.indexOf('.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
312 (i = ip.indexOf('.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
313 isValidIpV4Word(ip, i + 1, toExcluded);
314 }
315
316 private static boolean isValidIpV4Word(CharSequence word, int from, int toExclusive) {
317 final int len = toExclusive - from;
318 final char c0;
319 final char c1;
320 final char c2;
321 if (len < 1 || len > 3 || (c0 = word.charAt(from)) < '0') {
322 return false;
323 }
324 if (len == 3) {
325 return (c1 = word.charAt(from + 1)) >= '0' &&
326 (c2 = word.charAt(from + 2)) >= '0' &&
327 (c0 <= '1' && c1 <= '9' && c2 <= '9' ||
328 c0 == '2' && c1 <= '5' && (c2 <= '5' || c1 < '5' && c2 <= '9'));
329 }
330 return c0 <= '9' && (len == 1 || isValidNumericChar(word.charAt(from + 1)));
331 }
332
333 private static boolean isValidIpV6Address(String ip) {
334 int end = ip.length();
335 if (end < 2) {
336 return false;
337 }
338
339
340 int start;
341 char c = ip.charAt(0);
342 if (c == '[') {
343 end--;
344 if (ip.charAt(end) != ']') {
345
346 return false;
347 }
348 start = 1;
349 c = ip.charAt(1);
350 } else {
351 start = 0;
352 }
353
354 int colons;
355 int compressBegin;
356 if (c == ':') {
357
358 if (ip.charAt(start + 1) != ':') {
359 return false;
360 }
361 colons = 2;
362 compressBegin = start;
363 start += 2;
364 } else {
365 colons = 0;
366 compressBegin = -1;
367 }
368
369 int wordLen = 0;
370 loop:
371 for (int i = start; i < end; i++) {
372 c = ip.charAt(i);
373 if (isValidHexChar(c)) {
374 if (wordLen < 4) {
375 wordLen++;
376 continue;
377 }
378 return false;
379 }
380
381 switch (c) {
382 case ':':
383 if (colons > 7) {
384 return false;
385 }
386 if (ip.charAt(i - 1) == ':') {
387 if (compressBegin >= 0) {
388 return false;
389 }
390 compressBegin = i - 1;
391 } else {
392 wordLen = 0;
393 }
394 colons++;
395 break;
396 case '.':
397
398
399
400 if (compressBegin < 0 && colons != 6 ||
401
402
403 colons == 7 && compressBegin >= start || colons > 7) {
404 return false;
405 }
406
407
408
409
410 final int ipv4Start = i - wordLen;
411 int j = ipv4Start - 2;
412 if (isValidIPv4MappedChar(ip.charAt(j))) {
413 if (!isValidIPv4MappedChar(ip.charAt(j - 1)) ||
414 !isValidIPv4MappedChar(ip.charAt(j - 2)) ||
415 !isValidIPv4MappedChar(ip.charAt(j - 3))) {
416 return false;
417 }
418 j -= 5;
419 }
420
421 for (; j >= start; --j) {
422 final char tmpChar = ip.charAt(j);
423 if (tmpChar != '0' && tmpChar != ':') {
424 return false;
425 }
426 }
427
428
429 int ipv4End = ip.indexOf('%', ipv4Start + 7);
430 if (ipv4End < 0) {
431 ipv4End = end;
432 }
433 return isValidIpV4Address(ip, ipv4Start, ipv4End);
434 case '%':
435
436 end = i;
437 break loop;
438 default:
439 return false;
440 }
441 }
442
443
444 if (compressBegin < 0) {
445 return colons == 7 && wordLen > 0;
446 }
447
448 return compressBegin + 2 == end ||
449
450 wordLen > 0 && (colons < 8 || compressBegin <= start);
451 }
452
453 private static boolean isValidNumericChar(char c) {
454 return c >= '0' && c <= '9';
455 }
456
457 private static boolean isValidIPv4MappedChar(char c) {
458 return c == 'f' || c == 'F';
459 }
460
461 private static boolean isValidHexChar(char c) {
462 return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f';
463 }
464
465
466
467
468 public static void deleteFileTree(File directory) throws IOException {
469 if (directory.exists()) {
470 Files.walkFileTree(directory.toPath(), DeletingFileVisitor.INSTANCE);
471 }
472 }
473
474 private Util() {}
475 }