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 package com.aragost.javahg.internals;
27
28 import java.io.BufferedOutputStream;
29 import java.io.File;
30 import java.io.FileOutputStream;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.InputStreamReader;
34 import java.io.OutputStream;
35 import java.lang.reflect.Array;
36 import java.lang.reflect.InvocationTargetException;
37 import java.nio.ByteBuffer;
38 import java.nio.CharBuffer;
39 import java.nio.charset.CharacterCodingException;
40 import java.nio.charset.Charset;
41 import java.nio.charset.CharsetDecoder;
42 import java.util.Collection;
43 import java.util.Map;
44 import java.util.Random;
45 import java.util.concurrent.ExecutionException;
46 import java.util.regex.Pattern;
47
48 import com.aragost.javahg.log.Logger;
49 import com.aragost.javahg.log.LoggerFactory;
50 import com.google.common.io.ByteStreams;
51 import com.google.common.io.CharStreams;
52
53
54
55
56 public class Utils {
57
58 private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
59
60
61
62
63 private static final boolean IS_WINDOWS;
64
65
66
67
68
69 private static final File RESOURCES_TEMP_DIR;
70 static {
71 String osName = System.getProperty("os.name");
72 IS_WINDOWS = osName.startsWith("Windows");
73 File baseDirectory = new File(System.getProperty("user.home"), ".javahg" + File.separator + "tmp");
74 RESOURCES_TEMP_DIR = createTempDirectory(baseDirectory);
75 Runtime.getRuntime().addShutdownHook(new Thread() {
76 @Override
77 public void run() {
78 try {
79 Utils.deleteRecursive(RESOURCES_TEMP_DIR);
80 LOG.info("Deleted temp resource dir " + RESOURCES_TEMP_DIR);
81 } catch (IOException e) {
82 System.err.println("Could not delete temp resource dir " + RESOURCES_TEMP_DIR);
83 e.printStackTrace();
84 }
85 }
86 });
87 }
88
89 private static final Pattern HTTP_PATTERN = Pattern.compile("[hH][tT][tT][pP]:.*[@]");
90 private static final Pattern HTTPS_PATTERN = Pattern.compile("[hH][tT][tT][pP][sS]:.*[@]");
91 private static final Pattern SSH_PATTERN = Pattern.compile("[sS][sS][hH]:.*[@]");
92 private static final Pattern SVN_PATTERN = Pattern.compile("[sS][vV][nN]:.*[@]");
93
94
95
96
97
98 public static boolean isWindows() {
99 return IS_WINDOWS;
100 }
101
102 public static Charset getUtf8Charset() {
103 return Charset.forName("UTF-8");
104 }
105
106
107
108
109
110
111
112
113
114
115
116 public static int readBigEndian(InputStream stream) throws IOException {
117 byte b0 = (byte) stream.read();
118 byte b1 = (byte) stream.read();
119 byte b2 = (byte) stream.read();
120 int b3 = stream.read();
121 if (b3 == -1) {
122 return -1;
123 }
124 return (b0 << 24) + ((b1 & 0xFF) << 16) + ((b2 & 0xFF) << 8) + ((byte) b3 & 0xFF);
125 }
126
127
128
129
130
131
132
133
134
135
136 public static void writeBigEndian(int n, OutputStream stream) throws IOException {
137 byte[] bytes = new byte[] { (byte) (n >>> 24), (byte) (n >>> 16), (byte) (n >>> 8), (byte) n };
138 stream.write(bytes);
139 }
140
141
142
143
144
145
146
147 public static void consumeAll(InputStream stream) throws IOException {
148 while (stream.read(BUF) != -1)
149 ;
150 }
151
152 private static final byte[] BUF = new byte[256];
153
154
155
156
157
158
159
160
161
162
163
164 public static String readStream(InputStream in, CharsetDecoder decoder) {
165 try {
166 return CharStreams.toString(new InputStreamReader(in, decoder));
167 } catch (IOException e) {
168 throw new RuntimeIOException(e);
169 }
170 }
171
172
173
174
175
176
177
178
179 public static String decodeBytes(byte[] bytes, CharsetDecoder decoder) {
180 ByteBuffer wrap = ByteBuffer.wrap(bytes);
181 CharBuffer chars;
182 try {
183 chars = decoder.decode(wrap);
184 } catch (CharacterCodingException e) {
185 throw asRuntime(e);
186 }
187 return new String(chars.array(), chars.arrayOffset(), chars.limit());
188 }
189
190
191
192
193
194
195
196
197
198
199
200
201
202 public static <T> T[] arrayUnshift(T first, T[] rest) {
203 @SuppressWarnings("unchecked")
204 T[] result = (T[]) Array.newInstance(first.getClass(), 1 + rest.length);
205 result[0] = first;
206 System.arraycopy(rest, 0, result, 1, rest.length);
207 return result;
208 }
209
210
211
212
213
214
215
216
217
218
219
220 public static <T> T[] arrayConcat(T[] a, T[] b) {
221 @SuppressWarnings("unchecked")
222 T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
223 System.arraycopy(a, 0, result, 0, a.length);
224 System.arraycopy(b, 0, result, a.length, b.length);
225 return result;
226 }
227
228
229
230
231
232
233
234
235
236 public static String[] fileArray2StringArray(File[] files) {
237 String[] result = new String[files.length];
238 for (int i = 0; i < files.length; i++) {
239 File file = files[i];
240 result[i] = file.getPath();
241 }
242 return result;
243 }
244
245
246
247
248
249
250
251 public static File[] stringArray2FileArray(File baseDir, String[] strings) {
252 File[] result = new File[strings.length];
253 for (int i = 0; i < strings.length; i++) {
254 String s = strings[i];
255 File file = new File(s);
256 if (!file.isAbsolute()) {
257 file = new File(baseDir, s);
258 }
259 result[i] = Utils.resolveSymlinks(file);
260 }
261 return result;
262 }
263
264
265
266
267
268
269
270 public static File resolveSymlinks(File file) {
271
272
273 if (IS_WINDOWS) {
274 return file;
275 } else {
276 try {
277 return file.getCanonicalFile();
278 } catch (IOException e) {
279 throw new RuntimeIOException(e);
280 }
281 }
282 }
283
284
285
286
287
288
289
290
291
292
293
294 public static <T> T single(Collection<T> coll) {
295 switch (coll.size()) {
296 case 0:
297 return null;
298 case 1:
299 return coll.iterator().next();
300 default:
301 throw new IllegalArgumentException("Collection has more than one element");
302 }
303 }
304
305
306
307
308
309
310
311
312
313
314
315 public static File createTempFile(String suffix) {
316 try {
317 return File.createTempFile("javahg-", suffix);
318 } catch (IOException e) {
319 throw new RuntimeIOException(e);
320 }
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337 public static boolean isSymlink(File file) {
338 if (IS_WINDOWS) {
339 return false;
340 }
341 try {
342 File fileInCanonicalDir = null;
343 if (file.getParent() == null) {
344 fileInCanonicalDir = file;
345 } else {
346 File canonicalDir = file.getParentFile().getCanonicalFile();
347 fileInCanonicalDir = new File(canonicalDir, file.getName());
348 }
349
350 return !fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile());
351 } catch (IOException e) {
352 throw new RuntimeIOException(e);
353 }
354 }
355
356
357
358
359
360
361
362 public static RuntimeException asRuntime(Throwable e) {
363 if (e instanceof RuntimeException) {
364 return (RuntimeException) e;
365 }
366 if (e instanceof ExecutionException) {
367 return asRuntime(e.getCause());
368 }
369 if (e instanceof InvocationTargetException) {
370 return asRuntime(e.getCause());
371 }
372 if (e instanceof IOException) {
373 return new RuntimeIOException((IOException) e);
374 }
375 return new RuntimeException(e);
376 }
377
378
379
380
381
382
383
384 public static void deleteTempDir(File file) throws IOException {
385 file = file.getCanonicalFile();
386 String javaTmpDir = new File(System.getProperty("java.io.tmpdir")).getCanonicalPath();
387 if (file.getPath().startsWith(javaTmpDir)) {
388 deleteRecursive(file);
389 } else {
390 throw new IllegalArgumentException("Can only delete files in " + javaTmpDir);
391 }
392 }
393
394 private static void deleteRecursive(File file) throws IOException {
395 if (file.isDirectory()) {
396 File[] files = file.listFiles();
397 for (File f : files) {
398 deleteRecursive(f);
399 }
400 }
401
402
403
404
405
406
407
408
409
410
411 for (int i = 20; !file.delete(); i--) {
412 if (i == 0) {
413 throw new IOException("Failed to delete: " + file);
414 }
415 try {
416 System.err.println("Sleep a bit and try again to delete " + file + "[" + i + "/20]");
417 Thread.sleep(250);
418 } catch (InterruptedException e) {
419 }
420 }
421 }
422
423
424
425
426
427
428
429 private static File createTempDirectory( File parent ) {
430 String name = System.currentTimeMillis() + "-";
431 for (int i=0; i<100; i++) {
432 File directory = new File(parent, name + i);
433 if ( ! directory.exists() && directory.mkdirs() ){
434 return directory;
435 }
436 }
437 throw new IllegalStateException("could not create a temp directory at " + parent);
438 }
439
440
441
442
443
444
445
446
447
448
449 public static File resourceAsFile(String resourceName) {
450 return resourceAsFile(resourceName, null);
451 }
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466 public static File resourceAsFile(String resourceName, Map<String, byte[]> replacements) {
467 File file = new File(RESOURCES_TEMP_DIR, resourceName);
468 if (!file.exists()) {
469 file.getParentFile().mkdirs();
470 InputStream stream = Utils.class.getResourceAsStream(resourceName);
471 try {
472 OutputStream fileOutput = new BufferedOutputStream(new FileOutputStream(file));
473 OutputStream output = fileOutput;
474 if (replacements != null) {
475 output = new PatternReplacingOutputStream(fileOutput, replacements);
476 }
477 ByteStreams.copy(stream, output);
478 output.close();
479 } catch (IOException e) {
480 throw new RuntimeIOException(e);
481 }
482 LOG.info("Copy resource {} to {}", resourceName, file.getAbsolutePath());
483 }
484 return file;
485 }
486
487
488
489
490
491
492
493
494
495
496 public static byte[] randomBytes() {
497 byte[] bytes = new byte[20];
498 new Random().nextBytes(bytes);
499
500
501 assert '"' + 64 < 126;
502 for (int i = 0; i < bytes.length; i++) {
503 int b = '"' + 1 + (bytes[i] & 63);
504 if (b == '\\') {
505 b = 126;
506 }
507 bytes[i] = (byte) b;
508 }
509 LOG.info("Random bytes generated: {}", new String(bytes));
510 return bytes;
511 }
512
513 public static String obfuscateLoginData(String line) {
514 String myLine = (line == null ? "" : line);
515 myLine = HTTP_PATTERN.matcher(myLine).replaceAll("http://***@");
516 if (myLine.equals(line)) {
517 myLine = HTTPS_PATTERN.matcher(line).replaceAll("https://***@");
518 }
519 if (myLine.equals(line)) {
520 myLine = SSH_PATTERN.matcher(line).replaceAll("ssh://***@");
521 }
522 if (myLine.equals(line)) {
523 myLine = SVN_PATTERN.matcher(line).replaceAll("svn://***@");
524 }
525 return myLine;
526 }
527 }