001 /* 002 * Copyright (C) 2012 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 020 package org.crsh.util; 021 022 import javax.naming.Context; 023 import java.io.ByteArrayOutputStream; 024 import java.io.Closeable; 025 import java.io.Flushable; 026 import java.io.IOException; 027 import java.io.InputStream; 028 import java.io.OutputStream; 029 import java.lang.reflect.ParameterizedType; 030 import java.lang.reflect.Type; 031 import java.lang.reflect.TypeVariable; 032 import java.net.Socket; 033 import java.nio.charset.Charset; 034 import java.sql.*; 035 import java.io.File; 036 import java.net.URISyntaxException; 037 import java.net.URL; 038 import java.util.ArrayList; 039 import java.util.Arrays; 040 import java.util.Collections; 041 import java.util.HashMap; 042 import java.util.HashSet; 043 import java.util.Iterator; 044 import java.util.List; 045 import java.util.Map; 046 import java.util.NoSuchElementException; 047 import java.util.regex.Matcher; 048 import java.util.regex.Pattern; 049 050 public class Utils { 051 052 /** . */ 053 private static final Iterator EMPTY_ITERATOR = Collections.emptyList().iterator(); 054 055 /** . */ 056 private static final Pattern p = Pattern.compile("\\S+"); 057 058 public static <E> Iterator<E> iterator() { 059 @SuppressWarnings("unchecked") 060 Iterator<E> iterator = (Iterator<E>)EMPTY_ITERATOR; 061 return iterator; 062 } 063 064 public static <E> Iterator<E> iterator(final E element) { 065 return new BaseIterator<E>() { 066 boolean hasNext = true; 067 @Override 068 public boolean hasNext() { 069 return hasNext; 070 } 071 @Override 072 public E next() { 073 if (hasNext) { 074 hasNext = false; 075 return element; 076 } else { 077 throw new NoSuchElementException(); 078 } 079 } 080 }; 081 } 082 083 public static <E> E first(Iterable<E> elements) { 084 Iterator<E> i = elements.iterator(); 085 return i.hasNext() ? i.next() : null; 086 } 087 088 public static <K, V, M extends Map<K, V>> M map(M map, K key, V value) { 089 map.put(key, value); 090 return map; 091 } 092 093 public static <K, V> HashMap<K, V> map(K key, V value) { 094 HashMap<K, V> map = new HashMap<K, V>(); 095 map.put(key, value); 096 return map; 097 } 098 099 public static <E> HashSet<E> set(E... elements) { 100 HashSet<E> set = new HashSet<E>(elements.length); 101 Collections.addAll(set, elements); 102 return set; 103 } 104 105 public static <E> List<E> list(E... elements) { 106 return Arrays.asList(elements); 107 } 108 109 public static <E> List<E> list(Iterable<E> iterable) { 110 return list(iterable.iterator()); 111 } 112 113 public static <E> List<E> list(Iterator<E> iterator) { 114 ArrayList<E> list = new ArrayList<E>(); 115 while (iterator.hasNext()) { 116 list.add(iterator.next()); 117 } 118 return list; 119 } 120 121 public static int indexOf(CharSequence s, int off, char c) { 122 for (int len = s.length();off < len;off++) { 123 if (s.charAt(off) == c) { 124 return off; 125 } 126 } 127 return -1; 128 } 129 130 public static String trimLeft(String s) { 131 if (s == null) { 132 throw new NullPointerException("No null string accepted"); 133 } 134 int index = 0; 135 int len = s.length(); 136 while (index < len) { 137 if (s.charAt(index) == ' ') { 138 index++; 139 } else { 140 break; 141 } 142 } 143 if (index > 0) { 144 return s.substring(index); 145 } else { 146 return s; 147 } 148 } 149 150 private static final Pattern blankPattern = Pattern.compile("^\\s*$"); 151 152 public static boolean notBlank(String s) { 153 return !blankPattern.matcher(s).find(); 154 } 155 156 public static <E> E notNull(E e1, E e2) { 157 if (e1 != null) { 158 return e1; 159 } else { 160 return e2; 161 } 162 } 163 164 /** 165 * Return the value when it is positive otherwise return 0. 166 * 167 * @param value the value 168 * @return the non negative value 169 */ 170 public static int notNegative(int value) { 171 return value >= 0 ? value : 0; 172 } 173 174 private static final Pattern FOO = Pattern.compile("" + 175 "(\\*)" + // Wildcard * 176 "|" + 177 "(\\?)" + // Wildcard ? 178 "|" + 179 "(?:\\[([^)]+)\\])" + // Range 180 "|" + 181 "(\\\\.)" // Escape 182 ); 183 184 /** 185 * Create a pattern that transforms a glob expression into a regular expression, the following task are supported 186 * <ul> 187 * <li>* : Match any number of unknown characters</li> 188 * <li>? : Match one unknown character</li> 189 * <li>[characters] : Match a character as part of a group of characters</li> 190 * <li>\ : Escape character</li> 191 * </ul> 192 * 193 * @param globex the glob expression 194 * @return the regular expression 195 * @throws NullPointerException when the globex argument is null 196 */ 197 public static String globexToRegex(String globex) throws NullPointerException { 198 if (globex == null) { 199 throw new NullPointerException("No null globex accepted"); 200 } 201 StringBuilder regex = new StringBuilder(); 202 int prev = 0; 203 Matcher matcher = FOO.matcher(globex); 204 while (matcher.find()) { 205 int next = matcher.start(); 206 if (next > prev) { 207 regex.append(Pattern.quote(globex.substring(prev, next))); 208 } 209 if (matcher.group(1) != null) { 210 regex.append(".*"); 211 } else if (matcher.group(2) != null) { 212 regex.append("."); 213 } else if (matcher.group(3) != null) { 214 regex.append("["); 215 regex.append(Pattern.quote(matcher.group(3))); 216 regex.append("]"); 217 } else if (matcher.group(4) != null) { 218 regex.append(Pattern.quote(Character.toString(matcher.group(4).charAt(1)))); 219 } else { 220 throw new UnsupportedOperationException("Not handled yet"); 221 } 222 prev = matcher.end(); 223 } 224 if (prev < globex.length()) { 225 regex.append(Pattern.quote(globex.substring(prev))); 226 } 227 return regex.toString(); 228 } 229 230 /** 231 <<<<<<< HEAD 232 * Close the socket and catch any exception thrown. 233 * 234 * @param socket the socket to close 235 * @return any Exception thrown during the <code>close</code> operation 236 */ 237 public static Exception close(Socket socket) { 238 if (socket != null) { 239 try { 240 socket.close(); 241 } 242 catch (Exception e) { 243 return e; 244 } 245 } 246 return null; 247 } 248 249 /** 250 * Close the closeable and catch any exception thrown. 251 * 252 * @param closeable the closeable to close 253 * @return any Exception thrown during the <code>close</code> operation 254 */ 255 public static Exception close(Closeable closeable) { 256 if (closeable != null) { 257 try { 258 closeable.close(); 259 } 260 catch (Exception e) { 261 return e; 262 } 263 } 264 return null; 265 } 266 267 /** 268 * Close the connection and catch any exception thrown. 269 * 270 * @param connection the socket to close 271 * @return any Exception thrown during the <code>close</code> operation 272 */ 273 public static Exception close(Connection connection) { 274 if (connection != null) { 275 try { 276 connection.close(); 277 } 278 catch (Exception e) { 279 return e; 280 } 281 } 282 return null; 283 } 284 285 /** 286 * Close the statement and catch any exception thrown. 287 * 288 * @param statement the statement to close 289 * @return any Exception thrown during the <code>close</code> operation 290 */ 291 public static Exception close(java.sql.Statement statement) { 292 if (statement != null) { 293 try { 294 statement.close(); 295 } 296 catch (Exception e) { 297 return e; 298 } 299 } 300 return null; 301 } 302 303 /** 304 * Close the result set and catch any exception thrown. 305 * 306 * @param rs the result set to close 307 * @return any Exception thrown during the <code>close</code> operation 308 */ 309 public static Exception close(ResultSet rs) { 310 if (rs != null) { 311 try { 312 rs.close(); 313 } 314 catch (Exception e) { 315 return e; 316 } 317 } 318 return null; 319 } 320 321 /** 322 * Close the context and catch any exception thrown. 323 * 324 * @param context the context to close 325 * @return any Exception thrown during the <code>close</code> operation 326 */ 327 public static Exception close(Context context) { 328 if (context != null) { 329 try { 330 context.close(); 331 } 332 catch (Exception e) { 333 return e; 334 } 335 } 336 return null; 337 } 338 339 public static <T extends Throwable> void rethrow(Class<T> throwableClass, Throwable cause) throws T { 340 T throwable; 341 342 // 343 try { 344 throwable = throwableClass.newInstance(); 345 } 346 catch (Exception e) { 347 throw new AssertionError(e); 348 } 349 350 // 351 throwable.initCause(cause); 352 353 // 354 throw throwable; 355 } 356 357 public static boolean equals(Object o1, Object o2) { 358 return o1 == null ? o2 == null : (o2 != null && o1.equals(o2)); 359 } 360 361 public static boolean notEquals(Object o1, Object o2) { 362 return !equals(o1, o2); 363 } 364 365 /** 366 * Flush the flushable and catch any exception thrown. 367 * 368 * @param flushable the flushable to flush 369 * @return any Exception thrown during the <code>flush</code> operation 370 */ 371 public static Exception flush(Flushable flushable) { 372 if (flushable != null) { 373 try { 374 flushable.flush(); 375 } 376 catch (Exception e) { 377 return e; 378 } 379 } 380 return null; 381 } 382 383 public static List<String> chunks(CharSequence s) { 384 List<String> chunks = new ArrayList<String>(); 385 Matcher m = p.matcher(s); 386 while (m.find()) { 387 chunks.add(m.group()); 388 } 389 return chunks; 390 } 391 392 public static String join(Iterable<String> strings, String separator) { 393 Iterator<String> i = strings.iterator(); 394 if (i.hasNext()) { 395 String first = i.next(); 396 if (i.hasNext()) { 397 StringBuilder buf = new StringBuilder(); 398 buf.append(first); 399 while (i.hasNext()) { 400 buf.append(separator); 401 buf.append(i.next()); 402 } 403 return buf.toString(); 404 } else { 405 return first; 406 } 407 } else { 408 return ""; 409 } 410 } 411 412 public static String[] split(CharSequence s, char separator) { 413 return foo(s, separator, 0, 0, 0); 414 } 415 416 public static String[] split(CharSequence s, char separator, int rightPadding) { 417 if (rightPadding < 0) { 418 throw new IllegalArgumentException("Right padding cannot be negative"); 419 } 420 return foo(s, separator, 0, 0, rightPadding); 421 } 422 423 private static String[] foo(CharSequence s, char separator, int count, int from, int rightPadding) { 424 int len = s.length(); 425 if (from < len) { 426 int to = from; 427 while (to < len && s.charAt(to) != separator) { 428 to++; 429 } 430 String[] ret; 431 if (to == len - 1) { 432 ret = new String[count + 2 + rightPadding]; 433 ret[count + 1] = ""; 434 } 435 else { 436 ret = to == len ? new String[count + 1 + rightPadding] : foo(s, separator, count + 1, to + 1, rightPadding); 437 } 438 ret[count] = from == to ? "" : s.subSequence(from, to).toString(); 439 return ret; 440 } 441 else if (from == len) { 442 return new String[count + rightPadding]; 443 } 444 else { 445 throw new AssertionError(); 446 } 447 } 448 449 /** 450 * @see #findLongestCommonPrefix(Iterable) 451 */ 452 public static String findLongestCommonPrefix(CharSequence... seqs) { 453 return findLongestCommonPrefix(Arrays.asList(seqs)); 454 } 455 456 /** 457 * Find the longest possible common prefix of the provided char sequence. 458 * 459 * @param seqs the sequences 460 * @return the longest possible prefix 461 */ 462 public static String findLongestCommonPrefix(Iterable<? extends CharSequence> seqs) { 463 String common = ""; 464 out: 465 while (true) { 466 String candidate = null; 467 for (CharSequence s : seqs) { 468 if (common.length() + 1 > s.length()) { 469 break out; 470 } else { 471 if (candidate == null) { 472 candidate = s.subSequence(0, common.length() + 1).toString(); 473 } else if (s.subSequence(0, common.length() + 1).toString().equals(candidate)) { 474 // Ok it is a prefix 475 } else { 476 break out; 477 } 478 } 479 } 480 if (candidate == null) { 481 break; 482 } else { 483 common = candidate; 484 } 485 } 486 return common; 487 } 488 489 public static byte[] readAsBytes(InputStream in) throws IOException { 490 return read(in).toByteArray(); 491 } 492 493 public static String readAsUTF8(InputStream in) { 494 try { 495 ByteArrayOutputStream baos = read(in); 496 return baos.toString("UTF-8"); 497 } 498 catch (IOException e) { 499 e.printStackTrace(); 500 return null; 501 } 502 } 503 504 public static void copy(InputStream in, OutputStream out) throws IOException { 505 if (in == null) { 506 throw new NullPointerException(); 507 } 508 try { 509 byte[] buffer = new byte[256]; 510 for (int l = in.read(buffer); l != -1; l = in.read(buffer)) { 511 out.write(buffer, 0, l); 512 } 513 } 514 finally { 515 close(in); 516 } 517 } 518 519 private static ByteArrayOutputStream read(InputStream in) throws IOException { 520 if (in == null) { 521 throw new NullPointerException(); 522 } 523 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 524 copy(in, baos); 525 return baos; 526 } 527 528 /* 529 * Convert an file URL to a file, avoids issues on windows with whitespaces. 530 * 531 * @param url the URL to convert 532 * @return the related file 533 * @throws java.lang.IllegalArgumentException if the url protocol is not file 534 * @throws java.lang.NullPointerException if the url argument is null 535 */ 536 public static File toFile(URL url) throws IllegalArgumentException, NullPointerException { 537 if (url == null) { 538 throw new NullPointerException("No null URL accepted"); 539 } 540 if (!url.getProtocol().equals("file")) { 541 throw new IllegalArgumentException("Not file protocol"); 542 } 543 try { 544 return new File(url.toURI()); 545 } catch(URISyntaxException e) { 546 return new File(url.getPath()); 547 } 548 } 549 550 public static Class<?> resolveToClass(Type implementation, Class<?> type, int parameterIndex) { 551 if (implementation == null) { 552 throw new NullPointerException("No null type accepted"); 553 } 554 555 // First resolve to type 556 Type resolvedType = resolve(implementation, type, parameterIndex); 557 558 // 559 if (resolvedType != null) { 560 return resolveToClass(resolvedType); 561 } else { 562 return null; 563 } 564 } 565 566 public static Class resolveToClass(Type type) { 567 if (type == null) { 568 throw new NullPointerException("No null type accepted"); 569 } 570 if (type instanceof Class<?>) { 571 return (Class<?>)type; 572 } else if (type instanceof TypeVariable) { 573 TypeVariable resolvedTypeVariable = (TypeVariable)type; 574 return resolveToClass(resolvedTypeVariable.getBounds()[0]); 575 } else { 576 throw new UnsupportedOperationException("Type resolution of " + type + " not yet implemented"); 577 } 578 } 579 580 /** 581 * A simplistic implementation, it may not handle all cases but it should handle enough. 582 * 583 * @param implementation the type for which the parameter requires a resolution 584 * @param type the type that owns the parameter 585 * @param parameterIndex the parameter index 586 * @return the resolved type 587 */ 588 public static Type resolve(Type implementation, Class<?> type, int parameterIndex) { 589 if (implementation == null) { 590 throw new NullPointerException(); 591 } 592 593 // 594 if (implementation == type) { 595 TypeVariable<? extends Class<?>>[] tp = type.getTypeParameters(); 596 if (parameterIndex < tp.length) { 597 return tp[parameterIndex]; 598 } else { 599 throw new IllegalArgumentException(); 600 } 601 } else if (implementation instanceof Class<?>) { 602 Class<?> c = (Class<?>) implementation; 603 Type gsc = c.getGenericSuperclass(); 604 Type resolved = null; 605 if (gsc != null) { 606 resolved = resolve(gsc, type, parameterIndex); 607 if (resolved == null) { 608 // Try with interface 609 } 610 } 611 return resolved; 612 } else if (implementation instanceof ParameterizedType) { 613 ParameterizedType pt = (ParameterizedType) implementation; 614 Type[] typeArgs = pt.getActualTypeArguments(); 615 Type rawType = pt.getRawType(); 616 if (rawType == type) { 617 return typeArgs[parameterIndex]; 618 } else if (rawType instanceof Class<?>) { 619 Class<?> classRawType = (Class<?>)rawType; 620 Type resolved = resolve(classRawType, type, parameterIndex); 621 if (resolved == null) { 622 return null; 623 } else if (resolved instanceof TypeVariable) { 624 TypeVariable resolvedTV = (TypeVariable)resolved; 625 TypeVariable[] a = classRawType.getTypeParameters(); 626 for (int i = 0;i < a.length;i++) { 627 if (a[i].equals(resolvedTV)) { 628 return resolve(implementation, classRawType, i); 629 } 630 } 631 throw new AssertionError(); 632 } else { 633 throw new UnsupportedOperationException("Cannot support resolution of " + resolved); 634 } 635 } else { 636 throw new UnsupportedOperationException(); 637 } 638 } else { 639 throw new UnsupportedOperationException("todo " + implementation + " " + implementation.getClass()); 640 } 641 } 642 643 public static boolean instanceOf(Class c, List<String> types) { 644 645 for (String type: types) { 646 if (instanceOf(c, type)) { 647 return true; 648 } 649 } 650 651 return false; 652 653 } 654 655 public static boolean instanceOf(Class c, String type) { 656 657 if (c.getName().equals(type)) { 658 return true; 659 } 660 661 for (Class i : c.getInterfaces()) { 662 if (instanceOf(i, type)) { 663 return true; 664 } 665 } 666 667 if (c.getSuperclass() != null) { 668 return instanceOf(c.getSuperclass(), type); 669 } 670 671 return false; 672 } 673 674 /** . */ 675 private static final int ESCAPE = -1, SCANNING = 0, DOLLAR = 1, EVALUATING = 2; 676 677 /** 678 * Interpolate a string and replace the occurence from a context map, the syntax for a variable 679 * is <code>${}</code> and it can accept a default value used when the variable cannot be resolved 680 * with the <code>:-</code> separator: 681 * 682 * <ul> 683 * <li><code>{}</code> + "foo" => "foo"</li> 684 * <li><code>{}</code> + "${foo}" => ""</li> 685 * <li><code>{}</code> + "\\${foo}" => "${foo}"</li> 686 * <li><code>{foo:bar}</code> + "${foo}" => "bar"</li> 687 * <li><code>{}</code> + "${foo:-bar}" => "bar"</li> 688 * </ul> 689 * 690 * @param interpolated the value to interpolate 691 * @param context the context 692 * @return the interpolated string 693 * @throws NullPointerException if the interpolated argument is null 694 */ 695 public static String interpolate(String interpolated, Map<?, ?> context) throws NullPointerException { 696 StringBuilder sb = new StringBuilder(); 697 int status = 0; 698 int prev = 0; 699 int length = interpolated.length(); 700 for (int i = 0;i < length;i++) { 701 char c = interpolated.charAt(i); 702 switch (status) { 703 case ESCAPE: 704 if (c == '$') { 705 sb.append('$'); 706 } else { 707 sb.append('\\').append(c); 708 } 709 status = SCANNING; 710 break; 711 case SCANNING: 712 if (c == '$') { 713 status = DOLLAR; 714 } else if (c == '\\') { 715 status = ESCAPE; 716 } else { 717 sb.append(c); 718 } 719 break; 720 case DOLLAR: 721 if (c == '{') { 722 status = EVALUATING; 723 prev = i + 1; 724 } else { 725 sb.append('$').append(c); 726 } 727 break; 728 case EVALUATING: 729 if (c == '}') { 730 int j = prev + 1; 731 while (j < i) { 732 if (j < length && interpolated.charAt(j - 1) == ':' && interpolated.charAt(j) == '-') { 733 break; 734 } else { 735 j++; 736 } 737 } 738 Object value; 739 if (j < i) { 740 String key = interpolated.substring(prev, j - 1); 741 value = context.get(key); 742 if (value == null) { 743 value = interpolated.substring(j + 1, i); 744 } 745 } else { 746 String key = interpolated.substring(prev, i); 747 value = context.get(key); 748 } 749 if (value != null) { 750 sb.append(value); 751 } 752 status = SCANNING; 753 } 754 break; 755 } 756 } 757 switch (status) { 758 case DOLLAR: 759 sb.append('$'); 760 break; 761 case EVALUATING: 762 sb.append("${").append(interpolated, prev, interpolated.length()); 763 break; 764 } 765 return sb.toString(); 766 } 767 768 /** 769 * @return the current user directory 770 */ 771 public static File getCurrentDirectory() { 772 String userDir = System.getProperty("user.dir"); 773 return new File(userDir); 774 } 775 776 /** . */ 777 public static final Charset UTF_8; 778 779 static { 780 UTF_8 = Charset.forName("UTF-8"); 781 } 782 783 }