blob: 82e3b9aec3fceb4c0a09c3c696cfbb51a8fe6efe [file] [log] [blame]
Torsten Curdtca165392008-07-10 10:17:44 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.commons.compress.archivers.zip;
20
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
23import java.util.ArrayList;
24import java.util.zip.ZipException;
25
26import org.apache.commons.compress.archivers.ArchiveEntry;
27
28/**
29 * Extension that adds better handling of extra fields and provides access to
30 * the internal and external file attributes.
Torsten Curdtca165392008-07-10 10:17:44 +000031 */
32public class ZipArchiveEntry
33 extends java.util.zip.ZipEntry
34 implements ArchiveEntry
35{
36 /**
37 * Helper for JDK 1.1
38 *
39 * @since 1.2
40 */
41 private static Method c_setCompressedSizeMethod;
42
43 /**
44 * Helper for JDK 1.1
45 *
46 * @since 1.2
47 */
48 private static final Object c_lockReflection = new Object();
49
50 /**
51 * Helper for JDK 1.1
52 *
53 * @since 1.2
54 */
55 private static boolean c_triedToGetMethod;
56
57 private final ArrayList m_extraFields = new ArrayList();
58
59 private int m_internalAttributes;
60 private long m_externalAttributes;
61
62 /**
63 * Helper for JDK 1.1 <-> 1.2 incompatibility.
64 *
65 * @since 1.2
66 */
67 private Long m_compressedSize;
68
69 /**
70 * Creates a new zip entry with the specified name.
71 *
72 * @param name the name of entry
73 * @since 1.1
74 */
75 public ZipArchiveEntry( final String name )
76 {
77 super( name );
78 }
79
80 /**
81 * Creates a new zip entry with fields taken from the specified zip entry.
82 *
83 * @param entry the JDK ZipEntry to adapt
84 * @exception ZipException if can not create entry
85 * @since 1.1
86 */
87 public ZipArchiveEntry( java.util.zip.ZipEntry entry )
88 throws ZipException
89 {
90 /*
91 * REVISIT: call super(entry) instead of this stuff in Ant2,
92 * "copy constructor" has not been available in JDK 1.1
93 */
94 super( entry.getName() );
95
96 setComment( entry.getComment() );
97 setMethod( entry.getMethod() );
98 setTime( entry.getTime() );
99
100 final long size = entry.getSize();
101 if( size > 0 )
102 {
103 setSize( size );
104 }
105
106 final long cSize = entry.getCompressedSize();
107 if( cSize > 0 )
108 {
109 setComprSize( cSize );
110 }
111
112 final long crc = entry.getCrc();
113 if( crc > 0 )
114 {
115 setCrc( crc );
116 }
117
118 final byte[] extra = entry.getExtra();
119 if( extra != null )
120 {
121 setExtraFields( ExtraFieldUtils.parse( extra ) );
122 }
123 else
124 {
125 // initializes extra data to an empty byte array
126 setExtra();
127 }
128 }
129
130 /**
131 * Creates a new zip entry with fields taken from the specified zip entry.
132 *
133 * @param entry the entry to adapt
134 * @exception ZipException if can not create entry
135 * @since 1.1
136 */
137 public ZipArchiveEntry( final ZipArchiveEntry entry )
138 throws ZipException
139 {
140 this( (java.util.zip.ZipEntry)entry );
141 setInternalAttributes( entry.getInternalAttributes() );
142 setExternalAttributes( entry.getExternalAttributes() );
143 setExtraFields( entry.getExtraFields() );
144 }
145
146 /**
147 * Try to get a handle to the setCompressedSize method.
148 *
149 * @since 1.2
150 */
151 private static void checkSCS()
152 {
153 if( !c_triedToGetMethod )
154 {
155 synchronized( c_lockReflection )
156 {
157 c_triedToGetMethod = true;
158 try
159 {
160 c_setCompressedSizeMethod =
161 java.util.zip.ZipEntry.class.getMethod( "setCompressedSize",
162 new Class[]{Long.TYPE} );
163 }
164 catch( NoSuchMethodException nse )
165 {
166 }
167 }
168 }
169 }
170
171 /**
172 * Are we running JDK 1.2 or higher?
173 *
174 * @return Description of the Returned Value
175 * @since 1.2
176 */
177 private static boolean haveSetCompressedSize()
178 {
179 checkSCS();
180 return c_setCompressedSizeMethod != null;
181 }
182
183 /**
184 * Invoke setCompressedSize via reflection.
185 *
186 * @param entry Description of Parameter
187 * @param size Description of Parameter
188 * @since 1.2
189 */
190 private static void performSetCompressedSize( final ZipArchiveEntry entry,
191 final long size )
192 {
193 final Long[] s = {new Long( size )};
194 try
195 {
196 c_setCompressedSizeMethod.invoke( entry, s );
197 }
198 catch( final InvocationTargetException ite )
199 {
200 final Throwable nested = ite.getTargetException();
201 final String message = "Exception setting the compressed size " +
202 "of " + entry + ": " + nested.getMessage();
203 throw new RuntimeException( message );
204 }
205 catch( final Throwable t )
206 {
207 final String message = "Exception setting the compressed size " +
208 "of " + entry + ": " + t.getMessage();
209 throw new RuntimeException( message );
210 }
211 }
212
213 /**
214 * Make this class work in JDK 1.1 like a 1.2 class. <p>
215 *
216 * This either stores the size for later usage or invokes setCompressedSize
217 * via reflection.</p>
218 *
219 * @param size The new ComprSize value
220 * @since 1.2
221 */
222 public void setComprSize( final long size )
223 {
224 if( haveSetCompressedSize() )
225 {
226 performSetCompressedSize( this, size );
227 }
228 else
229 {
230 m_compressedSize = new Long( size );
231 }
232 }
233
234 /**
235 * Sets the external file attributes.
236 *
237 * @param externalAttributes The new ExternalAttributes value
238 * @since 1.1
239 */
240 public void setExternalAttributes( final long externalAttributes )
241 {
242 m_externalAttributes = externalAttributes;
243 }
244
245 /**
246 * Throws an Exception if extra data cannot be parsed into extra fields.
247 *
248 * @param extra The new Extra value
249 * @throws RuntimeException if fail to set extra data
250 * @since 1.1
251 */
252 public void setExtra( final byte[] extra )
253 throws RuntimeException
254 {
255 try
256 {
257 setExtraFields( ExtraFieldUtils.parse( extra ) );
258 }
259 catch( final Exception e )
260 {
261 throw new RuntimeException( e.getMessage() );
262 }
263 }
264
265 /**
266 * Replaces all currently attached extra fields with the new array.
267 *
268 * @param fields The new ExtraFields value
269 * @since 1.1
270 */
271 public void setExtraFields( final ZipExtraField[] fields )
272 {
273 m_extraFields.clear();
274 for( int i = 0; i < fields.length; i++ )
275 {
276 m_extraFields.add( fields[ i ] );
277 }
278 setExtra();
279 }
280
281 /**
282 * Sets the internal file attributes.
283 *
284 * @param value The new InternalAttributes value
285 * @since 1.1
286 */
287 public void setInternalAttributes( final int value )
288 {
289 m_internalAttributes = value;
290 }
291
292 /**
293 * Retrieves the extra data for the central directory.
294 *
295 * @return The CentralDirectoryExtra value
296 * @since 1.1
297 */
298 public byte[] getCentralDirectoryExtra()
299 {
300 return ExtraFieldUtils.mergeCentralDirectoryData( getExtraFields() );
301 }
302
303 /**
304 * Override to make this class work in JDK 1.1 like a 1.2 class.
305 *
306 * @return The CompressedSize value
307 * @since 1.2
308 */
309 public long getCompressedSize()
310 {
311 if( m_compressedSize != null )
312 {
313 // has been set explicitly and we are running in a 1.1 VM
314 return m_compressedSize.longValue();
315 }
316 return super.getCompressedSize();
317 }
318
319 /**
320 * Retrieves the external file attributes.
321 *
322 * @return The ExternalAttributes value
323 * @since 1.1
324 */
325 public long getExternalAttributes()
326 {
327 return m_externalAttributes;
328 }
329
330 /**
331 * Retrieves extra fields.
332 *
333 * @return The ExtraFields value
334 * @since 1.1
335 */
336 public ZipExtraField[] getExtraFields()
337 {
338 final ZipExtraField[] result = new ZipExtraField[ m_extraFields.size() ];
339 return (ZipExtraField[])m_extraFields.toArray( result );
340 }
341
342 /**
343 * Retrieves the internal file attributes.
344 *
345 * @return The InternalAttributes value
346 * @since 1.1
347 */
348 public int getInternalAttributes()
349 {
350 return m_internalAttributes;
351 }
352
353 /**
354 * Retrieves the extra data for the local file data.
355 *
356 * @return The LocalFileDataExtra value
357 * @since 1.1
358 */
359 public byte[] getLocalFileDataExtra()
360 {
361 byte[] extra = getExtra();
362 return extra != null ? extra : new byte[ 0 ];
363 }
364
365 /**
366 * Adds an extra fields - replacing an already present extra field of the
367 * same type.
368 *
369 * @param extraField The feature to be added to the ExtraField attribute
370 * @since 1.1
371 */
372 public void addExtraField( final ZipExtraField extraField )
373 {
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000374 final ZipShort type = extraField.getHeaderId();
Torsten Curdtca165392008-07-10 10:17:44 +0000375 boolean done = false;
376 for( int i = 0; !done && i < m_extraFields.size(); i++ )
377 {
378 final ZipExtraField other = (ZipExtraField)m_extraFields.get( i );
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000379 if( other.getHeaderId().equals( type ) )
Torsten Curdtca165392008-07-10 10:17:44 +0000380 {
381 m_extraFields.set( i, extraField );
382 done = true;
383 }
384 }
385 if( !done )
386 {
387 m_extraFields.add( extraField );
388 }
389 setExtra();
390 }
391
392 /**
393 * Overwrite clone
394 *
395 * @return Description of the Returned Value
396 * @since 1.1
397 */
398 public Object clone()
399 {
400 ZipArchiveEntry entry = null;
401 try
402 {
403 entry = new ZipArchiveEntry( (java.util.zip.ZipEntry)super.clone() );
404 }
405 catch( final Exception e )
406 {
407 // impossible as extra data is in correct format
408 e.printStackTrace();
409 return null;
410 }
411
412 entry.setInternalAttributes( getInternalAttributes() );
413 entry.setExternalAttributes( getExternalAttributes() );
414 entry.setExtraFields( getExtraFields() );
415 return entry;
416 }
417
418 /**
419 * Remove an extra fields.
420 *
421 * @param type Description of Parameter
422 * @since 1.1
423 */
424 public void removeExtraField( final ZipShort type )
425 {
426 boolean done = false;
427 for( int i = 0; !done && i < m_extraFields.size(); i++ )
428 {
Torsten Curdtab9ebfc2009-01-12 11:15:34 +0000429 if( ( (ZipExtraField)m_extraFields.get( i ) ).getHeaderId().equals( type ) )
Torsten Curdtca165392008-07-10 10:17:44 +0000430 {
431 m_extraFields.remove( i );
432 done = true;
433 }
434 }
435 if( !done )
436 {
437 throw new java.util.NoSuchElementException();
438 }
439 setExtra();
440 }
441
442 /**
443 * Unfortunately {@link java.util.zip.ZipOutputStream
444 * java.util.zip.ZipOutputStream} seems to access the extra data directly,
445 * so overriding getExtra doesn't help - we need to modify super's data
446 * directly.
447 *
448 * @since 1.1
449 */
450 protected void setExtra()
451 {
452 super.setExtra( ExtraFieldUtils.mergeLocalFileDataData( getExtraFields() ) );
453 }
454}