| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.commons.compress.archivers.zip; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.zip.ZipException; |
| |
| import org.apache.commons.compress.archivers.ArchiveEntry; |
| |
| /** |
| * Extension that adds better handling of extra fields and provides access to |
| * the internal and external file attributes. |
| */ |
| public class ZipArchiveEntry |
| extends java.util.zip.ZipEntry |
| implements ArchiveEntry |
| { |
| /** |
| * Helper for JDK 1.1 |
| * |
| * @since 1.2 |
| */ |
| private static Method c_setCompressedSizeMethod; |
| |
| /** |
| * Helper for JDK 1.1 |
| * |
| * @since 1.2 |
| */ |
| private static final Object c_lockReflection = new Object(); |
| |
| /** |
| * Helper for JDK 1.1 |
| * |
| * @since 1.2 |
| */ |
| private static boolean c_triedToGetMethod; |
| |
| private final ArrayList m_extraFields = new ArrayList(); |
| |
| private int m_internalAttributes; |
| private long m_externalAttributes; |
| |
| /** |
| * Helper for JDK 1.1 <-> 1.2 incompatibility. |
| * |
| * @since 1.2 |
| */ |
| private Long m_compressedSize; |
| |
| /** |
| * Creates a new zip entry with the specified name. |
| * |
| * @param name the name of entry |
| * @since 1.1 |
| */ |
| public ZipArchiveEntry( final String name ) |
| { |
| super( name ); |
| } |
| |
| /** |
| * Creates a new zip entry with fields taken from the specified zip entry. |
| * |
| * @param entry the JDK ZipEntry to adapt |
| * @exception ZipException if can not create entry |
| * @since 1.1 |
| */ |
| public ZipArchiveEntry( java.util.zip.ZipEntry entry ) |
| throws ZipException |
| { |
| /* |
| * REVISIT: call super(entry) instead of this stuff in Ant2, |
| * "copy constructor" has not been available in JDK 1.1 |
| */ |
| super( entry.getName() ); |
| |
| setComment( entry.getComment() ); |
| setMethod( entry.getMethod() ); |
| setTime( entry.getTime() ); |
| |
| final long size = entry.getSize(); |
| if( size > 0 ) |
| { |
| setSize( size ); |
| } |
| |
| final long cSize = entry.getCompressedSize(); |
| if( cSize > 0 ) |
| { |
| setComprSize( cSize ); |
| } |
| |
| final long crc = entry.getCrc(); |
| if( crc > 0 ) |
| { |
| setCrc( crc ); |
| } |
| |
| final byte[] extra = entry.getExtra(); |
| if( extra != null ) |
| { |
| setExtraFields( ExtraFieldUtils.parse( extra ) ); |
| } |
| else |
| { |
| // initializes extra data to an empty byte array |
| setExtra(); |
| } |
| } |
| |
| /** |
| * Creates a new zip entry with fields taken from the specified zip entry. |
| * |
| * @param entry the entry to adapt |
| * @exception ZipException if can not create entry |
| * @since 1.1 |
| */ |
| public ZipArchiveEntry( final ZipArchiveEntry entry ) |
| throws ZipException |
| { |
| this( (java.util.zip.ZipEntry)entry ); |
| setInternalAttributes( entry.getInternalAttributes() ); |
| setExternalAttributes( entry.getExternalAttributes() ); |
| setExtraFields( entry.getExtraFields() ); |
| } |
| |
| /** |
| * Try to get a handle to the setCompressedSize method. |
| * |
| * @since 1.2 |
| */ |
| private static void checkSCS() |
| { |
| if( !c_triedToGetMethod ) |
| { |
| synchronized( c_lockReflection ) |
| { |
| c_triedToGetMethod = true; |
| try |
| { |
| c_setCompressedSizeMethod = |
| java.util.zip.ZipEntry.class.getMethod( "setCompressedSize", |
| new Class[]{Long.TYPE} ); |
| } |
| catch( NoSuchMethodException nse ) |
| { |
| } |
| } |
| } |
| } |
| |
| /** |
| * Are we running JDK 1.2 or higher? |
| * |
| * @return Description of the Returned Value |
| * @since 1.2 |
| */ |
| private static boolean haveSetCompressedSize() |
| { |
| checkSCS(); |
| return c_setCompressedSizeMethod != null; |
| } |
| |
| /** |
| * Invoke setCompressedSize via reflection. |
| * |
| * @param entry Description of Parameter |
| * @param size Description of Parameter |
| * @since 1.2 |
| */ |
| private static void performSetCompressedSize( final ZipArchiveEntry entry, |
| final long size ) |
| { |
| final Long[] s = {new Long( size )}; |
| try |
| { |
| c_setCompressedSizeMethod.invoke( entry, s ); |
| } |
| catch( final InvocationTargetException ite ) |
| { |
| final Throwable nested = ite.getTargetException(); |
| final String message = "Exception setting the compressed size " + |
| "of " + entry + ": " + nested.getMessage(); |
| throw new RuntimeException( message ); |
| } |
| catch( final Throwable t ) |
| { |
| final String message = "Exception setting the compressed size " + |
| "of " + entry + ": " + t.getMessage(); |
| throw new RuntimeException( message ); |
| } |
| } |
| |
| /** |
| * Make this class work in JDK 1.1 like a 1.2 class. <p> |
| * |
| * This either stores the size for later usage or invokes setCompressedSize |
| * via reflection.</p> |
| * |
| * @param size The new ComprSize value |
| * @since 1.2 |
| */ |
| public void setComprSize( final long size ) |
| { |
| if( haveSetCompressedSize() ) |
| { |
| performSetCompressedSize( this, size ); |
| } |
| else |
| { |
| m_compressedSize = new Long( size ); |
| } |
| } |
| |
| /** |
| * Sets the external file attributes. |
| * |
| * @param externalAttributes The new ExternalAttributes value |
| * @since 1.1 |
| */ |
| public void setExternalAttributes( final long externalAttributes ) |
| { |
| m_externalAttributes = externalAttributes; |
| } |
| |
| /** |
| * Throws an Exception if extra data cannot be parsed into extra fields. |
| * |
| * @param extra The new Extra value |
| * @throws RuntimeException if fail to set extra data |
| * @since 1.1 |
| */ |
| public void setExtra( final byte[] extra ) |
| throws RuntimeException |
| { |
| try |
| { |
| setExtraFields( ExtraFieldUtils.parse( extra ) ); |
| } |
| catch( final Exception e ) |
| { |
| throw new RuntimeException( e.getMessage() ); |
| } |
| } |
| |
| /** |
| * Replaces all currently attached extra fields with the new array. |
| * |
| * @param fields The new ExtraFields value |
| * @since 1.1 |
| */ |
| public void setExtraFields( final ZipExtraField[] fields ) |
| { |
| m_extraFields.clear(); |
| for( int i = 0; i < fields.length; i++ ) |
| { |
| m_extraFields.add( fields[ i ] ); |
| } |
| setExtra(); |
| } |
| |
| /** |
| * Sets the internal file attributes. |
| * |
| * @param value The new InternalAttributes value |
| * @since 1.1 |
| */ |
| public void setInternalAttributes( final int value ) |
| { |
| m_internalAttributes = value; |
| } |
| |
| /** |
| * Retrieves the extra data for the central directory. |
| * |
| * @return The CentralDirectoryExtra value |
| * @since 1.1 |
| */ |
| public byte[] getCentralDirectoryExtra() |
| { |
| return ExtraFieldUtils.mergeCentralDirectoryData( getExtraFields() ); |
| } |
| |
| /** |
| * Override to make this class work in JDK 1.1 like a 1.2 class. |
| * |
| * @return The CompressedSize value |
| * @since 1.2 |
| */ |
| public long getCompressedSize() |
| { |
| if( m_compressedSize != null ) |
| { |
| // has been set explicitly and we are running in a 1.1 VM |
| return m_compressedSize.longValue(); |
| } |
| return super.getCompressedSize(); |
| } |
| |
| /** |
| * Retrieves the external file attributes. |
| * |
| * @return The ExternalAttributes value |
| * @since 1.1 |
| */ |
| public long getExternalAttributes() |
| { |
| return m_externalAttributes; |
| } |
| |
| /** |
| * Retrieves extra fields. |
| * |
| * @return The ExtraFields value |
| * @since 1.1 |
| */ |
| public ZipExtraField[] getExtraFields() |
| { |
| final ZipExtraField[] result = new ZipExtraField[ m_extraFields.size() ]; |
| return (ZipExtraField[])m_extraFields.toArray( result ); |
| } |
| |
| /** |
| * Retrieves the internal file attributes. |
| * |
| * @return The InternalAttributes value |
| * @since 1.1 |
| */ |
| public int getInternalAttributes() |
| { |
| return m_internalAttributes; |
| } |
| |
| /** |
| * Retrieves the extra data for the local file data. |
| * |
| * @return The LocalFileDataExtra value |
| * @since 1.1 |
| */ |
| public byte[] getLocalFileDataExtra() |
| { |
| byte[] extra = getExtra(); |
| return extra != null ? extra : new byte[ 0 ]; |
| } |
| |
| /** |
| * Adds an extra fields - replacing an already present extra field of the |
| * same type. |
| * |
| * @param extraField The feature to be added to the ExtraField attribute |
| * @since 1.1 |
| */ |
| public void addExtraField( final ZipExtraField extraField ) |
| { |
| final ZipShort type = extraField.getHeaderId(); |
| boolean done = false; |
| for( int i = 0; !done && i < m_extraFields.size(); i++ ) |
| { |
| final ZipExtraField other = (ZipExtraField)m_extraFields.get( i ); |
| if( other.getHeaderId().equals( type ) ) |
| { |
| m_extraFields.set( i, extraField ); |
| done = true; |
| } |
| } |
| if( !done ) |
| { |
| m_extraFields.add( extraField ); |
| } |
| setExtra(); |
| } |
| |
| /** |
| * Overwrite clone |
| * |
| * @return Description of the Returned Value |
| * @since 1.1 |
| */ |
| public Object clone() |
| { |
| ZipArchiveEntry entry = null; |
| try |
| { |
| entry = new ZipArchiveEntry( (java.util.zip.ZipEntry)super.clone() ); |
| } |
| catch( final Exception e ) |
| { |
| // impossible as extra data is in correct format |
| e.printStackTrace(); |
| return null; |
| } |
| |
| entry.setInternalAttributes( getInternalAttributes() ); |
| entry.setExternalAttributes( getExternalAttributes() ); |
| entry.setExtraFields( getExtraFields() ); |
| return entry; |
| } |
| |
| /** |
| * Remove an extra fields. |
| * |
| * @param type Description of Parameter |
| * @since 1.1 |
| */ |
| public void removeExtraField( final ZipShort type ) |
| { |
| boolean done = false; |
| for( int i = 0; !done && i < m_extraFields.size(); i++ ) |
| { |
| if( ( (ZipExtraField)m_extraFields.get( i ) ).getHeaderId().equals( type ) ) |
| { |
| m_extraFields.remove( i ); |
| done = true; |
| } |
| } |
| if( !done ) |
| { |
| throw new java.util.NoSuchElementException(); |
| } |
| setExtra(); |
| } |
| |
| /** |
| * Unfortunately {@link java.util.zip.ZipOutputStream |
| * java.util.zip.ZipOutputStream} seems to access the extra data directly, |
| * so overriding getExtra doesn't help - we need to modify super's data |
| * directly. |
| * |
| * @since 1.1 |
| */ |
| protected void setExtra() |
| { |
| super.setExtra( ExtraFieldUtils.mergeLocalFileDataData( getExtraFields() ) ); |
| } |
| } |