View Javadoc
1   /*
2    * Copyright (C) 2005-2015 Schlichtherle IT Services.
3    * All rights reserved. Use is subject to license terms.
4    */
5   package net.java.truevfs.comp.zip;
6   
7   import java.util.HashMap;
8   import java.util.Map;
9   import javax.annotation.concurrent.NotThreadSafe;
10  import static net.java.truevfs.comp.zip.Constants.EMPTY;
11  
12  /**
13   * Abstract base class for an extra field in a Local or Central Header of a
14   * ZIP file.
15   * It defines the common properties of all extra fields and how to
16   * serialize/deserialize them to/from byte arrays.
17   *
18   * @author Christian Schlichtherle
19   */
20  @NotThreadSafe
21  abstract class ExtraField {
22  
23      private static final Map<Integer, Class<? extends ExtraField>>
24              registry = new HashMap<>();
25  
26      /** The Header ID for a ZIP64 Extended Information extra field. */
27      static final int ZIP64_HEADER_ID = 0x0001;
28  
29      /** The Header ID for a WinZip AES extra field. */
30      static final int WINZIP_AES_ID = 0x9901;
31  
32      static { register(WinZipAesExtraField.class); }
33  
34      /**
35       * Registers a concrete implementation of this abstract base class for
36       * use with the static factory method {@link #create}.
37       *
38       * @param  c the implementation class of this abstract base class.
39       * @throws IllegalArgumentException if {@code c} cannot get instantiated,
40       *         is not a subclass of {@code ExtraField} or its Header ID is out
41       *         of range.
42       *         A more descriptive exception may be associated to it as its
43       *         cause.
44       * @see    #create
45       */
46      static void register(final Class<? extends ExtraField> c) {
47          final ExtraField ef;
48          try {
49              ef = (ExtraField) c.newInstance();
50          } catch (NullPointerException ex) {
51              throw ex;
52          } catch (Exception ex) {
53              throw new IllegalArgumentException(ex);
54          }
55          final int headerId = ef.getHeaderId();
56          assert UShort.check(headerId);
57          registry.put(headerId, c);
58      }
59  
60      /**
61       * A static constructor which creates a new extra field based on the
62       * given header id.
63       * The returned extra field still requires proper initialization, for
64       * example by calling {@link #readFrom}.
65       *
66       * @param  headerId An unsigned short integer (two bytes) which indicates
67       *         the type of the returned extra field.
68       * @return A new extra field
69       * @see    #register
70       */
71      static ExtraField create(final int headerId) {
72          assert UShort.check(headerId);
73          final Class<? extends ExtraField> c = registry.get(headerId);
74          final ExtraField ef;
75          try {
76              ef = null != c
77                      ? (ExtraField) c.newInstance()
78                      : new DefaultExtraField(headerId);
79          } catch (final Exception cannotHappen) {
80              throw new AssertionError(cannotHappen);
81          }
82          assert headerId == ef.getHeaderId();
83          return ef;
84      }
85  
86      /**
87       * Returns the Header ID (type) of this extra field.
88       * The Header ID is an unsigned short integer (two bytes)
89       * which must be constant during the life cycle of this object.
90       */
91      abstract int getHeaderId();
92  
93      /**
94       * Returns the data size of this extra field.
95       * The data size is an unsigned short integer (two bytes)
96       * which indicates the length of the data block in bytes and does
97       * <em>not</em> include its own size in this extra field.
98       * This property may be initialized by calling {@link #readFrom}.
99       *
100      * @return The size of the data block in bytes or {@code 0} if unknown.
101      * @see    #getDataBlock
102      */
103     abstract int getDataSize();
104 
105     /**
106      * Returns a protective copy of the data block.
107      *
108      * @see #getDataSize
109      */
110     final byte[] getDataBlock() {
111         final int size = getDataSize();
112         assert UShort.check(size);
113         if (0 == size) return EMPTY;
114         final byte[] data = new byte[size];
115         writeTo(data, 0);
116         return data;
117     }
118 
119     /**
120      * Deserializes this extra field from
121      * the data block starting at the zero based offset {@code off} with
122      * {@code len} bytes length in the byte array {@code buf}.
123      * Upon return, this extra field shall not access {@code data}
124      * subsequently and {@link #getDataSize} must equal {@code size}.
125      *
126      * @param  buf The byte array to read the data block from.
127      * @param  off The zero based offset in the byte array where the first byte
128      *         of the data block is read from.
129      * @param  len The length of the data block in bytes.
130      * @throws IndexOutOfBoundsException If the byte array
131      *         {@code buf} does not hold at least {@code len}
132      *         bytes at the zero based offset {@code off}.
133      * @throws IllegalArgumentException If the data block does not conform to
134      *         this type of extra field.
135      * @see    #getDataSize
136      */
137     abstract void readFrom(byte[] buf, int off, int len)
138     throws IndexOutOfBoundsException, IllegalArgumentException;
139 
140     /**
141      * Serializes this extra field to
142      * the data block starting at the zero based offset {@code off} with
143      * {@link #getDataSize} bytes length in the byte array {@code buf}.
144      * Upon return, this extra field shall not access {@code buf}
145      * subsequently.
146      *
147      * @param  buf The byte array to write the data block to.
148      * @param  off The zero based offset in the byte array where the first byte
149      *         of the data block is written to.
150      * @throws IndexOutOfBoundsException If the byte array
151      *         {@code buf} does not hold at least {@link #getDataSize}
152      *         bytes at the zero based offset {@code off}.
153      * @see    #getDataSize
154      */
155     abstract void writeTo(byte[] buf, int off)
156     throws IndexOutOfBoundsException;
157 }