git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@749610 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -0,0 +1,40 @@ | |||
| /* | |||
| * 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.tools.zip; | |||
| import java.util.zip.ZipException; | |||
| /** | |||
| * {@link ZipExtraField ZipExtraField} that knows how to parse central | |||
| * directory data. | |||
| * | |||
| * @since Ant 1.8.0 | |||
| */ | |||
| public interface CentralDirectoryParsingZipExtraField extends ZipExtraField { | |||
| /** | |||
| * Populate data from this array as if it was in central directory data. | |||
| * @param data an array of bytes | |||
| * @param offset the start offset | |||
| * @param length the number of bytes in the array from offset | |||
| * | |||
| * @throws ZipException on error | |||
| */ | |||
| void parseFromCentralDirectoryData(byte[] data, int offset, int length) | |||
| throws ZipException; | |||
| } | |||
| @@ -92,13 +92,27 @@ public class ExtraFieldUtils { | |||
| /** | |||
| * Split the array into ExtraFields and populate them with the | |||
| * give data. | |||
| * given data as local file data. | |||
| * @param data an array of bytes as it appears in local file data | |||
| * @return an array of ExtraFields | |||
| * @throws ZipException on error | |||
| */ | |||
| public static ZipExtraField[] parse(byte[] data) throws ZipException { | |||
| return parse(data, true); | |||
| } | |||
| /** | |||
| * Split the array into ExtraFields and populate them with the | |||
| * given data. | |||
| * @param data an array of bytes | |||
| * @param local whether data originates from the local file data | |||
| * or the central directory | |||
| * @return an array of ExtraFields | |||
| * @since 1.1 | |||
| * @throws ZipException on error | |||
| */ | |||
| public static ZipExtraField[] parse(byte[] data) throws ZipException { | |||
| public static ZipExtraField[] parse(byte[] data, boolean local) | |||
| throws ZipException { | |||
| List v = new ArrayList(); | |||
| int start = 0; | |||
| while (start <= data.length - WORD) { | |||
| @@ -110,7 +124,14 @@ public class ExtraFieldUtils { | |||
| } | |||
| try { | |||
| ZipExtraField ze = createExtraField(headerId); | |||
| ze.parseFromLocalFileData(data, start + WORD, length); | |||
| if (local | |||
| || !(ze instanceof CentralDirectoryParsingZipExtraField)) { | |||
| ze.parseFromLocalFileData(data, start + WORD, length); | |||
| } else { | |||
| ((CentralDirectoryParsingZipExtraField) ze) | |||
| .parseFromCentralDirectoryData(data, start + WORD, | |||
| length); | |||
| } | |||
| v.add(ze); | |||
| } catch (InstantiationException ie) { | |||
| throw new ZipException(ie.getMessage()); | |||
| @@ -26,7 +26,8 @@ package org.apache.tools.zip; | |||
| * identical - unless told the opposite.</p> | |||
| * | |||
| */ | |||
| public class UnrecognizedExtraField implements ZipExtraField { | |||
| public class UnrecognizedExtraField | |||
| implements CentralDirectoryParsingZipExtraField { | |||
| /** | |||
| * The Header-ID. | |||
| @@ -135,6 +136,22 @@ public class UnrecognizedExtraField implements ZipExtraField { | |||
| setLocalFileDataData(tmp); | |||
| } | |||
| /** | |||
| * @param data the array of bytes. | |||
| * @param offset the source location in the data array. | |||
| * @param length the number of bytes to use in the data array. | |||
| * @see ZipExtraField#parseFromCentralDirectoryData(byte[], int, int) | |||
| */ | |||
| public void parseFromCentralDirectoryData(byte[] data, int offset, | |||
| int length) { | |||
| byte[] tmp = new byte[length]; | |||
| System.arraycopy(data, offset, tmp, 0, length); | |||
| setCentralDirectoryData(tmp); | |||
| if (localData == null) { | |||
| setLocalFileDataData(tmp); | |||
| } | |||
| } | |||
| private static byte[] copy(byte[] from) { | |||
| if (from != null) { | |||
| byte[] to = new byte[from.length]; | |||
| @@ -284,7 +284,8 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { | |||
| */ | |||
| public void setExtra(byte[] extra) throws RuntimeException { | |||
| try { | |||
| setExtraFields(ExtraFieldUtils.parse(extra)); | |||
| ZipExtraField[] local = ExtraFieldUtils.parse(extra, true); | |||
| mergeExtraFields(local, true); | |||
| } catch (Exception e) { | |||
| throw new RuntimeException(e.getMessage(), e); | |||
| } | |||
| @@ -302,6 +303,18 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { | |||
| super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields())); | |||
| } | |||
| /** | |||
| * Sets the central directory part of extra fields. | |||
| */ | |||
| public void setCentralDirectoryExtra(byte[] b) { | |||
| try { | |||
| ZipExtraField[] central = ExtraFieldUtils.parse(b, false); | |||
| mergeExtraFields(central, false); | |||
| } catch (Exception e) { | |||
| throw new RuntimeException(e.getMessage(), e); | |||
| } | |||
| } | |||
| /** | |||
| * Retrieves the extra data for the local file data. | |||
| * @return the extra data for local file | |||
| @@ -386,4 +399,37 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { | |||
| return (this == o); | |||
| } | |||
| /** | |||
| * If there are no extra fields, use the given fields as new extra | |||
| * data - otherwise merge the fields assuming the existing fields | |||
| * and the new fields stem from different locations inside the | |||
| * archive. | |||
| * @param f the extra fields to merge | |||
| * @param local whether the new fields originate from local data | |||
| */ | |||
| private void mergeExtraFields(ZipExtraField[] f, boolean local) | |||
| throws ZipException { | |||
| if (extraFields == null) { | |||
| setExtraFields(f); | |||
| } else { | |||
| for (int i = 0; i < f.length; i++) { | |||
| ZipExtraField existing = getExtraField(f[i].getHeaderId()); | |||
| if (existing == null) { | |||
| addExtraField(f[i]); | |||
| } else { | |||
| if (local | |||
| || !(existing | |||
| instanceof CentralDirectoryParsingZipExtraField)) { | |||
| byte[] b = f[i].getLocalFileDataData(); | |||
| existing.parseFromLocalFileData(b, 0, b.length); | |||
| } else { | |||
| byte[] b = f[i].getCentralDirectoryData(); | |||
| ((CentralDirectoryParsingZipExtraField) existing) | |||
| .parseFromCentralDirectoryData(b, 0, b.length); | |||
| } | |||
| } | |||
| } | |||
| setExtra(); | |||
| } | |||
| } | |||
| } | |||
| @@ -389,15 +389,9 @@ public class ZipFile { | |||
| nameMap.put(ze.getName(), ze); | |||
| int lenToSkip = extraLen; | |||
| while (lenToSkip > 0) { | |||
| int skipped = archive.skipBytes(lenToSkip); | |||
| if (skipped <= 0) { | |||
| throw new RuntimeException("failed to skip extra data in" | |||
| + " central directory"); | |||
| } | |||
| lenToSkip -= skipped; | |||
| } | |||
| byte[] cdExtraData = new byte[extraLen]; | |||
| archive.readFully(cdExtraData); | |||
| ze.setCentralDirectoryExtra(cdExtraData); | |||
| byte[] comment = new byte[commentLen]; | |||
| archive.readFully(comment); | |||
| @@ -32,8 +32,6 @@ public class ZipEntryTest extends TestCase { | |||
| /** | |||
| * test handling of extra fields | |||
| * | |||
| * @since 1.1 | |||
| */ | |||
| public void testExtraFields() { | |||
| AsiExtraField a = new AsiExtraField(); | |||
| @@ -85,10 +83,52 @@ public class ZipEntryTest extends TestCase { | |||
| } | |||
| } | |||
| /** | |||
| * test handling of extra fields via central directory | |||
| */ | |||
| public void testExtraFieldMerging() { | |||
| AsiExtraField a = new AsiExtraField(); | |||
| a.setDirectory(true); | |||
| a.setMode(0755); | |||
| UnrecognizedExtraField u = new UnrecognizedExtraField(); | |||
| u.setHeaderId(new ZipShort(1)); | |||
| u.setLocalFileDataData(new byte[0]); | |||
| ZipEntry ze = new ZipEntry("test/"); | |||
| ze.setExtraFields(new ZipExtraField[] {a, u}); | |||
| // merge | |||
| // Header-ID 1 + length 1 + one byte of data | |||
| ze.setCentralDirectoryExtra(new byte[] {1, 0, 1, 0, 127}); | |||
| ZipExtraField[] result = ze.getExtraFields(); | |||
| assertEquals("first pass", 2, result.length); | |||
| assertSame(a, result[0]); | |||
| assertEquals(new ZipShort(1), result[1].getHeaderId()); | |||
| assertEquals(new ZipShort(0), result[1].getLocalFileDataLength()); | |||
| assertEquals(new ZipShort(1), result[1].getCentralDirectoryLength()); | |||
| // add new | |||
| // Header-ID 2 + length 0 | |||
| ze.setCentralDirectoryExtra(new byte[] {2, 0, 0, 0}); | |||
| result = ze.getExtraFields(); | |||
| assertEquals("second pass", 3, result.length); | |||
| // merge | |||
| // Header-ID 2 + length 1 + one byte of data | |||
| ze.setExtra(new byte[] {2, 0, 1, 0, 127}); | |||
| result = ze.getExtraFields(); | |||
| assertEquals("third pass", 3, result.length); | |||
| assertSame(a, result[0]); | |||
| assertEquals(new ZipShort(2), result[2].getHeaderId()); | |||
| assertEquals(new ZipShort(1), result[2].getLocalFileDataLength()); | |||
| assertEquals(new ZipShort(0), result[2].getCentralDirectoryLength()); | |||
| } | |||
| /** | |||
| * test handling of extra fields | |||
| * | |||
| * @since 1.1 | |||
| */ | |||
| public void testAddAsFirstExtraField() { | |||
| AsiExtraField a = new AsiExtraField(); | |||