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 }