1 6 21 22 package de.schlichtherle.io; 23 24 import de.schlichtherle.io.archive.spi.ArchiveDriver; 25 26 import java.io.IOException ; 27 import java.io.InputStream ; 28 import java.io.ObjectInputStream ; 29 import java.io.Serializable ; 30 import java.net.URL ; 31 import java.text.MessageFormat ; 32 import java.util.Collection ; 33 import java.util.Enumeration ; 34 import java.util.HashMap ; 35 import java.util.Iterator ; 36 import java.util.Map ; 37 import java.util.Properties ; 38 import java.util.ResourceBundle ; 39 import java.util.Set ; 40 import java.util.TreeSet ; 41 import java.util.logging.Level ; 42 import java.util.logging.Logger ; 43 import java.util.regex.Matcher ; 44 import java.util.regex.Pattern ; 45 46 83 public class DefaultArchiveDetector 84 extends AbstractArchiveDetector 85 implements Serializable { 86 87 91 private static final String CLASS_NAME 92 = "de/schlichtherle/io/DefaultArchiveDetector".replace('/', '.'); private static final ResourceBundle resources 94 = ResourceBundle.getBundle(CLASS_NAME); 95 private static final Logger logger 96 = Logger.getLogger(CLASS_NAME, CLASS_NAME); 97 98 private static final String SERVICE = 99 "META-INF/services/de.schlichtherle.io.archive.spi.ArchiveDriver.properties"; 100 101 private static final Object INVALID_DRIVER = new Object (); 102 103 109 private static final Map allDrivers; 110 111 131 public static final String ALL_SUFFIXES; 133 153 public static final String DEFAULT_SUFFIXES; 155 159 163 private final DefaultArchiveDetector delegate; 164 165 168 private final String suffixes; 169 170 177 private final Map drivers; 178 179 183 private transient ThreadLocalMatcher matcher; 185 189 203 public DefaultArchiveDetector(final String suffixes) { 204 final SuffixSet suffixSet = new SuffixSet(suffixes); 205 final Set allSuffixes = allDrivers.keySet(); 206 if (suffixSet.retainAll(allSuffixes)) { 207 final SuffixSet unknown = new SuffixSet(suffixes); 208 unknown.removeAll(allSuffixes); 209 throw new IllegalArgumentException (unknown + " (no archive driver installed for these suffixes)"); 210 } 211 212 assert configurationOK(allDrivers); 213 214 this.delegate = this; 215 this.drivers = allDrivers; 216 this.suffixes = suffixSet.toString(); 217 this.matcher = new ThreadLocalMatcher(suffixSet.toRegex()); 218 } 219 220 225 public DefaultArchiveDetector(String suffixes, ArchiveDriver driver) { 226 this(NULL, suffixes, driver); 227 } 228 229 245 public DefaultArchiveDetector( 246 DefaultArchiveDetector delegate, 247 String suffixes, 248 ArchiveDriver driver) { 249 this(delegate, new Object [] { suffixes, driver}); 250 } 251 252 274 public DefaultArchiveDetector( 275 final DefaultArchiveDetector delegate, 276 Object [] configuration) { 277 this(delegate, toMap(configuration)); 278 } 279 280 300 public DefaultArchiveDetector( 301 final DefaultArchiveDetector delegate, 302 final Map configuration) { 303 if (delegate == null) 304 throw new NullPointerException ("delegate"); 305 checkConfiguration(configuration); 306 307 this.delegate = delegate; 308 drivers = new HashMap (); 309 registerArchiveDrivers(configuration, drivers); 310 final SuffixSet suffixSet = new SuffixSet(delegate.suffixes); suffixSet.addAll(drivers.keySet()); 312 suffixes = suffixSet.toString(); 313 matcher = new ThreadLocalMatcher(suffixSet.toRegex()); 314 } 315 316 320 private static Map toMap(Object [] configuration) { 321 if (configuration == null) 322 return null; 323 324 final Map map = new HashMap ((int) (configuration.length / (2 * .75))); 325 for (int i = 0, l = configuration.length; i < l; i += 2) 326 map.put(configuration[i], configuration[i + 1]); 327 328 return map; 329 } 330 331 private static boolean configurationOK(Map configuration) { 332 try { 333 checkConfiguration(configuration); 334 return true; 335 } catch (RuntimeException failure) { 336 return false; 337 } 338 } 339 340 private static void checkConfiguration(final Map configuration) { 341 if (configuration == null) 342 throw new NullPointerException ("configuration"); 343 344 for (Iterator it = configuration.entrySet().iterator(); it.hasNext();) { 345 final Map.Entry entry = (Map.Entry ) it.next(); 346 final Object key = entry.getKey(); 347 if (!(key instanceof String )) 348 if (key != null) 349 throw new IllegalArgumentException ("configuration key is not a string!"); 350 else 351 throw new NullPointerException ("configuration key"); 352 final String suffixes = (String ) key; 353 if (suffixes.length() <= 0) 354 throw new IllegalArgumentException ("configuration key is empty!"); 355 if ("DRIVER".equals(suffixes)) 356 throw new IllegalArgumentException ("DRIVER directive not allowed in configuration key!"); 357 if ("DEFAULT".equals(suffixes)) 358 throw new IllegalArgumentException ("DEFAULT directive not allowed in configuration key!"); 359 360 final Object value = entry.getValue(); 361 if (value == null) 362 throw new NullPointerException ("configuration value"); 363 if (value instanceof ArchiveDriver) 364 continue; 365 if (value instanceof String ) { 366 if (((String ) value).length() <= 0) 367 throw new IllegalArgumentException ("configuration string value is empty!"); 368 continue; 369 } 370 if (value instanceof Class ) { 371 if (!ArchiveDriver.class.isAssignableFrom((Class ) value)) 372 throw new IllegalArgumentException ("configuration class value is not an archive driver!"); 373 continue; 374 } 375 throw new IllegalArgumentException ("configuration value is not an archive driver, class or string!"); 376 } 377 } 378 379 401 public ArchiveDriver getArchiveDriver(final String pathname) { 402 final Matcher m = matcher.reset(pathname); 403 if (!m.matches()) 404 return null; 405 return lookupArchiveDriver(m.group(1).toLowerCase()); 406 } 407 408 private ArchiveDriver lookupArchiveDriver(final String suffix) { 409 assert matcher.reset("." + suffix).matches(); 410 411 synchronized (drivers) { 412 Object driver = drivers.get(suffix); 414 if (driver instanceof ArchiveDriver) { 415 return (ArchiveDriver) driver; 416 } else if (driver == INVALID_DRIVER) { 417 return null; 418 } else if (driver == null) { 419 driver = delegate.lookupArchiveDriver(suffix); 423 drivers.put(suffix, driver != null ? driver : INVALID_DRIVER); 424 return (ArchiveDriver) driver; 425 } else { 426 try { 433 if (driver instanceof String ) 434 driver = Thread.currentThread().getContextClassLoader() 435 .loadClass((String ) driver); 436 437 assert driver instanceof Class  438 : "The constructor failed to ensure that all values in the drivers map are either ArchiveDriver, Class or String instances!"; 439 440 driver = (ArchiveDriver) ((Class ) driver).newInstance(); 441 drivers.put(suffix, driver); 442 logger.log(Level.FINE, "driverInstalled", 443 new Object [] { suffix, driver }); 444 return (ArchiveDriver) driver; 445 } catch (Exception failure) { 446 drivers.put(suffix, INVALID_DRIVER); 449 final String message = MessageFormat.format( 450 resources.getString("driverInstallationFailed"), 451 new Object [] { suffix, driver }); 452 logger.log(Level.WARNING, message, failure); 453 return null; 454 } 455 } 456 } } 458 459 475 public String getSuffixes() { 476 return suffixes; 477 } 478 479 private void readObject(final ObjectInputStream in) 480 throws IOException , ClassNotFoundException { 481 in.defaultReadObject(); 482 matcher = new ThreadLocalMatcher(new SuffixSet(suffixes).toRegex()); 483 } 484 485 static { 486 logger.config("banner"); 487 488 allDrivers = registerArchiveDrivers(); 491 492 DEFAULT_SUFFIXES = defaultSuffixes(allDrivers); 498 ALL_SUFFIXES = new SuffixSet(allDrivers.keySet()).toString(); 499 500 final Iterator it = allDrivers.entrySet().iterator(); 502 if (it.hasNext()) { 503 do { 504 final Map.Entry entry = (Map.Entry ) it.next(); 505 logger.log(Level.CONFIG, "driverRegistered", 506 new Object [] { entry.getKey(), entry.getValue() }); 507 } while (it.hasNext()); 508 509 logger.log(Level.CONFIG, "allSuffixes", ALL_SUFFIXES); 510 logger.log(Level.CONFIG, "defaultSuffixes", DEFAULT_SUFFIXES); 511 } else { 512 logger.warning("noDriversRegistered"); 513 } 514 } 515 516 522 private static Map registerArchiveDrivers() { 523 final Map driverDrivers = new HashMap (); 524 final Map clientDrivers = new HashMap (); 525 526 final Enumeration urls; 527 try { 528 urls = Thread.currentThread().getContextClassLoader() 529 .getResources(SERVICE); 530 } catch (IOException failure) { 531 logger.log(Level.WARNING, "resourceLookupFailed", SERVICE); 532 return driverDrivers; 533 } 534 535 while (urls.hasMoreElements()) { 536 final URL url = (URL ) urls.nextElement(); 537 registerArchiveDrivers( 538 url, driverDrivers, clientDrivers); 539 } 540 541 driverDrivers.putAll(clientDrivers); 544 return driverDrivers; 545 } 546 547 551 private static void registerArchiveDrivers( 552 final URL url, 553 final Map driverDrivers, 554 final Map clientDrivers) { 555 assert url != null; 556 assert driverDrivers != null; 557 assert clientDrivers != null; 558 559 logger.log(Level.CONFIG, "loadingConfiguration", url); 560 final Properties configuration = new Properties (); 562 try { 563 final InputStream in = url.openStream(); 564 try { 565 configuration.load(in); 566 registerArchiveDrivers( 567 configuration, driverDrivers, clientDrivers); 568 } finally { 569 in.close(); 570 } 571 } catch (IOException failure) { 572 logger.log(Level.WARNING, "loadingConfigurationFailed", failure); 573 } 575 } 576 577 581 private static void registerArchiveDrivers( 582 final Map configuration, 583 final Map driverDrivers, 584 final Map clientDrivers) { 585 assert configuration != null; 586 assert driverDrivers != null; 587 assert clientDrivers != null; 588 589 final String driver = (String ) configuration.remove("DRIVER"); 591 final boolean isDriver = Boolean.TRUE.equals(Boolean.valueOf(driver)); 592 593 final Map drivers; 595 if (isDriver) 596 drivers = driverDrivers; 597 else 598 drivers = clientDrivers; 599 600 registerArchiveDrivers(configuration, drivers); 601 } 602 603 613 private static void registerArchiveDrivers( 614 final Map configuration, 615 final Map drivers) { 616 assert configuration != null; 617 assert drivers != null; 618 619 for (final Iterator i = configuration.entrySet().iterator(); i.hasNext(); ) { 620 final Map.Entry entry = (Map.Entry ) i.next(); 621 final String key = (String ) entry.getKey(); 622 final Object id = entry.getValue(); 623 assert !"DRIVER".equals(key) : "DRIVER should have been removed from this map before this method was called!"; 624 if ("DEFAULT".equals(key)) { 625 drivers.put(key, id); 629 } else { 630 registerArchiveDriver(key, id, drivers); 631 } 632 } 633 } 634 635 641 private static void registerArchiveDriver( 642 final String suffixes, 643 final Object id, 644 final Map drivers) { 645 assert drivers != null; 646 647 if (id == null) 648 throw new NullPointerException ("Archive driver ID must not be null!"); 649 650 final SuffixSet suffixSet = new SuffixSet(suffixes); 651 for (final Iterator it = suffixSet.iterator(); it.hasNext();) { 652 final String suffix = (String ) it.next(); 653 drivers.put(suffix, id); 654 } 655 } 656 657 private static String defaultSuffixes(final Map drivers) { 658 assert drivers != null; 659 660 final String suffixes = (String ) drivers.remove("DEFAULT"); 661 if (suffixes == null) 662 return null; 663 664 if ("NULL".equals(suffixes)) { 665 return null; 666 } else if ("ALL".equals(suffixes)) { 667 return new SuffixSet(drivers.keySet()).toString(); 668 } else { 669 final Set suffixSet = new SuffixSet(suffixes); 670 for (final Iterator it = suffixSet.iterator(); it.hasNext();) { 671 final String suffix = (String ) it.next(); 672 if (!drivers.containsKey(suffix)) { 673 it.remove(); 674 logger.log(Level.WARNING, "unknownSuffix", suffix); 675 } 676 } 677 return suffixSet.toString(); 678 } 679 } 680 681 685 688 private static final class SuffixSet extends TreeSet { 689 692 public SuffixSet(final String suffixes) { 693 if (suffixes == null) 694 return; 695 696 final String [] split = suffixes.split("\\|"); 697 for (int i = 0, l = split.length; i < l; i++) { 698 final String suffix = split[i]; 699 if (suffix.length() > 0) 700 add(suffix); 701 } 702 } 703 704 public SuffixSet(final Collection c) { 705 super(c); 706 } 707 708 711 public boolean add(Object o) { 712 return add((String ) o); 713 } 714 715 721 public boolean add(String suffix) { 722 suffix = suffix.replaceAll("\\\\\\.", "\\."); 724 if (suffix.length() > 0 && suffix.charAt(0) == '.') 725 suffix = suffix.substring(1); 726 if (suffix.length() > 0) 727 return super.add(suffix.toLowerCase()); 728 return false; 729 } 730 731 public String toRegex() { 732 if (isEmpty()) 733 return "\\00"; 735 final StringBuffer sb = new StringBuffer (".*\\.("); 736 int c = 0; 737 for (Iterator i = iterator(); i.hasNext(); ) { 738 final String suffix = (String ) i.next(); 739 if (c > 0) 740 sb.append('|'); 741 sb.append("\\Q"); 742 sb.append(suffix); 743 sb.append("\\E"); 744 c++; 745 } 746 sb.append(')'); 747 748 return sb.toString(); 749 } 750 751 public String toString() { 752 if (isEmpty()) 753 return null; 754 755 final StringBuffer sb = new StringBuffer (); 756 int c = 0; 757 for (Iterator i = iterator(); i.hasNext(); ) { 758 final String suffix = (String ) i.next(); 759 if (c > 0) 760 sb.append('|'); 761 sb.append(suffix); 762 c++; 763 } 764 assert c > 0; 765 766 return sb.toString(); 767 } 768 } 769 770 private static final class ThreadLocalMatcher extends ThreadLocal { 771 private final String regex; 772 773 private ThreadLocalMatcher(final String regex) { 774 this.regex = regex; 775 } 776 777 protected final Object initialValue() { 778 return Pattern.compile(regex, 783 Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) 784 .matcher(""); 785 } 786 787 public final Matcher reset(CharSequence input) { 788 return ((Matcher ) get()).reset(input); 789 } 790 791 798 } 799 } 800
| Popular Tags
|