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

net.java.truevfs.comp.zip.ExtraField Maven / Gradle / Ivy

There is a newer version: 0.14.0
Show newest version
/*
 * Copyright (C) 2005-2015 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package net.java.truevfs.comp.zip;

import java.util.HashMap;
import java.util.Map;
import javax.annotation.concurrent.NotThreadSafe;
import static net.java.truevfs.comp.zip.Constants.EMPTY;

/**
 * Abstract base class for an extra field in a Local or Central Header of a
 * ZIP file.
 * It defines the common properties of all extra fields and how to
 * serialize/deserialize them to/from byte arrays.
 *
 * @author Christian Schlichtherle
 */
@NotThreadSafe
abstract class ExtraField {

    private static final Map>
            registry = new HashMap<>();

    /** The Header ID for a ZIP64 Extended Information extra field. */
    static final int ZIP64_HEADER_ID = 0x0001;

    /** The Header ID for a WinZip AES extra field. */
    static final int WINZIP_AES_ID = 0x9901;

    static { register(WinZipAesExtraField.class); }

    /**
     * Registers a concrete implementation of this abstract base class for
     * use with the static factory method {@link #create}.
     *
     * @param  c the implementation class of this abstract base class.
     * @throws IllegalArgumentException if {@code c} cannot get instantiated,
     *         is not a subclass of {@code ExtraField} or its Header ID is out
     *         of range.
     *         A more descriptive exception may be associated to it as its
     *         cause.
     * @see    #create
     */
    static void register(final Class c) {
        final ExtraField ef;
        try {
            ef = (ExtraField) c.newInstance();
        } catch (NullPointerException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new IllegalArgumentException(ex);
        }
        final int headerId = ef.getHeaderId();
        assert UShort.check(headerId);
        registry.put(headerId, c);
    }

    /**
     * A static constructor which creates a new extra field based on the
     * given header id.
     * The returned extra field still requires proper initialization, for
     * example by calling {@link #readFrom}.
     *
     * @param  headerId An unsigned short integer (two bytes) which indicates
     *         the type of the returned extra field.
     * @return A new extra field
     * @see    #register
     */
    static ExtraField create(final int headerId) {
        assert UShort.check(headerId);
        final Class c = registry.get(headerId);
        final ExtraField ef;
        try {
            ef = null != c
                    ? (ExtraField) c.newInstance()
                    : new DefaultExtraField(headerId);
        } catch (final Exception cannotHappen) {
            throw new AssertionError(cannotHappen);
        }
        assert headerId == ef.getHeaderId();
        return ef;
    }

    /**
     * Returns the Header ID (type) of this extra field.
     * The Header ID is an unsigned short integer (two bytes)
     * which must be constant during the life cycle of this object.
     */
    abstract int getHeaderId();

    /**
     * Returns the data size of this extra field.
     * The data size is an unsigned short integer (two bytes)
     * which indicates the length of the data block in bytes and does
     * not include its own size in this extra field.
     * This property may be initialized by calling {@link #readFrom}.
     *
     * @return The size of the data block in bytes or {@code 0} if unknown.
     * @see    #getDataBlock
     */
    abstract int getDataSize();

    /**
     * Returns a protective copy of the data block.
     *
     * @see #getDataSize
     */
    final byte[] getDataBlock() {
        final int size = getDataSize();
        assert UShort.check(size);
        if (0 == size) return EMPTY;
        final byte[] data = new byte[size];
        writeTo(data, 0);
        return data;
    }

    /**
     * Deserializes this extra field from
     * the data block starting at the zero based offset {@code off} with
     * {@code len} bytes length in the byte array {@code buf}.
     * Upon return, this extra field shall not access {@code data}
     * subsequently and {@link #getDataSize} must equal {@code size}.
     *
     * @param  buf The byte array to read the data block from.
     * @param  off The zero based offset in the byte array where the first byte
     *         of the data block is read from.
     * @param  len The length of the data block in bytes.
     * @throws IndexOutOfBoundsException If the byte array
     *         {@code buf} does not hold at least {@code len}
     *         bytes at the zero based offset {@code off}.
     * @throws IllegalArgumentException If the data block does not conform to
     *         this type of extra field.
     * @see    #getDataSize
     */
    abstract void readFrom(byte[] buf, int off, int len)
    throws IndexOutOfBoundsException, IllegalArgumentException;

    /**
     * Serializes this extra field to
     * the data block starting at the zero based offset {@code off} with
     * {@link #getDataSize} bytes length in the byte array {@code buf}.
     * Upon return, this extra field shall not access {@code buf}
     * subsequently.
     *
     * @param  buf The byte array to write the data block to.
     * @param  off The zero based offset in the byte array where the first byte
     *         of the data block is written to.
     * @throws IndexOutOfBoundsException If the byte array
     *         {@code buf} does not hold at least {@link #getDataSize}
     *         bytes at the zero based offset {@code off}.
     * @see    #getDataSize
     */
    abstract void writeTo(byte[] buf, int off)
    throws IndexOutOfBoundsException;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy