001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 */ 018 package org.apache.commons.compress.archivers.zip; 019 020 import java.util.ArrayList; 021 import java.util.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 import java.util.zip.ZipException; 025 026 /** 027 * ZipExtraField related methods 028 * @NotThreadSafe because the HashMap is not synch. 029 */ 030 // CheckStyle:HideUtilityClassConstructorCheck OFF (bc) 031 public class ExtraFieldUtils { 032 033 private static final int WORD = 4; 034 035 /** 036 * Static registry of known extra fields. 037 */ 038 private static final Map implementations; 039 040 static { 041 implementations = new HashMap(); 042 register(AsiExtraField.class); 043 register(JarMarker.class); 044 register(UnicodePathExtraField.class); 045 register(UnicodeCommentExtraField.class); 046 } 047 048 /** 049 * Register a ZipExtraField implementation. 050 * 051 * <p>The given class must have a no-arg constructor and implement 052 * the {@link ZipExtraField ZipExtraField interface}.</p> 053 * @param c the class to register 054 */ 055 public static void register(Class c) { 056 try { 057 ZipExtraField ze = (ZipExtraField) c.newInstance(); 058 implementations.put(ze.getHeaderId(), c); 059 } catch (ClassCastException cc) { 060 throw new RuntimeException(c + " doesn\'t implement ZipExtraField"); 061 } catch (InstantiationException ie) { 062 throw new RuntimeException(c + " is not a concrete class"); 063 } catch (IllegalAccessException ie) { 064 throw new RuntimeException(c + "\'s no-arg constructor is not public"); 065 } 066 } 067 068 /** 069 * Create an instance of the approriate ExtraField, falls back to 070 * {@link UnrecognizedExtraField UnrecognizedExtraField}. 071 * @param headerId the header identifier 072 * @return an instance of the appropiate ExtraField 073 * @exception InstantiationException if unable to instantiate the class 074 * @exception IllegalAccessException if not allowed to instatiate the class 075 */ 076 public static ZipExtraField createExtraField(ZipShort headerId) 077 throws InstantiationException, IllegalAccessException { 078 Class c = (Class) implementations.get(headerId); 079 if (c != null) { 080 return (ZipExtraField) c.newInstance(); 081 } 082 UnrecognizedExtraField u = new UnrecognizedExtraField(); 083 u.setHeaderId(headerId); 084 return u; 085 } 086 087 /** 088 * Split the array into ExtraFields and populate them with the 089 * given data as local file data. 090 * @param data an array of bytes as it appears in local file data 091 * @return an array of ExtraFields 092 * @throws ZipException on error 093 */ 094 public static ZipExtraField[] parse(byte[] data) throws ZipException { 095 return parse(data, true); 096 } 097 098 /** 099 * Split the array into ExtraFields and populate them with the 100 * given data. 101 * @param data an array of bytes 102 * @param local whether data originates from the local file data 103 * or the central directory 104 * @return an array of ExtraFields 105 * @throws ZipException on error 106 */ 107 public static ZipExtraField[] parse(byte[] data, boolean local) 108 throws ZipException { 109 List v = new ArrayList(); 110 int start = 0; 111 while (start <= data.length - WORD) { 112 ZipShort headerId = new ZipShort(data, start); 113 int length = (new ZipShort(data, start + 2)).getValue(); 114 if (start + WORD + length > data.length) { 115 throw new ZipException("data starting at " + start 116 + " is in unknown format"); 117 } 118 try { 119 ZipExtraField ze = createExtraField(headerId); 120 if (local) { 121 ze.parseFromLocalFileData(data, start + WORD, length); 122 } else { 123 ze.parseFromCentralDirectoryData(data, start + WORD, 124 length); 125 } 126 v.add(ze); 127 } catch (InstantiationException ie) { 128 throw new ZipException(ie.getMessage()); 129 } catch (IllegalAccessException iae) { 130 throw new ZipException(iae.getMessage()); 131 } 132 start += (length + WORD); 133 } 134 135 ZipExtraField[] result = new ZipExtraField[v.size()]; 136 return (ZipExtraField[]) v.toArray(result); 137 } 138 139 /** 140 * Merges the local file data fields of the given ZipExtraFields. 141 * @param data an array of ExtraFiles 142 * @return an array of bytes 143 */ 144 public static byte[] mergeLocalFileDataData(ZipExtraField[] data) { 145 int sum = WORD * data.length; 146 for (int i = 0; i < data.length; i++) { 147 sum += data[i].getLocalFileDataLength().getValue(); 148 } 149 byte[] result = new byte[sum]; 150 int start = 0; 151 for (int i = 0; i < data.length; i++) { 152 System.arraycopy(data[i].getHeaderId().getBytes(), 153 0, result, start, 2); 154 System.arraycopy(data[i].getLocalFileDataLength().getBytes(), 155 0, result, start + 2, 2); 156 byte[] local = data[i].getLocalFileDataData(); 157 System.arraycopy(local, 0, result, start + WORD, local.length); 158 start += (local.length + WORD); 159 } 160 return result; 161 } 162 163 /** 164 * Merges the central directory fields of the given ZipExtraFields. 165 * @param data an array of ExtraFields 166 * @return an array of bytes 167 */ 168 public static byte[] mergeCentralDirectoryData(ZipExtraField[] data) { 169 int sum = WORD * data.length; 170 for (int i = 0; i < data.length; i++) { 171 sum += data[i].getCentralDirectoryLength().getValue(); 172 } 173 byte[] result = new byte[sum]; 174 int start = 0; 175 for (int i = 0; i < data.length; i++) { 176 System.arraycopy(data[i].getHeaderId().getBytes(), 177 0, result, start, 2); 178 System.arraycopy(data[i].getCentralDirectoryLength().getBytes(), 179 0, result, start + 2, 2); 180 byte[] local = data[i].getCentralDirectoryData(); 181 System.arraycopy(local, 0, result, start + WORD, local.length); 182 start += (local.length + WORD); 183 } 184 return result; 185 } 186 }