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 }