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

de.schlichtherle.io.archive.spi.AbstractArchiveDriver 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) 2006-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.archive.spi;

import de.schlichtherle.io.archive.Archive;
import java.io.CharConversionException;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.UnsupportedCharsetException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Icon;

/**
 * An abstract archive driver implementation to ease the task of developing
 * an archive driver.
 * It provides default implementations for character sets and icon handling.
 * 

* Since TrueZIP 6.4, this class is serializable in order to meet the * requirements of the {@link de.schlichtherle.io.File} class. * * @author Christian Schlichtherle * @version $Id: AbstractArchiveDriver.java,v 1.5 2010/08/30 22:02:17 christian_schlichtherle Exp $ * @since TrueZIP 6.0 */ public abstract class AbstractArchiveDriver implements ArchiveDriver, Serializable { private static final long serialVersionUID = 6546816846546846516L; private static final String CLASS_NAME = "de.schlichtherle.io.archive.spi.AbstractArchiveDriver"; private static final Logger logger = Logger.getLogger(CLASS_NAME, CLASS_NAME); private final String charset; private final Icon openIcon, closedIcon; /** * This field should be considered to be {@code final}! * * @see #ensureEncodable */ private transient ThreadLocalEncoder encoder; // never transmit this over the wire! /** * Constructs a new abstract archive driver. * * @param charset The name of a character set to use by default for all * entry names and probably other meta data when reading or writing * archive files. * @param openIcon The icon to return by {@link #getOpenIcon}. * May be {@code null}. * @param closedIcon The icon to return by {@link #getClosedIcon}. * May be {@code null}. * @throws NullPointerException If {@code charset} is * {@code null}. * @throws UnsupportedCharsetException If {@code charset} is not * supported by both the JSE 1.1 API and JSE 1.4 API. * @throws InconsistentCharsetSupportError If {@code charset} is * supported by the JSE 1.1 API, but not the JSE 1.4 API, * or vice versa. */ protected AbstractArchiveDriver( final String charset, final Icon openIcon, final Icon closedIcon) { this.charset = charset; this.encoder = new ThreadLocalEncoder(); this.openIcon = openIcon; this.closedIcon = closedIcon; // Perform fail fast tests for character set charsets using both // JSE 1.1 API and the NIO API. final UnsupportedEncodingException uee = testJSE11Support(charset); final UnsupportedCharsetException uce = testJSE14Support(charset); if (uee != null || uce != null) { if (uee == null) throw new InconsistentCharsetSupportError(charset, uce); if (uce == null) throw new InconsistentCharsetSupportError(charset, uee); throw uce; // throw away uee - it has same reason } assert invariants(); } private static UnsupportedEncodingException testJSE11Support( final String charset) { try { new String(new byte[0], charset); } catch (UnsupportedEncodingException ex) { return ex; } return null; } private static UnsupportedCharsetException testJSE14Support( final String charset) { try { final Charset impl = Charset.forName(charset); logger.log(Level.CONFIG, "charset.class", new Object[] { // NOI18N charset, impl.name(), impl.getClass().getName(), }); } catch (UnsupportedCharsetException ex) { return ex; } return null; } /** * Postfixes the instance after its default deserialization. * * @throws InvalidObjectException If the instance invariants are not met. */ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); assert encoder == null; encoder = new ThreadLocalEncoder(); try { invariants(); } catch (AssertionError ex) { throw (InvalidObjectException) new InvalidObjectException(ex.toString()).initCause(ex); } } /** * Checks the invariants of this class and throws an AssertionError if * any is violated even if assertion checking is disabled. *

* The constructors call this method like this: *

{@code assert invariants(); }
* This calls the method if and only if assertions are enabled in order * to assert that the instance invariants are properly obeyed. * If assertions are disabled, the call to this method is thrown away by * the HotSpot compiler, so there is no performance penalty. *

* When deserializing however, this method is called regardless of the * assertion status. On error, the {@link AssertionError} is wrapped * in an {@link InvalidObjectException} and thrown instead. * * @throws AssertionError If any invariant is violated even if assertions * are disabled. * @return {@code true} */ private boolean invariants() { if (charset == null) throw new AssertionError("character set not initialized"); try { ensureEncodable(""); } catch (CharConversionException ex) { throw new AssertionError(ex); } return true; } /** * Ensures that the given entry name is representable in this driver's * character set charset. * Should be called by sub classes in their implementation of the method * {@link ArchiveDriver#createArchiveEntry}. * * @param entryName A valid archive entry name - {@code null} is not * permissible. * @see #getCharset * @see Requirements for Archive Entry Names * @throws CharConversionException If the entry name contains characters * which cannot get encoded. */ protected final void ensureEncodable(String entryName) throws CharConversionException { if (!encoder.canEncode(entryName)) throw new CharConversionException(entryName + " (illegal characters in entry name)"); } /** * Returns the value of the property {@code charset} which was * provided to the constructor. */ public final String getCharset() { return charset; } /** @deprecated Use {@link #getCharset} instead. */ public final String getEncoding() { return charset; } /** * Returns the value of the property {@code openIcon} which was * provided to the constructor. * * @param archive Ignored. */ public final Icon getOpenIcon(Archive archive) { return openIcon; } /** * Returns the value of the property {@code closedIcon} which was * provided to the constructor. * * @param archive Ignored. */ public final Icon getClosedIcon(Archive archive) { return closedIcon; } private final class ThreadLocalEncoder extends ThreadLocal { protected Object initialValue() { return Charset.forName(charset).newEncoder(); } boolean canEncode(CharSequence cs) { return ((CharsetEncoder) get()).canEncode(cs); } } /** * Thrown to indicate that the character set implementation in the Java * Runtime Environment (JRE) for the Java Standard Edition (JSE) is broken * and needs fixing. *

* This error is thrown if and only if the character set provided to the * constructor of the enclosing class is either supported by the JSE 1.1 * style API ({@link String#String(byte[], String)}), but not the JSE 1.4 * style API ({@link Charset#forName(String)}), or vice versa. * This implies that this error is not thrown if the character * set is consistently supported or not supported by both APIs! *

* Most of the time, this error happens when accessing regular ZIP files. * The respective archive drivers require "IBM437" as the * character set. * Unfortunately, this character set is optional and Sun's JSE * implementations usually only install it if the JSE has been fully * installed. Its provider is then located in * $JAVA_HOME/lib/charsets.jar, where $JAVA_HOME is the * path name of the installed JRE. *

* To ensure that "IBM437" is always available regardless of * the JRE installation, TrueZIP provides its own provider for this charset. * This provider is configured in * truezip.jar/META-INF/services/java.nio.charset.spi.CharsetProvider. * So you should actually never see this happening (cruel world - sigh...). *

* Because the detected inconsistency would cause subtle bugs in archive * drivers and may affect other applications, too, it needs fixing. * Your options in order of preference: *

    *
  1. Upgrade to a more recent JRE or reinstall it. * When asked during installation, make sure to do a "full install". *
  2. Fix the JRE by copying $JAVA_HOME/lib/charsets.jar from some * other distribution. *
* This should ensure that $JAVA_HOME/lib/charsets.jar is present in the * JRE, which contains the provider for the "IBM437" character * set. * Although this should not be necessary due to TrueZIP's own provider, * this seems to fix the issue. *

* This error class has protected visibility solely for the purpose of * documenting it in the Javadoc. */ protected static final class InconsistentCharsetSupportError extends Error { private static final long serialVersionUID = 5976345821010992606L; private InconsistentCharsetSupportError(String charset, Exception cause) { super(message(charset, cause), cause); } private static String message( final String charset, final Exception cause) { assert cause instanceof UnsupportedEncodingException || cause instanceof UnsupportedCharsetException; final String[] api = cause instanceof UnsupportedEncodingException ? new String[] { "J2SE 1.4", "JSE 1.1" } : new String[] { "JSE 1.1", "J2SE 1.4" }; return "The character set '" + charset + "' is supported by the " + api[0] + " API, but not the " + api[1] + " API." + "\nThis requires fixing the Java Runtime Environment!" + "\nPlease read the Javadoc of this error class for more information."; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy