All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schlichtherle.io.DefaultArchiveDetector Maven / Gradle / Ivy

Go to download

TrueZIP is a Java based Virtual File System (VFS) to enable transparent, multi-threaded read/write access to archive files (ZIP, TAR etc.) as if they were directories. Archive files may be arbitrarily nested and the nesting level is only limited by heap and file system size.

The newest version!
/*
 * Copyright (C) 2005-2010 Schlichtherle IT Services
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.schlichtherle.io;

import de.schlichtherle.io.archive.spi.*;
import de.schlichtherle.io.util.*;
import de.schlichtherle.util.regex.*;

import java.io.*;
import java.util.*;
import java.util.regex.*;

/**
 * An {@link ArchiveDetector} which matches file paths against a pattern of
 * archive file suffixes in order to detect prospective archive files and
 * look up their corresponding {@link ArchiveDriver} in its registry.
 * 

* When this class is initialized, it enumerates all instances of the relative * path META-INF/services/de.schlichtherle.io.registry.properties * on the class path (this ensures that TrueZIP is compatible with JNLP as used * by Java Web Start and can be safely added to the Extension Class Path). *

* These configuration files are processed in arbitrary order * to configure the global registry of archive file suffixes and * archive drivers. * This allows archive drivers to be "plugged in" by simply providing * their own configuration file somewhere on the class path. * One such instance is located inside the JAR for TrueZIP itself and contains * TrueZIP's default configuration (please refer to this file for full details * on the syntax). * Likewise, client applications may provide their own configuration * file somewhere on the class path in order to extend or override the settings * configured by TrueZIP and any optional plug-in drivers. *

* Each instance has a local registry. Constructors are provided which * allow an instance to: *

    *
  1. Filter the set of archive file suffixes in the global registry. * For example, {@code "tar|zip"} could * be accepted by the filter in order to recognize only the TAR and ZIP * file formats.
  2. *
  3. Add custom archive file suffixes for supported archive types to the * local registry in order to create pseudo archive types. * For example, {@code "myapp"} * could be added as an custom archive file suffix for the JAR file format.
  4. *
  5. Add custom archive file suffixes and archive drivers to the local * registry in order to support new archive types. * For example, the suffix {@code "7z"} * could be associated to a custom archive driver which supports the 7z * file format.
  6. *
  7. Put multiple instances in a chain of responsibility: * The first instance which holds a mapping for any given archive file * suffix in its registry determines the archive driver to be used.
  8. *
*

* Altogether, this enables to build arbitrary complex configurations with * very few lines of Java code or properties in the configuration file(s). *

* Where a constructor expects a suffix list as a parameter, this string must * have the form {@code "suffix[|suffix]*"}, where * {@code suffix} is a combination of case insensitive letters. * Empty or duplicated suffixes and leading dots are silently ignored * and {@code null} is interpreted as an empty list. * As an example, the parameter {@code "zip|jar"} would cause * the archive detector to recognize ZIP and JAR files in a path. * The same would be true for {@code "||.ZIP||.JAR||ZIP||JAR||"}, * but this notation is discouraged because it's not in canonical form * (see {@link #getSuffixes}. *

* {@link ArchiveDriver} classes are loaded on demand by the * {@link #getArchiveDriver} method using the current thread's context class * loader. This usually happens when a client application instantiates the * {@link File} class. *

* This implementation is (virtually) immutable and thread safe. *

* Since TrueZIP 6.4, this class is serializable in order to meet the * requirements of the {@link de.schlichtherle.io.File} class. * However, it's not recommended to serialize DefaultArchiveDetector instances: * Together with the instance, all associated archive drivers are serialized, * too, which is pretty inefficient for a single instance. * * @author Christian Schlichtherle * @version $Id$ * @see ArchiveDetector#NULL * @see ArchiveDetector#DEFAULT * @see ArchiveDetector#ALL * @since TrueZIP 6.0 */ public class DefaultArchiveDetector extends AbstractArchiveDetector implements Serializable { private static final long serialVersionUID = 848158760183179884L; /** * The canonical list of archive file suffixes in the global registry * which have been configured to be recognized by default. * * @deprecated This field is not for public use and will vanish * private access in the next major release. * Use {@code ArchiveDetector.DEFAULT.getSuffixes()} instead. */ public static final String DEFAULT_SUFFIXES = GlobalArchiveDriverRegistry.INSTANCE.defaultSuffixes; /** * The canonical list of all archive file suffixes in the global registry. * * @deprecated This field is not for public use and will vanish * private access in the next major release. * Use {@code ArchiveDetector.ALL.getSuffixes()} instead. */ public static final String ALL_SUFFIXES = GlobalArchiveDriverRegistry.INSTANCE.allSuffixes; /** * The local registry for archive file suffixes and archive drivers. * This could actually be the global registry * ({@link GlobalArchiveDriverRegistry#INSTANCE}), filtered by a custom * {@link #list}. */ private final ArchiveDriverRegistry registry; /** * The canonical suffix list recognized by this archive detector. * This list is used to filter the registered archive file suffixes in * {@link #registry}. */ private final String list; /** * The thread local matcher used to match archive file suffixes. * This field should be considered final! */ private transient ThreadLocalMatcher matcher; // never transmit this over the wire! /** * Creates a new {@code DefaultArchiveDetector} by filtering the * global registry for all canonicalized suffixes in {@code list}. * * @param list A list of suffixes which shall identify prospective * archive files. May be {@code null} or empty, but must * obeye the usual syntax. * @see DefaultArchiveDetector Syntax Definition for Suffix Lists * @throws IllegalArgumentException If any of the suffixes in the suffix * list names a suffix for which no {@link ArchiveDriver} is * configured in the global registry. */ public DefaultArchiveDetector(final String list) { registry = GlobalArchiveDriverRegistry.INSTANCE; final SuffixSet set = new SuffixSet(list); final SuffixSet all = registry.suffixes(); if (set.retainAll(all)) { final SuffixSet unknown = new SuffixSet(list); unknown.removeAll(all); throw new IllegalArgumentException("\"" + unknown + "\" (no archive driver installed for these suffixes)"); } this.list = set.toString(); matcher = new ThreadLocalMatcher(set.toRegex()); } /** * Equivalent to * {@link #DefaultArchiveDetector(DefaultArchiveDetector, String, ArchiveDriver) * DefaultArchiveDetector(ArchiveDetector.NULL, list, driver)}. */ public DefaultArchiveDetector(String list, ArchiveDriver driver) { this(NULL, list, driver); } /** * Creates a new {@code DefaultArchiveDetector} by * decorating the configuration of {@code delegate} with * mappings for all canonicalized suffixes in {@code list} to * {@code driver}. * * @param delegate The {@code DefaultArchiveDetector} which's * configuration is to be virtually inherited. * @param list A non-null, non-empty archive file suffix list, obeying * the usual syntax. * @param driver The archive driver to map for the suffix list. * This must either be an archive driver instance or * {@code null}. * A {@code null} archive driver may be used to shadow a * mapping for the same archive driver in {@code delegate}, * effectively removing it. * @see DefaultArchiveDetector Syntax Definition for Suffix Lists * @throws NullPointerException If {@code delegate} or * {@code list} is {@code null}. * @throws IllegalArgumentException If any other parameter precondition * does not hold or an illegal keyword is found in the * suffix list. */ public DefaultArchiveDetector( DefaultArchiveDetector delegate, String list, ArchiveDriver driver) { this(delegate, new Object[] { list, driver }); } /** * Creates a new {@code DefaultArchiveDetector} by * decorating the configuration of {@code delegate} with * mappings for all entries in {@code config}. * * @param delegate The {@code DefaultArchiveDetector} which's * configuration is to be virtually inherited. * @param config An array of suffix lists and archive driver IDs. * Each key in this map must be a non-null, non-empty archive file * suffix list, obeying the usual syntax. * Each value must either be an archive driver instance, an archive * driver class, a string with the fully qualified name name of * an archive driver class, or {@code null}. * A {@code null} archive driver may be used to shadow a * mapping for the same archive driver in {@code delegate}, * effectively removing it. * @throws NullPointerException If any parameter or configuration element * other than an archive driver is {@code null}. * @throws IllegalArgumentException If any other parameter precondition * does not hold or an illegal keyword is found in the * configuration. * @see DefaultArchiveDetector Syntax Definition for Suffix Lists */ public DefaultArchiveDetector( DefaultArchiveDetector delegate, Object[] config) { this(delegate, toMap(config)); } /** * Creates a new {@code DefaultArchiveDetector} by * decorating the configuration of {@code delegate} with * mappings for all entries in {@code config}. * * @param delegate The {@code DefaultArchiveDetector} which's * configuration is to be virtually inherited. * @param config A map of suffix lists and archive drivers. * Each key in this map must be a non-null, non-empty archive file * suffix list, obeying the usual syntax. * Each value must either be an archive driver instance, an archive * driver class, a string with the fully qualified name name of * an archive driver class, or {@code null}. * A {@code null} archive driver may be used to shadow a * mapping for the same archive driver in {@code delegate}, * effectively removing it. * @throws NullPointerException If any parameter or configuration element * other than an archive driver is {@code null}. * @throws IllegalArgumentException If any other parameter precondition * does not hold or an illegal keyword is found in the * configuration. * @see DefaultArchiveDetector Syntax Definition for Suffix Lists */ public DefaultArchiveDetector( final DefaultArchiveDetector delegate, final Map config) { registry = new ArchiveDriverRegistry(delegate.registry, config); final SuffixSet set = registry.decorate(new SuffixSet(delegate.list)); // may be a subset of delegate.registry.decorate(new SuffixSet())! list = set.toString(); matcher = new ThreadLocalMatcher(set.toRegex()); } private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); matcher = new ThreadLocalMatcher(new SuffixSet(list).toRegex()); } private static Map toMap(final Object[] config) { if (config == null) return null; final Map map = new LinkedHashMap((int) (config.length / .75) + 1); // order may be important! for (int i = 0, l = config.length; i < l; i++) map.put(config[i], config[++i]); return map; } /** * Looks up a registered archive driver for the given (file) path by * matching it against the set of configured archive file suffixes. * An archive driver is looked up in the registry as follows: *

    *
  1. If the registry holds a string, it's supposed to be the fully * qualified class name of an {@code ArchiveDriver} * implementation. The class will be loaded and stored in the registry. *
  2. If the registry then holds a class instance, it's instantiated * with its no-arguments constructor, cast to the * {@code ArchiveDriver} type and stored in the registry. *
  3. If the registry then holds an instance of an * {@code ArchiveDriver} implementation, it's returned. *
  4. Otherwise, {@code null} is returned. *
* * @throws RuntimeException A subclass is thrown if loading or * instantiating an archive driver class fails. */ public ArchiveDriver getArchiveDriver(final String path) { final Matcher m = matcher.reset(path); if (!m.matches()) return null; final ArchiveDriver driver = registry.getArchiveDriver( m.group(1).toLowerCase(Locale.ENGLISH)); assert driver != null : "archive driver does not exist for a recognized suffix"; return driver; } /** * Returns the set of archive file suffixes recognized by this archive * detector in canonical form. * * @return Either {@code ""} to indicate an empty set or * a string of the form {@code "suffix[|suffix]*"}, * where {@code suffix} is a combination of lower case * letters which does not start with a dot. * The string never contains empty or duplicated suffixes and the * suffixes are sorted in natural order. * @see #DefaultArchiveDetector(String) */ public String getSuffixes() { return list; // canonical form } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy