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(); | |||