diff options
author | Jesse Wilson <jessewilson@google.com> | 2009-05-21 17:39:08 -0700 |
---|---|---|
committer | Jesse Wilson <jessewilson@google.com> | 2009-06-09 16:09:47 -0700 |
commit | 57995e8186b54515d5a03bf2ab104c3dc247f1b6 (patch) | |
tree | fb4502a727023f0021f949c366304b3a082d03f7 /archive/src | |
parent | 796290e84a25f1444d43604af31cf872648c583f (diff) | |
download | libcore-57995e8186b54515d5a03bf2ab104c3dc247f1b6.zip libcore-57995e8186b54515d5a03bf2ab104c3dc247f1b6.tar.gz libcore-57995e8186b54515d5a03bf2ab104c3dc247f1b6.tar.bz2 |
Updating archive to Harmony r772995.
Squashed commit of the following:
Adding @TestTargetNew tags
Initial merge of branch 'archive_772995' into archive_dalvik.
Fixed some problems, including InflaterInputStream.available()
and JarFile.skip() bugs.
Conflicts:
libcore/archive/.classpath
libcore/archive/META-INF/MANIFEST.MF
libcore/archive/build.xml
libcore/archive/make/hyproperties.xml
libcore/archive/src/main/java/java/util/jar/Attributes.java
libcore/archive/src/main/java/java/util/jar/JarEntry.java
libcore/archive/src/main/java/java/util/jar/JarException.java
libcore/archive/src/main/java/java/util/jar/JarFile.java
libcore/archive/src/main/java/java/util/jar/JarInputStream.java
libcore/archive/src/main/java/java/util/jar/JarVerifier.java
libcore/archive/src/main/java/java/util/jar/Manifest.java
libcore/archive/src/main/java/java/util/jar/Pack200.java
libcore/archive/src/main/java/java/util/zip/Adler32.java
libcore/archive/src/main/java/java/util/zip/CRC32.java
libcore/archive/src/main/java/java/util/zip/Checksum.java
libcore/archive/src/main/java/java/util/zip/DataFormatException.java
libcore/archive/src/main/java/java/util/zip/Deflater.java
libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java
libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java
libcore/archive/src/main/java/java/util/zip/Inflater.java
libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
libcore/archive/src/main/java/java/util/zip/ZipEntry.java
libcore/archive/src/main/java/java/util/zip/ZipException.java
libcore/archive/src/main/java/java/util/zip/ZipFile.java
libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
libcore/archive/src/main/native/archive/shared/archiveglob.c
libcore/archive/src/main/native/archive/shared/jarfile.c
libcore/archive/src/main/native/archive/shared/zip.c
libcore/archive/src/main/native/archive/shared/zip.h
libcore/archive/src/main/native/archive/unix/makefile
libcore/archive/src/main/native/archive/windows/makefile
libcore/archive/src/main/native/java_util_zip_Adler32.c
libcore/archive/src/main/native/java_util_zip_CRC32.c
libcore/archive/src/main/native/java_util_zip_Deflater.c
libcore/archive/src/main/native/java_util_zip_Inflater.c
libcore/archive/src/main/native/zip/shared/hyzip.nls
libcore/archive/src/main/native/zip/unix/makefile
libcore/archive/src/main/native/zipsup.h
libcore/archive/src/main/native/zlib/unix/makefile
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesNameTest.java
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarInputStreamTest.java
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java
libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java
commit 946f165f5b592f4453fd8f2c19766921544d38dd
Author: Jesse Wilson <jessewilson@google.com>
Date: Tue May 12 15:12:55 2009 -0700
Strip @Since Android 1.0 from Archive
commit 3498f216d7e826bfc9c4cc7c0da35830ca239367
Author: Jesse Wilson <jessewilson@google.com>
Date: Tue May 12 15:09:05 2009 -0700
Dalvik archive
commit 62e9db90bc6aa6b5d1c897cccdd616d812672677
Author: Jesse Wilson <jessewilson@google.com>
Date: Tue May 12 15:07:51 2009 -0700
Archive 772995
commit d1bf618681d6badf1b50edaf204a083d3912213d
Author: Jesse Wilson <jessewilson@google.com>
Date: Tue May 12 15:06:36 2009 -0700
Archive 527399
Diffstat (limited to 'archive/src')
53 files changed, 2116 insertions, 1853 deletions
diff --git a/archive/src/main/java/java/util/jar/Attributes.java b/archive/src/main/java/java/util/jar/Attributes.java index 5a4d923..4ee94df 100644 --- a/archive/src/main/java/java/util/jar/Attributes.java +++ b/archive/src/main/java/java/util/jar/Attributes.java @@ -17,6 +17,7 @@ package java.util.jar; +import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -28,7 +29,6 @@ import org.apache.harmony.archive.util.Util; * The {@code Attributes} class is used to store values for manifest entries. * Attribute keys are generally instances of {@code Attributes.Name}. Values * associated with attribute keys are of type {@code String}. - * @since Android 1.0 */ public class Attributes implements Cloneable, Map<Object, Object> { @@ -37,8 +37,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { * {@link Attributes.Name}) of a JAR file manifest to arbitrary values. The * attribute names thus are obtained from the {@link Manifest} for * convenience. - * - * @since Android 1.0 */ protected Map<Object, Object> map; @@ -46,46 +44,35 @@ public class Attributes implements Cloneable, Map<Object, Object> { * The name part of the name/value pairs constituting an attribute as * defined by the specification of the JAR manifest. May be composed of the * following ASCII signs as defined in the EBNF below: - * + * * <pre> * name = alphanum *headerchar * headerchar = alphanum | - | _ - * alphanum = {A-Z} | {a-z} | {0-9} + * alphanum = {A-Z} | {a-z} | {0-9} * </pre> - * - * @since Android 1.0 */ public static class Name { - private final String name; + private final byte[] name; private int hashCode; /** * The class path (a main attribute). - * - * @since Android 1.0 */ public static final Name CLASS_PATH = new Name("Class-Path"); //$NON-NLS-1$ /** * The version of the manifest file (a main attribute). - * - * @since Android 1.0 */ - public static final Name MANIFEST_VERSION = new Name( - "Manifest-Version"); //$NON-NLS-1$ + public static final Name MANIFEST_VERSION = new Name("Manifest-Version"); //$NON-NLS-1$ /** * The main class's name (for stand-alone applications). - * - * @since Android 1.0 */ public static final Name MAIN_CLASS = new Name("Main-Class"); //$NON-NLS-1$ /** * Defines the signature version of the JAR file. - * - * @since Android 1.0 */ public static final Name SIGNATURE_VERSION = new Name( "Signature-Version"); //$NON-NLS-1$ @@ -98,16 +85,12 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Sealed} manifest attribute which may have the value * {@code true} for sealed archives. - * - * @since Android 1.0 */ public static final Name SEALED = new Name("Sealed"); //$NON-NLS-1$ /** * The {@code Implementation-Title} attribute whose value is a string * that defines the title of the extension implementation. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_TITLE = new Name( "Implementation-Title"); //$NON-NLS-1$ @@ -115,8 +98,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Implementation-Version} attribute defining the version of * the extension implementation. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_VERSION = new Name( "Implementation-Version"); //$NON-NLS-1$ @@ -124,8 +105,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Implementation-Vendor} attribute defining the organization * that maintains the extension implementation. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_VENDOR = new Name( "Implementation-Vendor"); //$NON-NLS-1$ @@ -133,8 +112,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Specification-Title} attribute defining the title of the * extension specification. - * - * @since Android 1.0 */ public static final Name SPECIFICATION_TITLE = new Name( "Specification-Title"); //$NON-NLS-1$ @@ -142,8 +119,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Specification-Version} attribute defining the version of * the extension specification. - * - * @since Android 1.0 */ public static final Name SPECIFICATION_VERSION = new Name( "Specification-Version"); //$NON-NLS-1$ @@ -151,8 +126,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Specification-Vendor} attribute defining the organization * that maintains the extension specification. - * - * @since Android 1.0 */ public static final Name SPECIFICATION_VENDOR = new Name( "Specification-Vendor"); //$NON-NLS-1$ @@ -160,23 +133,17 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * The {@code Extension-List} attribute defining the extensions that are * needed by the applet. - * - * @since Android 1.0 */ public static final Name EXTENSION_LIST = new Name("Extension-List"); //$NON-NLS-1$ /** * The {@code Extension-Name} attribute which defines the unique name of * the extension. - * - * @since Android 1.0 */ public static final Name EXTENSION_NAME = new Name("Extension-Name"); //$NON-NLS-1$ /** * The {@code Extension-Installation} attribute. - * - * @since Android 1.0 */ public static final Name EXTENSION_INSTALLATION = new Name( "Extension-Installation"); //$NON-NLS-1$ @@ -185,8 +152,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { * The {@code Implementation-Vendor-Id} attribute specifies the vendor * of an extension implementation if the applet requires an * implementation from a specific vendor. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_VENDOR_ID = new Name( "Implementation-Vendor-Id"); //$NON-NLS-1$ @@ -195,91 +160,112 @@ public class Attributes implements Cloneable, Map<Object, Object> { * The {@code Implementation-URL} attribute specifying a URL that can be * used to obtain the most recent version of the extension if the * required version is not already installed. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_URL = new Name( "Implementation-URL"); //$NON-NLS-1$ + static final Name NAME = new Name("Name"); + /** * A String which must satisfy the following EBNF grammar to specify an * additional attribute: - * + * * <pre> * name = alphanum *headerchar * headerchar = alphanum | - | _ * alphanum = {A-Z} | {a-z} | {0-9} * </pre> - * + * * @param s * The Attribute string. * @exception IllegalArgumentException * if the string does not satisfy the EBNF grammar. - * @since Android 1.0 */ public Name(String s) { int i = s.length(); - if (i == 0 || i > 70) { + if (i == 0 || i > Manifest.LINE_LENGTH_LIMIT - 2) { throw new IllegalArgumentException(); } + + name = new byte[i]; + for (; --i >= 0;) { char ch = s.charAt(i); if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || ch == '-' || (ch >= '0' && ch <= '9'))) { throw new IllegalArgumentException(s); } + name[i] = (byte) ch; } - name = s; + } + + /** + * A private constructor for a trusted attribute name. + */ + Name(byte[] buf) { + name = buf; + } + + byte[] getBytes() { + return name; } /** * Returns this attribute name. - * + * * @return the attribute name. - * @since Android 1.0 */ @Override public String toString() { - return name; + try { + return new String(name, "ISO-8859-1"); + } catch (UnsupportedEncodingException iee) { + throw new InternalError(iee.getLocalizedMessage()); + } } /** * returns whether the argument provided is the same as the attribute * name. - * + * * @return if the attribute names correspond. - * @param an + * @param object * An attribute name to be compared with this name. - * @since Android 1.0 */ @Override - public boolean equals(Object an) { - if (an == null) { + public boolean equals(Object object) { + if (object == null || object.getClass() != getClass() + || object.hashCode() != hashCode()) { return false; } - return an.getClass() == this.getClass() - && name.equalsIgnoreCase(((Name) an).name); + + return Util.equalsIgnoreCase(name, ((Name) object).name); } /** * Computes a hash code of the name. - * + * * @return the hash value computed from the name. - * @since Android 1.0 */ @Override public int hashCode() { if (hashCode == 0) { - hashCode = Util.toASCIILowerCase("name").hashCode(); + int hash = 0, multiplier = 1; + for (int i = name.length - 1; i >= 0; i--) { + // 'A' & 0xDF == 'a' & 0xDF, ..., 'Z' & 0xDF == 'z' & 0xDF + hash += (name[i] & 0xDF) * multiplier; + int shifted = multiplier << 5; + multiplier = shifted - multiplier; + } + hashCode = hash; } return hashCode; } + } /** * Constructs an {@code Attributes} instance. - * - * @since Android 1.0 */ public Attributes() { map = new HashMap<Object, Object>(); @@ -288,23 +274,21 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Constructs an {@code Attributes} instance obtaining keys and values from * the parameter {@code attrib}. - * + * * @param attrib * The attributes to obtain entries from. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public Attributes(Attributes attrib) { - map = (Map<Object, Object>)((HashMap) attrib.map).clone(); + map = (Map<Object, Object>) ((HashMap) attrib.map).clone(); } /** * Constructs an {@code Attributes} instance with initial capacity of size * {@code size}. - * + * * @param size * Initial size of this {@code Attributes} instance. - * @since Android 1.0 */ public Attributes(int size) { map = new HashMap<Object, Object>(size); @@ -312,8 +296,6 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Removes all key/value pairs from this {@code Attributes}. - * - * @since Android 1.0 */ public void clear() { map.clear(); @@ -321,11 +303,10 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Determines whether this {@code Attributes} contains the specified key. - * + * * @param key * The key to search for. * @return {@code true} if the key is found, {@code false} otherwise. - * @since Android 1.0 */ public boolean containsKey(Object key) { return map.containsKey(key); @@ -333,11 +314,10 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Determines whether this {@code Attributes} contains the specified value. - * + * * @param value * the value to search for. * @return {@code true} if the value is found, {@code false} otherwise. - * @since Android 1.0 */ public boolean containsValue(Object value) { return map.containsValue(value); @@ -346,9 +326,8 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns a set containing map entries for each of the key/value pair * contained in this {@code Attributes}. - * + * * @return a set of Map.Entry's - * @since Android 1.0 */ public Set<Map.Entry<Object, Object>> entrySet() { return map.entrySet(); @@ -356,12 +335,11 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns the value associated with the parameter key. - * + * * @param key * the key to search for. * @return Object associated with key, or {@code null} if key does not * exist. - * @since Android 1.0 */ public Object get(Object key) { return map.get(key); @@ -369,9 +347,8 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Determines whether this {@code Attributes} contains any keys. - * + * * @return {@code true} if one or more keys exist, {@code false} otherwise. - * @since Android 1.0 */ public boolean isEmpty() { return map.isEmpty(); @@ -380,9 +357,8 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns a {@code Set} containing all the keys found in this {@code * Attributes}. - * + * * @return a {@code Set} of all keys. - * @since Android 1.0 */ public Set<Object> keySet() { return map.keySet(); @@ -390,7 +366,7 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Stores key/value pairs in this {@code Attributes}. - * + * * @param key * the key to associate with value. * @param value @@ -399,21 +375,20 @@ public class Attributes implements Cloneable, Map<Object, Object> { * @exception ClassCastException * when key is not an {@code Attributes.Name} or value is not * a {@code String}. - *@since Android 1.0 */ - @SuppressWarnings("cast") // Require cast to force ClassCastException + @SuppressWarnings("cast") + // Require cast to force ClassCastException public Object put(Object key, Object value) { - return map.put((Name)key, (String)value); + return map.put((Name) key, (String) value); } /** - * Stores all the key/value pairs in the argument in this {@code Attributes} - * . - * + * Stores all the key/value pairs in the argument in this {@code + * Attributes}. + * * @param attrib - * the associations to store (must be of type {@code Attributes} - * ). - * @since Android 1.0 + * the associations to store (must be of type {@code + * Attributes}). */ public void putAll(Map<?, ?> attrib) { if (attrib == null || !(attrib instanceof Attributes)) { @@ -425,12 +400,11 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Deletes the key/value pair with key {@code key} from this {@code * Attributes}. - * + * * @param key * the key to remove. * @return the values associated with the removed key, {@code null} if not * present. - * @since Android 1.0 */ public Object remove(Object key) { return map.remove(key); @@ -439,20 +413,18 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns the number of key/value pairs associated with this {@code * Attributes}. - * + * * @return the size of this {@code Attributes}. - * @since Android 1.0 */ public int size() { return map.size(); } /** - * Returns a collection of all the values present in this {@code Attributes} - * . - * + * Returns a collection of all the values present in this {@code + * Attributes}. + * * @return a collection of all values present. - * @since Android 1.0 */ public Collection<Object> values() { return map.values(); @@ -473,9 +445,8 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns the hash code of this {@code Attributes}. - * + * * @return the hash code of this object. - * @since Android 1.0 */ @Override public int hashCode() { @@ -486,12 +457,11 @@ public class Attributes implements Cloneable, Map<Object, Object> { * Determines if this {@code Attributes} and the parameter {@code * Attributes} are equal. Two {@code Attributes} instances are equal if they * contain the same keys and values. - * + * * @param obj * the object with which this {@code Attributes} is compared. * @return {@code true} if the {@code Attributes} are equal, {@code false} * otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object obj) { @@ -507,12 +477,11 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns the value associated with the parameter {@code Attributes.Name} * key. - * + * * @param name * the key to obtain the value for. * @return the {@code String} associated with name, or {@code null} if name * is not a valid key. - * @since Android 1.0 */ public String getValue(Attributes.Name name) { return (String) map.get(name); @@ -520,12 +489,11 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Returns the string associated with the parameter name. - * + * * @param name * the key to obtain the value for. * @return the string associated with name, or {@code null} if name is not a * valid key. - * @since Android 1.0 */ public String getValue(String name) { return (String) map.get(new Attributes.Name(name)); @@ -534,13 +502,12 @@ public class Attributes implements Cloneable, Map<Object, Object> { /** * Stores the value {@code val} associated with the key {@code name} in this * {@code Attributes}. - * + * * @param name * the key to store. * @param val * the value to store in this {@code Attributes}. * @return the value being stored. - * @since Android 1.0 */ public String putValue(String name, String val) { return (String) map.put(new Attributes.Name(name), val); diff --git a/archive/src/main/java/java/util/jar/InitManifest.java b/archive/src/main/java/java/util/jar/InitManifest.java index 47fdf1c..bf9c397 100644 --- a/archive/src/main/java/java/util/jar/InitManifest.java +++ b/archive/src/main/java/java/util/jar/InitManifest.java @@ -17,279 +17,206 @@ package java.util.jar; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.UTFDataFormatException; -import java.security.AccessController; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; import java.util.Map; import org.apache.harmony.archive.internal.nls.Messages; -import org.apache.harmony.luni.util.PriviAction; -import org.apache.harmony.luni.util.Util; +import org.apache.harmony.luni.util.ThreadLocalCache; class InitManifest { - private final byte[] inbuf = new byte[1024]; - private int inbufCount = 0, inbufPos = 0; + private byte[] buf; - private byte[] buffer = new byte[5]; + private int pos; - private char[] charbuf = new char[0]; + Attributes.Name name; - private final ByteArrayOutputStream out = new ByteArrayOutputStream(256); + String value; - private String encoding; + CharsetDecoder decoder = ThreadLocalCache.utf8Decoder.get(); + CharBuffer cBuf = ThreadLocalCache.charBuffer.get(); - private boolean usingUTF8 = true; - - private final Map<String, Attributes.Name> attributeNames = new HashMap<String, Attributes.Name>(); + InitManifest(byte[] buf, Attributes main, Attributes.Name ver) + throws IOException { - private final byte[] mainAttributesChunk; + this.buf = buf; - InitManifest(InputStream is, Attributes main, Map<String, Attributes> entries, Map<String, byte[]> chunks, - String verString) throws IOException { - encoding = AccessController.doPrivileged(new PriviAction<String>( - "manifest.read.encoding")); //$NON-NLS-1$ - if ("".equals(encoding)) { //$NON-NLS-1$ - encoding = null; + // check a version attribute + if (!readHeader() || (ver != null && !name.equals(ver))) { + throw new IOException(Messages.getString( + "archive.2D", ver)); //$NON-NLS-1$ } - Attributes current = main; - ArrayList<String> list = new ArrayList<String>(); - - // Return the chunk of main attributes in the manifest. - mainAttributesChunk = nextChunk(is, list); - - Iterator<String> it = list.iterator(); - while (it.hasNext()) { - addAttribute(it.next(), current); + main.put(name, value); + while (readHeader()) { + main.put(name, value); } + } - // Check for version attribute - if (verString != null && main.getValue(verString) == null) { - throw new IOException(Messages.getString("archive.2D", verString)); //$NON-NLS-1$ - } + void initEntries(Map<String, Attributes> entries, + Map<String, Manifest.Chunk> chunks) throws IOException { - list.clear(); - byte[] chunk = null; - while (chunks == null ? readLines(is, list) : (chunk = nextChunk(is, - list)) != null) { - it = list.iterator(); - String line = it.next(); - if (line.length() < 7 - || !Util.toASCIILowerCase(line.substring(0, 5)).equals("name:")) { //$NON-NLS-1$ + int mark = pos; + while (readHeader()) { + if (!Attributes.Name.NAME.equals(name)) { throw new IOException(Messages.getString("archive.23")); //$NON-NLS-1$ } - // Name: length required space char - String name = line.substring(6, line.length()); - current = new Attributes(12); - if (chunks != null) { - chunks.put(name, chunk); + String entryNameValue = value; + + Attributes entry = entries.get(entryNameValue); + if (entry == null) { + entry = new Attributes(12); } - entries.put(name, current); - while (it.hasNext()) { - addAttribute(it.next(), current); + + while (readHeader()) { + entry.put(name, value); + } + + if (chunks != null) { + if (chunks.get(entryNameValue) != null) { + // TODO A bug: there might be several verification chunks for + // the same name. I believe they should be used to update + // signature in order of appearance; there are two ways to fix + // this: either use a list of chunks, or decide on used + // signature algorithm in advance and reread the chunks while + // updating the signature; for now a defensive error is thrown + throw new IOException(Messages.getString("archive.34")); //$NON-NLS-1$ + } + chunks.put(entryNameValue, new Manifest.Chunk(mark, pos)); + mark = pos; } - list.clear(); + + entries.put(entryNameValue, entry); } + } + + int getPos() { + return pos; + } + /** + * Number of subsequent line breaks. + */ + int linebreak = 0; + + /** + * Read a single line from the manifest buffer. + */ + private boolean readHeader() throws IOException { + if (linebreak > 1) { + // break a section on an empty line + linebreak = 0; + return false; + } + readName(); + linebreak = 0; + readValue(); + // if the last line break is missed, the line + // is ignored by the reference implementation + return linebreak > 0; } - byte[] getMainAttributesChunk() { - return mainAttributesChunk; + private byte[] wrap(int mark, int pos) { + byte[] buffer = new byte[pos - mark]; + System.arraycopy(buf, mark, buffer, 0, pos - mark); + return buffer; } - private void addLine(int length, List<String> lines) throws IOException { - if (encoding != null) { - lines.add(new String(buffer, 0, length, encoding)); - } else { - if (usingUTF8) { - try { - if (charbuf.length < length) { - charbuf = new char[length]; - } - lines.add(Util.convertUTF8WithBuf(buffer, charbuf, 0, - length)); - } catch (UTFDataFormatException e) { - usingUTF8 = false; + private void readName() throws IOException { + int i = 0; + int mark = pos; + + while (pos < buf.length) { + byte b = buf[pos++]; + + if (b == ':') { + byte[] nameBuffer = wrap(mark, pos - 1); + + if (buf[pos++] != ' ') { + throw new IOException(Messages.getString( + "archive.30", nameBuffer)); //$NON-NLS-1$ } + + name = new Attributes.Name(nameBuffer); + return; } - if (!usingUTF8) { - if (charbuf.length < length) { - charbuf = new char[length]; - } - // If invalid UTF8, convert bytes to chars setting the upper - // bytes to zeros - int charOffset = 0; - int offset = 0; - for (int i = length; --i >= 0;) { - charbuf[charOffset++] = (char) (buffer[offset++] & 0xff); - } - lines.add(new String(charbuf, 0, length)); + + if (!((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' + || b == '-' || (b >= '0' && b <= '9'))) { + throw new IOException(Messages.getString("archive.30", b)); //$NON-NLS-1$ } } + if (i > 0) { + throw new IOException(Messages.getString( + "archive.30", wrap(mark, buf.length))); //$NON-NLS-1$ + } } - private byte[] nextChunk(InputStream in, List<String> lines) - throws IOException { - if (inbufCount == -1) { - return null; - } + private void readValue() throws IOException { byte next; - int pos = 0; - boolean blankline = false, lastCr = false; - out.reset(); - while (true) { - if (inbufPos == inbufCount) { - if ((inbufCount = in.read(inbuf)) == -1) { - if (out.size() == 0) { - return null; - } - if (blankline) { - addLine(pos, lines); - } - return out.toByteArray(); - } - if (inbufCount == inbuf.length && in.available() == 0) { - /* archive.2E = "line too long" */ - throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$ - } - inbufPos = 0; - } - next = inbuf[inbufPos++]; - if (lastCr) { - if (next != '\n') { - inbufPos--; - next = '\r'; + boolean lastCr = false; + int mark = pos; + int last = pos; + + decoder.reset(); + cBuf.clear(); + + while (pos < buf.length) { + next = buf[pos++]; + + switch (next) { + case 0: + throw new IOException(Messages.getString("archive.2F")); //$NON-NLS-1$ + case '\n': + if (lastCr) { + lastCr = false; } else { - if (out.size() == 0) { - continue; - } - out.write('\r'); + linebreak++; } - lastCr = false; - } else if (next == '\r') { + continue; + case '\r': lastCr = true; + linebreak++; continue; - } - if (blankline) { - if (next == ' ') { - out.write(next); - blankline = false; - continue; - } - addLine(pos, lines); - if (next == '\n') { - out.write(next); - return out.toByteArray(); - } - pos = 0; - } else if (next == '\n') { - if (out.size() == 0) { + case ' ': + if (linebreak == 1) { + decode(mark, last, false); + mark = pos; + linebreak = 0; continue; } - out.write(next); - blankline = true; - continue; } - blankline = false; - out.write(next); - if (pos == buffer.length) { - byte[] newBuf = new byte[buffer.length * 2]; - System.arraycopy(buffer, 0, newBuf, 0, buffer.length); - buffer = newBuf; + + if (linebreak >= 1) { + pos--; + break; } - buffer[pos++] = next; + last = pos; } - } - private boolean readLines(InputStream in, List<String> lines) - throws IOException { - if (inbufCount == -1) { - return false; - } - byte next; - int pos = 0; - boolean blankline = false, lastCr = false; - while (true) { - if (inbufPos == inbufCount) { - if ((inbufCount = in.read(inbuf)) == -1) { - if (blankline) { - addLine(pos, lines); - } - return lines.size() != 0; - } - if (inbufCount == inbuf.length && in.available() == 0) { - /* archive.2E = "line too long" */ - throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$ - } - inbufPos = 0; - } - next = inbuf[inbufPos++]; - if (lastCr) { - if (next != '\n') { - inbufPos--; - next = '\r'; - } - lastCr = false; - } else if (next == '\r') { - lastCr = true; - continue; - } - if (blankline) { - if (next == ' ') { - blankline = false; - continue; - } - addLine(pos, lines); - if (next == '\n') { - return true; - } - pos = 0; - } else if (next == '\n') { - if (pos == 0 && lines.size() == 0) { - continue; - } - blankline = true; - continue; - } - blankline = false; - if (pos == buffer.length) { - byte[] newBuf = new byte[buffer.length * 2]; - System.arraycopy(buffer, 0, newBuf, 0, buffer.length); - buffer = newBuf; - } - buffer[pos++] = next; + decode(mark, last, true); + while (CoderResult.OVERFLOW == decoder.flush(cBuf)) { + enlargeBuffer(); } + value = new String(cBuf.array(), cBuf.arrayOffset(), cBuf.position()); } - /* Get the next attribute and add it */ - private void addAttribute(String line, Attributes current) + private void decode(int mark, int pos, boolean endOfInput) throws IOException { - String header; - int hdrIdx = line.indexOf(':'); - if (hdrIdx < 1) { - throw new IOException(Messages.getString("archive.2F", line)); //$NON-NLS-1$ - } - header = line.substring(0, hdrIdx); - Attributes.Name name = attributeNames.get(header); - if (name == null) { - try { - name = new Attributes.Name(header); - } catch (IllegalArgumentException e) { - throw new IOException(e.toString()); - } - attributeNames.put(header, name); - } - if (hdrIdx + 1 >= line.length() || line.charAt(hdrIdx + 1) != ' ') { - throw new IOException(Messages.getString("archive.2F", line)); //$NON-NLS-1$ + ByteBuffer bBuf = ByteBuffer.wrap(buf, mark, pos - mark); + while (CoderResult.OVERFLOW == decoder.decode(bBuf, cBuf, endOfInput)) { + enlargeBuffer(); } - // +2 due to required SPACE char - current.put(name, line.substring(hdrIdx + 2, line.length())); + } + + private void enlargeBuffer() { + CharBuffer newBuf = CharBuffer.allocate(cBuf.capacity() * 2); + newBuf.put(cBuf.array(), cBuf.arrayOffset(), cBuf.position()); + cBuf = newBuf; + ThreadLocalCache.charBuffer.set(cBuf); } } diff --git a/archive/src/main/java/java/util/jar/JarEntry.java b/archive/src/main/java/java/util/jar/JarEntry.java index b8dabee..869e4b4 100644 --- a/archive/src/main/java/java/util/jar/JarEntry.java +++ b/archive/src/main/java/java/util/jar/JarEntry.java @@ -33,27 +33,27 @@ import javax.security.auth.x500.X500Principal; /** * Represents a single file in a JAR archive together with the manifest * attributes and digital signatures associated with it. - * - * @since Android 1.0 + * + * @see JarFile + * @see JarInputStream */ public class JarEntry extends ZipEntry { private Attributes attributes; JarFile parentJar; - + CodeSigner signers[]; // Cached factory used to build CertPath-s in <code>getCodeSigners()</code>. private CertificateFactory factory; - private boolean isFactoryChecked = false; + private boolean isFactoryChecked = false; /** * Creates a new {@code JarEntry} named name. * * @param name * The name of the new {@code JarEntry}. - * @since Android 1.0 */ public JarEntry(String name) { super(name); @@ -64,7 +64,6 @@ public class JarEntry extends ZipEntry { * * @param entry * The ZipEntry to obtain values from. - * @since Android 1.0 */ public JarEntry(ZipEntry entry) { super(entry); @@ -78,7 +77,6 @@ public class JarEntry extends ZipEntry { * @exception IOException * If an error occurs obtaining the {@code Attributes}. * @see Attributes - * @since Android 1.0 */ public Attributes getAttributes() throws IOException { if (attributes != null || parentJar == null) { @@ -99,7 +97,6 @@ public class JarEntry extends ZipEntry { * * @return the certificate for this entry. * @see java.security.cert.Certificate - * @since Android 1.0 */ public Certificate[] getCertificates() { if (null == parentJar) { @@ -122,12 +119,11 @@ public class JarEntry extends ZipEntry { * * @param je * The {@code JarEntry} to obtain values from. - * @since Android 1.0 */ public JarEntry(JarEntry je) { super(je); parentJar = je.parentJar; - attributes = je.attributes; + attributes = je.attributes; signers = je.signers; } @@ -139,7 +135,6 @@ public class JarEntry extends ZipEntry { * * @return the code signers for the JAR entry. * @see CodeSigner - * @since Android 1.0 */ public CodeSigner[] getCodeSigners() { if (null == signers) { @@ -155,7 +150,7 @@ public class JarEntry extends ZipEntry { } private CodeSigner[] getCodeSigners(Certificate[] certs) { - if(null == certs) { + if (null == certs) { return null; } diff --git a/archive/src/main/java/java/util/jar/JarException.java b/archive/src/main/java/java/util/jar/JarException.java index f18d639..d6943c4 100644 --- a/archive/src/main/java/java/util/jar/JarException.java +++ b/archive/src/main/java/java/util/jar/JarException.java @@ -22,8 +22,6 @@ import java.util.zip.ZipException; /** * This runtime exception is thrown when a problem occurs while reading a JAR * file. - * - * @since Android 1.0 */ public class JarException extends ZipException { @@ -31,8 +29,6 @@ public class JarException extends ZipException { /** * Constructs a new {@code JarException} instance. - * - * @since Android 1.0 */ public JarException() { super(); @@ -41,10 +37,9 @@ public class JarException extends ZipException { /** * Constructs a new {@code JarException} instance with the specified * message. - * + * * @param detailMessage * the detail message for the exception. - * @since Android 1.0 */ public JarException(String detailMessage) { super(detailMessage); diff --git a/archive/src/main/java/java/util/jar/JarFile.java b/archive/src/main/java/java/util/jar/JarFile.java index 9af9056..d6e8339 100644 --- a/archive/src/main/java/java/util/jar/JarFile.java +++ b/archive/src/main/java/java/util/jar/JarFile.java @@ -29,7 +29,6 @@ import java.io.File; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; -import java.security.MessageDigest; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -39,17 +38,14 @@ import org.apache.harmony.archive.util.Util; /** * {@code JarFile} is used to read jar entries and their associated data from * jar files. - * + * * @see JarInputStream * @see JarEntry - * @since Android 1.0 */ public class JarFile extends ZipFile { /** * The MANIFEST file name. - * - * @since Android 1.0 */ public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; //$NON-NLS-1$ @@ -70,62 +66,68 @@ public class JarFile extends ZipFile { private ZipEntry zipEntry; - private JarVerifier verifier; - private JarVerifier.VerifierEntry entry; - private MessageDigest digest; - - JarFileInputStream(InputStream is, ZipEntry ze, JarVerifier ver) { + JarFileInputStream(InputStream is, ZipEntry ze, + JarVerifier.VerifierEntry e) { super(is); - if (ver != null) { - zipEntry = ze; - verifier = ver; - count = zipEntry.getSize(); - entry = verifier.initEntry(ze.getName()); - if (entry != null) { - digest = entry.digest; - } - } + zipEntry = ze; + count = zipEntry.getSize(); + entry = e; } @Override public int read() throws IOException { - int r = super.read(); - if (entry != null) { + if (count > 0) { + int r = super.read(); if (r != -1) { - digest.update((byte) r); + entry.write(r); count--; + } else { + count = 0; } - if (r == -1 || count <= 0) { - JarVerifier.VerifierEntry temp = entry; - entry = null; - verifier.verifySignatures(temp, zipEntry); + if (count == 0) { + entry.verify(); } + return r; + } else { + return -1; } - return r; } @Override public int read(byte[] buf, int off, int nbytes) throws IOException { - int r = super.read(buf, off, nbytes); - if (entry != null) { + if (count > 0) { + int r = super.read(buf, off, nbytes); if (r != -1) { int size = r; if (count < size) { size = (int) count; } - digest.update(buf, off, size); - count -= r; + entry.write(buf, off, size); + count -= size; + } else { + count = 0; } - if (r == -1 || count <= 0) { - JarVerifier.VerifierEntry temp = entry; - entry = null; - verifier.verifySignatures(temp, zipEntry); + if (count == 0) { + entry.verify(); } + return r; + } else { + return -1; + } + } + + // BEGIN android-added + @Override + public int available() throws IOException { + if (count > 0) { + return super.available(); + } else { + return 0; } - return r; } + // END android-added @Override public long skip(long nbytes) throws IOException { @@ -146,12 +148,11 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} using the contents of the specified file. - * + * * @param file * the JAR file as {@link File}. * @throws IOException * If the file cannot be read. - * @since Android 1.0 */ public JarFile(File file) throws IOException { this(file, true); @@ -159,14 +160,13 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} using the contents of the specified file. - * + * * @param file * the JAR file as {@link File}. * @param verify * if this JAR file is signed whether it must be verified. * @throws IOException * If the file cannot be read. - * @since Android 1.0 */ public JarFile(File file, boolean verify) throws IOException { super(file); @@ -178,7 +178,7 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} using the contents of file. - * + * * @param file * the JAR file as {@link File}. * @param verify @@ -188,7 +188,6 @@ public class JarFile extends ZipFile { * {@link ZipFile#OPEN_DELETE OPEN_DELETE}. * @throws IOException * If the file cannot be read. - * @since Android 1.0 */ public JarFile(File file, boolean verify, int mode) throws IOException { super(file, mode); @@ -201,12 +200,11 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} from the contents of the file specified by * filename. - * + * * @param filename * the file name referring to the JAR file. * @throws IOException * if file name cannot be opened for reading. - * @since Android 1.0 */ public JarFile(String filename) throws IOException { this(filename, true); @@ -216,14 +214,13 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} from the contents of the file specified by * {@code filename}. - * + * * @param filename * the file name referring to the JAR file. * @param verify * if this JAR filed is signed whether it must be verified. * @throws IOException * If file cannot be opened or read. - * @since Android 1.0 */ public JarFile(String filename, boolean verify) throws IOException { super(filename); @@ -236,11 +233,10 @@ public class JarFile extends ZipFile { /** * Return an enumeration containing the {@code JarEntrys} contained in this * {@code JarFile}. - * + * * @return the {@code Enumeration} containing the JAR entries. * @throws IllegalStateException * if this {@code JarFile} is closed. - * @since Android 1.0 */ @Override public Enumeration<JarEntry> entries() { @@ -260,7 +256,7 @@ public class JarFile extends ZipFile { public JarEntry nextElement() { JarEntry je = new JarEntry(ze.nextElement()); - je.parentJar = jf; + je.parentJar = jf; return je; } } @@ -270,11 +266,10 @@ public class JarFile extends ZipFile { /** * Return the {@code JarEntry} specified by its name or {@code null} if no * such entry exists. - * + * * @param name * the name of the entry in the JAR file. * @return the JAR entry defined by the name. - * @since Android 1.0 */ public JarEntry getJarEntry(String name) { return (JarEntry) getEntry(name); @@ -302,14 +297,13 @@ public class JarFile extends ZipFile { /** * Returns the {@code Manifest} object associated with this {@code JarFile} * or {@code null} if no MANIFEST entry exists. - * + * * @return the MANIFEST. * @throws IOException * if an error occurs reading the MANIFEST file. * @throws IllegalStateException * if the jar file is closed. * @see Manifest - * @since Android 1.0 */ public Manifest getManifest() throws IOException { // BEGIN android-added @@ -358,10 +352,14 @@ public class JarFile extends ZipFile { if (verifier == null) { break; } - } else if (verifier != null && entryName.length() > dirLength - && (Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 3, ".SF", 0 ,3) //$NON-NLS-1$ - || Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 4, ".DSA", 0 ,4) //$NON-NLS-1$ - || Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 4, ".RSA", 0 ,4))){ //$NON-NLS-1$ + } else if (verifier != null + && entryName.length() > dirLength + && (Util.ASCIIIgnoreCaseRegionMatches(entryName, + entryName.length() - 3, ".SF", 0, 3) //$NON-NLS-1$ + || Util.ASCIIIgnoreCaseRegionMatches(entryName, + entryName.length() - 4, ".DSA", 0, 4) //$NON-NLS-1$ + || Util.ASCIIIgnoreCaseRegionMatches(entryName, + entryName.length() - 4, ".RSA", 0, 4))) { //$NON-NLS-1$ signed = true; InputStream is = super.getInputStream(entry); // BEGIN android-modified @@ -379,13 +377,12 @@ public class JarFile extends ZipFile { /** * Return an {@code InputStream} for reading the decompressed contents of * ZIP entry. - * + * * @param ze * the ZIP entry to be read. * @return the input stream to read from. * @throws IOException * if an error occurred while creating the input stream. - * @since Android 1.0 */ @Override public InputStream getInputStream(ZipEntry ze) throws IOException { @@ -395,8 +392,7 @@ public class JarFile extends ZipFile { if (verifier != null) { verifier.setManifest(getManifest()); if (manifest != null) { - verifier.mainAttributesChunk = manifest - .getMainAttributesChunk(); + verifier.mainAttributesEnd = manifest.getMainAttributesEnd(); } if (verifier.readCertificates()) { verifier.removeMetaEntries(); @@ -412,19 +408,24 @@ public class JarFile extends ZipFile { if (in == null) { return null; } - return new JarFileInputStream(in, ze, ze.getSize() >= 0 ? verifier - : null); + if (verifier == null || ze.getSize() == -1) { + return in; + } + JarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName()); + if (entry == null) { + return in; + } + return new JarFileInputStream(in, ze, entry); } /** * Return the {@code JarEntry} specified by name or {@code null} if no such * entry exists. - * + * * @param name * the name of the entry in the JAR file. * @return the ZIP entry extracted. - * @since Android 1.0 - */ + */ @Override public ZipEntry getEntry(String name) { ZipEntry ze = super.getEntry(name); @@ -432,16 +433,16 @@ public class JarFile extends ZipFile { return ze; } JarEntry je = new JarEntry(ze); - je.parentJar = this; + je.parentJar = this; return je; } // BEGIN android-modified private ZipEntry[] getMetaEntriesImpl(byte[] buf) { int n = 0; - + List<ZipEntry> list = new ArrayList<ZipEntry>(); - + Enumeration<? extends ZipEntry> allEntries = entries(); while (allEntries.hasMoreElements()) { ZipEntry ze = allEntries.nextElement(); @@ -463,10 +464,9 @@ public class JarFile extends ZipFile { // BEGIN android-added /** * Closes this {@code JarFile}. - * + * * @throws IOException * if an error occurs. - * @since Android 1.0 */ @Override public void close() throws IOException { diff --git a/archive/src/main/java/java/util/jar/JarInputStream.java b/archive/src/main/java/java/util/jar/JarInputStream.java index ef353ab..c803183 100644 --- a/archive/src/main/java/java/util/jar/JarInputStream.java +++ b/archive/src/main/java/java/util/jar/JarInputStream.java @@ -24,14 +24,13 @@ import java.io.OutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.apache.harmony.archive.util.Util; +import org.apache.harmony.luni.util.Util; /** * The input stream from which the JAR file to be read may be fetched. It is * used like the {@code ZipInputStream}. - * + * * @see ZipInputStream - * @since Android 1.0 */ public class JarInputStream extends ZipInputStream { @@ -51,7 +50,7 @@ public class JarInputStream extends ZipInputStream { /** * Constructs a new {@code JarInputStream} from an input stream. - * + * * @param stream * the input stream containing the JAR file. * @param verify @@ -59,7 +58,6 @@ public class JarInputStream extends ZipInputStream { * @throws IOException * If an error occurs reading entries from the input stream. * @see ZipInputStream#ZipInputStream(InputStream) - * @since Android 1.0 */ public JarInputStream(InputStream stream, boolean verify) throws IOException { @@ -84,8 +82,8 @@ public class JarInputStream extends ZipInputStream { if (verify) { verifier.setManifest(manifest); if (manifest != null) { - verifier.mainAttributesChunk = manifest - .getMainAttributesChunk(); + verifier.mainAttributesEnd = manifest + .getMainAttributesEnd(); } } @@ -103,13 +101,12 @@ public class JarInputStream extends ZipInputStream { /** * Constructs a new {@code JarInputStream} from an input stream. - * + * * @param stream * the input stream containing the JAR file. * @throws IOException * If an error occurs reading entries from the input stream. * @see ZipInputStream#ZipInputStream(InputStream) - * @since Android 1.0 */ public JarInputStream(InputStream stream) throws IOException { this(stream, true); @@ -120,7 +117,6 @@ public class JarInputStream extends ZipInputStream { * JarInputStream} or {@code null} if no manifest entry exists. * * @return the MANIFEST specifying the contents of the JAR file. - * @since Android 1.0 */ public Manifest getManifest() { return manifest; @@ -133,7 +129,6 @@ public class JarInputStream extends ZipInputStream { * @return the next JAR entry. * @throws IOException * if an error occurs while reading the entry. - * @since Android 1.0 */ public JarEntry getNextJarEntry() throws IOException { return (JarEntry) getNextEntry(); @@ -142,7 +137,7 @@ public class JarInputStream extends ZipInputStream { /** * Reads up to {@code length} of decompressed data and stores it in * {@code buffer} starting at {@code offset}. - * + * * @param buffer * Buffer to store into * @param offset @@ -152,7 +147,6 @@ public class JarInputStream extends ZipInputStream { * @return Number of uncompressed bytes read * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int offset, int length) throws IOException { @@ -175,9 +169,7 @@ public class JarInputStream extends ZipInputStream { throw e; } } else { - verifier.verifySignatures( - (JarVerifier.VerifierEntry) verStream, - jarEntry); + ((JarVerifier.VerifierEntry) verStream).verify(); } } } else { @@ -194,7 +186,6 @@ public class JarInputStream extends ZipInputStream { * @return the next extracted ZIP entry. * @throws IOException * if an error occurs while reading the entry. - * @since Android 1.0 */ @Override public ZipEntry getNextEntry() throws IOException { diff --git a/archive/src/main/java/java/util/jar/JarOutputStream.java b/archive/src/main/java/java/util/jar/JarOutputStream.java index 640f4ef..e901d87 100644 --- a/archive/src/main/java/java/util/jar/JarOutputStream.java +++ b/archive/src/main/java/java/util/jar/JarOutputStream.java @@ -25,8 +25,6 @@ import java.util.zip.ZipOutputStream; /** * The {@code JarOutputStream} is used to write data in the {@code JarFile} * format to an arbitrary output stream - * - * @since Android 1.0 */ public class JarOutputStream extends ZipOutputStream { @@ -79,7 +77,6 @@ public class JarOutputStream extends ZipOutputStream { * @throws IOException * if an error occurs writing to the entry. * @see ZipEntry - * @since Android 1.0 */ @Override public void putNextEntry(ZipEntry ze) throws IOException { diff --git a/archive/src/main/java/java/util/jar/JarVerifier.java b/archive/src/main/java/java/util/jar/JarVerifier.java index b9173f2..6c1ee93 100644 --- a/archive/src/main/java/java/util/jar/JarVerifier.java +++ b/archive/src/main/java/java/util/jar/JarVerifier.java @@ -31,13 +31,12 @@ import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import java.util.Vector; -import java.util.zip.ZipEntry; import org.apache.harmony.archive.internal.nls.Messages; import org.apache.harmony.luni.util.Base64; import org.apache.harmony.security.utils.JarUtils; -import org.apache.harmony.archive.util.Util; +import org.apache.harmony.luni.util.Util; // BEGIN android-added import org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigestJDK; @@ -65,65 +64,81 @@ class JarVerifier { private HashMap<String, byte[]> metaEntries = new HashMap<String, byte[]>(5); - private final Hashtable<String, HashMap<String, Attributes>> signatures = - new Hashtable<String, HashMap<String, Attributes>>(5); + private final Hashtable<String, HashMap<String, Attributes>> signatures = new Hashtable<String, HashMap<String, Attributes>>( + 5); - private final Hashtable<String, Certificate[]> certificates = - new Hashtable<String, Certificate[]>(5); + private final Hashtable<String, Certificate[]> certificates = new Hashtable<String, Certificate[]>( + 5); - private final Hashtable<String, Certificate[]> verifiedEntries = - new Hashtable<String, Certificate[]>(); + private final Hashtable<String, Certificate[]> verifiedEntries = new Hashtable<String, Certificate[]>(); - byte[] mainAttributesChunk; + int mainAttributesEnd; - // BEGIN android-added - private static long measureCount = 0; - - private static long averageTime = 0; - // END android-added - /** - * TODO Type description + * Stores and a hash and a message digest and verifies that massage digest + * matches the hash. */ - static class VerifierEntry extends OutputStream { + class VerifierEntry extends OutputStream { - MessageDigest digest; + private String name; - byte[] hash; + private MessageDigest digest; - Certificate[] certificates; + private byte[] hash; - VerifierEntry(MessageDigest digest, byte[] hash, + private Certificate[] certificates; + + VerifierEntry(String name, MessageDigest digest, byte[] hash, Certificate[] certificates) { + this.name = name; this.digest = digest; this.hash = hash; this.certificates = certificates; } - /* - * (non-Javadoc) - * - * @see java.io.OutputStream#write(int) + /** + * Updates a digest with one byte. */ @Override public void write(int value) { digest.update((byte) value); } - /* - * (non-Javadoc) - * - * @see java.io.OutputStream#write(byte[], int, int) + /** + * Updates a digest with byte array. */ @Override public void write(byte[] buf, int off, int nbytes) { digest.update(buf, off, nbytes); } + + /** + * Verifies that the digests stored in the manifest match the decrypted + * digests from the .SF file. This indicates the validity of the + * signing, not the integrity of the file, as it's digest must be + * calculated and verified when its contents are read. + * + * @throws SecurityException + * if the digest value stored in the manifest does <i>not</i> + * agree with the decrypted digest as recovered from the + * <code>.SF</code> file. + * @see #initEntry(String) + */ + void verify() { + byte[] d = digest.digest(); + if (!MessageDigest.isEqual(d, Base64.decode(hash))) { + throw new SecurityException(Messages.getString( + "archive.32", new Object[] { //$NON-NLS-1$ + JarFile.MANIFEST_NAME, name, jarName })); + } + verifiedEntries.put(name, certificates); + } + } /** * Constructs and returns a new instance of {@code JarVerifier}. - * + * * @param name * the name of the JAR file being verified. */ @@ -136,13 +151,12 @@ class JarVerifier { * stream. This method constructs and returns a new {@link VerifierEntry} * which contains the certificates used to sign the entry and its hash value * as specified in the JAR MANIFEST format. - * + * * @param name * the name of an entry in a JAR file which is <b>not</b> in the * {@code META-INF} directory. * @return a new instance of {@link VerifierEntry} which can be used by * callers as an {@link OutputStream}. - * @since Android 1.0 */ VerifierEntry initEntry(String name) { // If no manifest is present by the time an entry is found, @@ -159,8 +173,8 @@ class JarVerifier { } Vector<Certificate> certs = new Vector<Certificate>(); - Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = - signatures.entrySet().iterator(); + Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures + .entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, HashMap<String, Attributes>> entry = it.next(); HashMap<String, Attributes> hm = entry.getValue(); @@ -197,18 +211,18 @@ class JarVerifier { } byte[] hashBytes; try { - hashBytes = hash.getBytes("ISO8859_1"); //$NON-NLS-1$ + hashBytes = hash.getBytes("ISO-8859-1"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.toString()); } try { // BEGIN android-changed - return new VerifierEntry(OpenSSLMessageDigestJDK.getInstance(algorithm), + return new VerifierEntry(name, OpenSSLMessageDigestJDK.getInstance(algorithm), hashBytes, certificatesArray); // END android-changed } catch (NoSuchAlgorithmException e) { - // Ignored + // ignored } } return null; @@ -219,7 +233,7 @@ class JarVerifier { * entry in the {@code META-INF} directory including the manifest * file itself. Files associated with the signing of a JAR would also be * added to this collection. - * + * * @param name * the name of the file located in the {@code META-INF} * directory. @@ -234,7 +248,7 @@ class JarVerifier { /** * If the associated JAR file is signed, check on the validity of all of the * known signatures. - * + * * @return {@code true} if the associated JAR is signed and an internal * check verifies the validity of the signature(s). {@code false} if * the associated JAR file has no entries at all in its {@code @@ -243,12 +257,10 @@ class JarVerifier { * <p> * Will also return {@code true} if the JAR file is <i>not</i> * signed. - * </p> * @throws SecurityException * if the JAR file is signed and it is determined that a * signature block file contains an invalid signature for the * corresponding signature file. - * @since Android 1.0 */ synchronized boolean readCertificates() { if (metaEntries == null) { @@ -281,6 +293,12 @@ class JarVerifier { return; } + byte[] manifest = metaEntries.get(JarFile.MANIFEST_NAME); + // Manifest entry is required for any verifications. + if (manifest == null) { + return; + } + byte[] sBlockBytes = metaEntries.get(certFile); try { Certificate[] signerCertChain = JarUtils.verifySignature( @@ -288,7 +306,7 @@ class JarVerifier { new ByteArrayInputStream(sBlockBytes)); /* * Recursive call in loading security provider related class which - * is in a signed JAR. + * is in a signed JAR. */ if (null == metaEntries) { return; @@ -299,74 +317,70 @@ class JarVerifier { } catch (IOException e) { return; } catch (GeneralSecurityException e) { - /* [MSG "archive.30", "{0} failed verification of {1}"] */ - throw new SecurityException( - Messages.getString("archive.30", jarName, signatureFile)); //$NON-NLS-1$ + /* [MSG "archive.31", "{0} failed verification of {1}"] */ + throw new SecurityException(Messages.getString( + "archive.31", jarName, signatureFile)); //$NON-NLS-1$ } // Verify manifest hash in .sf file Attributes attributes = new Attributes(); - HashMap<String, Attributes> hm = new HashMap<String, Attributes>(); + HashMap<String, Attributes> entries = new HashMap<String, Attributes>(); try { - new InitManifest(new ByteArrayInputStream(sfBytes), attributes, hm, - null, "Signature-Version"); //$NON-NLS-1$ + InitManifest im = new InitManifest(sfBytes, attributes, Attributes.Name.SIGNATURE_VERSION); + im.initEntries(entries, null); } catch (IOException e) { return; } boolean createdBySigntool = false; - String createdByValue = attributes.getValue("Created-By"); //$NON-NLS-1$ - if (createdByValue != null) { - createdBySigntool = createdByValue.indexOf("signtool") != -1; //$NON-NLS-1$ + String createdBy = attributes.getValue("Created-By"); //$NON-NLS-1$ + if (createdBy != null) { + createdBySigntool = createdBy.indexOf("signtool") != -1; //$NON-NLS-1$ } // Use .SF to verify the mainAttributes of the manifest // If there is no -Digest-Manifest-Main-Attributes entry in .SF // file, such as those created before java 1.5, then we ignore // such verification. - // FIXME: The meaning of createdBySigntool - if (mainAttributesChunk != null && !createdBySigntool) { + if (mainAttributesEnd > 0 && !createdBySigntool) { String digestAttribute = "-Digest-Manifest-Main-Attributes"; //$NON-NLS-1$ - if (!verify(attributes, digestAttribute, mainAttributesChunk, - false, true)) { - /* [MSG "archive.30", "{0} failed verification of {1}"] */ - throw new SecurityException( - Messages.getString("archive.30", jarName, signatureFile)); //$NON-NLS-1$ + if (!verify(attributes, digestAttribute, manifest, 0, + mainAttributesEnd, false, true)) { + /* [MSG "archive.31", "{0} failed verification of {1}"] */ + throw new SecurityException(Messages.getString( + "archive.31", jarName, signatureFile)); //$NON-NLS-1$ } } - byte[] manifest = metaEntries.get(JarFile.MANIFEST_NAME); - if (manifest == null) { - return; - } - // Use .SF to verify the whole manifest + // Use .SF to verify the whole manifest. String digestAttribute = createdBySigntool ? "-Digest" //$NON-NLS-1$ : "-Digest-Manifest"; //$NON-NLS-1$ - if (!verify(attributes, digestAttribute, manifest, false, false)) { - Iterator<Map.Entry<String, Attributes>> it = hm.entrySet() + if (!verify(attributes, digestAttribute, manifest, 0, manifest.length, + false, false)) { + Iterator<Map.Entry<String, Attributes>> it = entries.entrySet() .iterator(); while (it.hasNext()) { Map.Entry<String, Attributes> entry = it.next(); - byte[] chunk = man.getChunk(entry.getKey()); + Manifest.Chunk chunk = man.getChunk(entry.getKey()); if (chunk == null) { return; } - if (!verify(entry.getValue(), "-Digest", chunk, //$NON-NLS-1$ - createdBySigntool, false)) { - /* [MSG "archive.31", "{0} has invalid digest for {1} in {2}"] */ - throw new SecurityException( - Messages.getString("archive.31", //$NON-NLS-1$ - new Object[] { signatureFile, entry.getKey(), jarName })); + if (!verify(entry.getValue(), "-Digest", manifest, //$NON-NLS-1$ + chunk.start, chunk.end, createdBySigntool, false)) { + throw new SecurityException(Messages.getString( + "archive.32", //$NON-NLS-1$ + new Object[] { signatureFile, entry.getKey(), + jarName })); } } } metaEntries.put(signatureFile, null); - signatures.put(signatureFile, hm); + signatures.put(signatureFile, entries); } /** * Associate this verifier with the specified {@link Manifest} object. - * + * * @param mf * a {@code java.util.jar.Manifest} object. */ @@ -375,36 +389,9 @@ class JarVerifier { } /** - * Verifies that the digests stored in the manifest match the decrypted - * digests from the .SF file. This indicates the validity of the signing, - * not the integrity of the file, as it's digest must be calculated and - * verified when its contents are read. - * - * @param entry - * the {@link VerifierEntry} associated with the specified - * {@code zipEntry}. - * @param zipEntry - * an entry in the JAR file - * @throws SecurityException - * if the digest value stored in the manifest does <i>not</i> - * agree with the decrypted digest as recovered from the - * {@code .SF} file. - * @see #initEntry(String) - */ - void verifySignatures(VerifierEntry entry, ZipEntry zipEntry) { - byte[] digest = entry.digest.digest(); - if (!MessageDigest.isEqual(digest, Base64.decode(entry.hash))) { - /* [MSG "archive.31", "{0} has invalid digest for {1} in {2}"] */ - throw new SecurityException(Messages.getString("archive.31", new Object[] { //$NON-NLS-1$ - JarFile.MANIFEST_NAME, zipEntry.getName(), jarName })); - } - verifiedEntries.put(zipEntry.getName(), entry.certificates); - } - - /** - * Returns a {@code boolean} indication of whether or not the - * associated JAR file is signed. - * + * Returns a <code>boolean</code> indication of whether or not the + * associated jar file is signed. + * * @return {@code true} if the JAR is signed, {@code false} * otherwise. */ @@ -413,7 +400,7 @@ class JarVerifier { } private boolean verify(Attributes attributes, String entry, byte[] data, - boolean ignoreSecondEndline, boolean ignorable) { + int start, int end, boolean ignoreSecondEndline, boolean ignorable) { String algorithms = attributes.getValue("Digest-Algorithms"); //$NON-NLS-1$ if (algorithms == null) { algorithms = "SHA SHA1"; //$NON-NLS-1$ @@ -434,16 +421,16 @@ class JarVerifier { } catch (NoSuchAlgorithmException e) { continue; } - if (ignoreSecondEndline && data[data.length - 1] == '\n' - && data[data.length - 2] == '\n') { - md.update(data, 0, data.length - 1); + if (ignoreSecondEndline && data[end - 1] == '\n' + && data[end - 2] == '\n') { + md.update(data, start, end - 1 - start); } else { - md.update(data, 0, data.length); + md.update(data, start, end - start); } byte[] b = md.digest(); byte[] hashBytes; try { - hashBytes = hash.getBytes("ISO8859_1"); //$NON-NLS-1$ + hashBytes = hash.getBytes("ISO-8859-1"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.toString()); } @@ -456,7 +443,7 @@ class JarVerifier { * Returns all of the {@link java.security.cert.Certificate} instances that * were used to verify the signature on the JAR entry called * {@code name}. - * + * * @param name * the name of a JAR entry. * @return an array of {@link java.security.cert.Certificate}. @@ -472,7 +459,7 @@ class JarVerifier { /** * Remove all entries from the internal collection of data held about each * JAR entry in the {@code META-INF} directory. - * + * * @see #addMetaEntry(String, byte[]) */ void removeMetaEntries() { @@ -483,7 +470,7 @@ class JarVerifier { * Returns a {@code Vector} of all of the * {@link java.security.cert.Certificate}s that are associated with the * signing of the named signature file. - * + * * @param signatureFileName * the name of a signature file. * @param certificates diff --git a/archive/src/main/java/java/util/jar/Manifest.java b/archive/src/main/java/java/util/jar/Manifest.java index 3b0d89a..b28f3fb 100644 --- a/archive/src/main/java/java/util/jar/Manifest.java +++ b/archive/src/main/java/java/util/jar/Manifest.java @@ -17,47 +17,65 @@ package java.util.jar; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.security.AccessController; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import org.apache.harmony.luni.util.PriviAction; +import org.apache.harmony.archive.internal.nls.Messages; +import org.apache.harmony.luni.util.InputStreamExposer; +import org.apache.harmony.luni.util.ThreadLocalCache; /** * The {@code Manifest} class is used to obtain attribute information for a * {@code JarFile} and its entries. - * - * @since Android 1.0 */ public class Manifest implements Cloneable { - private static final int LINE_LENGTH_LIMIT = 70; + static final int LINE_LENGTH_LIMIT = 72; private static final byte[] LINE_SEPARATOR = new byte[] { '\r', '\n' }; - private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name("Name"); //$NON-NLS-1$ + private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' }; + + private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name( + "Name"); //$NON-NLS-1$ private Attributes mainAttributes = new Attributes(); - private HashMap<String, Attributes> entryAttributes = new HashMap<String, Attributes>(); + private HashMap<String, Attributes> entries = new HashMap<String, Attributes>(); + + static class Chunk { + int start; + int end; + + Chunk(int start, int end) { + this.start = start; + this.end = end; + } + } + + private HashMap<String, Chunk> chunks; - private HashMap<String, byte[]> chunks; + /** + * Manifest bytes are used for delayed entry parsing. + */ + private InitManifest im; /** - * The data chunk of main attributes in the manifest is needed in + * The end of the main attributes section in the manifest is needed in * verification. */ - private byte[] mainAttributesChunk; + private int mainEnd; /** * Creates a new {@code Manifest} instance. - * - * @since Android 1.0 */ public Manifest() { super(); @@ -66,12 +84,11 @@ public class Manifest implements Cloneable { /** * Creates a new {@code Manifest} instance using the attributes obtained * from the input stream. - * + * * @param is * {@code InputStream} to parse for attributes. * @throws IOException * if an IO error occurs while creating this {@code Manifest} - * @since Android 1.0 */ public Manifest(InputStream is) throws IOException { super(); @@ -81,20 +98,20 @@ public class Manifest implements Cloneable { /** * Creates a new {@code Manifest} instance. The new instance will have the * same attributes as those found in the parameter {@code Manifest}. - * + * * @param man * {@code Manifest} instance to obtain attributes from. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public Manifest(Manifest man) { mainAttributes = (Attributes) man.mainAttributes.clone(); - entryAttributes = (HashMap<String, Attributes>) man.entryAttributes.clone(); + entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) man + .getEntries()).clone(); } Manifest(InputStream is, boolean readChunks) throws IOException { if (readChunks) { - chunks = new HashMap<String, byte[]>(); + chunks = new HashMap<String, Chunk>(); } read(is); } @@ -102,23 +119,21 @@ public class Manifest implements Cloneable { /** * Resets the both the main attributes as well as the entry attributes * associated with this {@code Manifest}. - * - * @since Android 1.0 */ public void clear() { - entryAttributes.clear(); + im = null; + entries.clear(); mainAttributes.clear(); } /** * Returns the {@code Attributes} associated with the parameter entry * {@code name}. - * + * * @param name * the name of the entry to obtain {@code Attributes} from. * @return the Attributes for the entry or {@code null} if the entry does * not exist. - * @since Android 1.0 */ public Attributes getAttributes(String name) { return getEntries().get(name); @@ -127,20 +142,31 @@ public class Manifest implements Cloneable { /** * Returns a map containing the {@code Attributes} for each entry in the * {@code Manifest}. - * + * * @return the map of entry attributes. - * @since Android 1.0 */ public Map<String, Attributes> getEntries() { - return entryAttributes; + initEntries(); + return entries; + } + + private void initEntries() { + if (im == null) { + return; + } + // try { + // im.initEntries(entries, chunks); + // } catch (IOException ioe) { + // throw new RuntimeException(ioe); + // } + // im = null; } /** * Returns the main {@code Attributes} of the {@code JarFile}. - * + * * @return main {@code Attributes} associated with the source {@code * JarFile}. - * @since Android 1.0 */ public Attributes getMainAttributes() { return mainAttributes; @@ -149,9 +175,8 @@ public class Manifest implements Cloneable { /** * Creates a copy of this {@code Manifest}. The returned {@code Manifest} * will equal the {@code Manifest} from which it was cloned. - * + * * @return a copy of this instance. - * @since Android 1.0 */ @Override public Object clone() { @@ -161,12 +186,11 @@ public class Manifest implements Cloneable { /** * Writes out the attribute information of the receiver to the specified * {@code OutputStream}. - * + * * @param os * The {@code OutputStream} to write to. * @throws IOException * If an error occurs writing the {@code Manifest}. - * @since Android 1.0 */ public void write(OutputStream os) throws IOException { write(this, os); @@ -175,39 +199,98 @@ public class Manifest implements Cloneable { /** * Constructs a new {@code Manifest} instance obtaining attribute * information from the specified input stream. - * + * * @param is * The {@code InputStream} to read from. * @throws IOException * If an error occurs reading the {@code Manifest}. - * @since Android 1.0 */ public void read(InputStream is) throws IOException { - InitManifest initManifest = new InitManifest(is, mainAttributes, entryAttributes, - chunks, null); - mainAttributesChunk = initManifest.getMainAttributesChunk(); + byte[] buf; + // Try to read get a reference to the bytes directly + try { + buf = InputStreamExposer.expose(is); + } catch (UnsupportedOperationException uoe) { + buf = readFully(is); + } + + if (buf.length == 0) { + return; + } + + // a workaround for HARMONY-5662 + // replace EOF and NUL with another new line + // which does not trigger an error + byte b = buf[buf.length - 1]; + if (0 == b || 26 == b) { + buf[buf.length - 1] = '\n'; + } + + // Attributes.Name.MANIFEST_VERSION is not used for + // the second parameter for RI compatibility + im = new InitManifest(buf, mainAttributes, null); + mainEnd = im.getPos(); + // FIXME + im.initEntries(entries, chunks); + im = null; + } + + /* + * Helper to read the entire contents of the manifest from the + * given input stream. Usually we can do this in a single read + * but we need to account for 'infinite' streams, by ensuring we + * have a line feed within a reasonable number of characters. + */ + private byte[] readFully(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + + while (true) { + int count = is.read(buffer); + if (count == -1) { + // TODO: Do we need to copy this, or can we live with junk at the end? + return baos.toByteArray(); + } + baos.write(buffer, 0, count); + + if (!containsLine(buffer, count)) { + throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$ + } + } + } + + /* + * Check to see if the buffer contains a newline or carriage + * return character within the first 'length' bytes. Used to + * check the validity of the manifest input stream. + */ + private boolean containsLine(byte[] buffer, int length) { + for (int i = 0; i < length; i++) { + if (buffer[i] == 0x0A || buffer[i] == 0x0D) { + return true; + } + } + return false; } /** * Returns the hash code for this instance. - * + * * @return this {@code Manifest}'s hashCode. - * @since Android 1.0 */ @Override public int hashCode() { - return mainAttributes.hashCode() ^ entryAttributes.hashCode(); + return mainAttributes.hashCode() ^ getEntries().hashCode(); } /** * Determines if the receiver is equal to the parameter object. Two {@code * Manifest}s are equal if they have identical main attributes as well as * identical entry attributes. - * + * * @param o * the object to compare against. * @return {@code true} if the manifests are equal, {@code false} otherwise - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -220,10 +303,10 @@ public class Manifest implements Cloneable { if (!mainAttributes.equals(((Manifest) o).mainAttributes)) { return false; } - return entryAttributes.equals(((Manifest) o).entryAttributes); + return getEntries().equals(((Manifest) o).getEntries()); } - byte[] getChunk(String name) { + Chunk getChunk(String name) { return chunks.get(name); } @@ -231,114 +314,84 @@ public class Manifest implements Cloneable { chunks = null; } - byte[] getMainAttributesChunk() { - return mainAttributesChunk; + int getMainAttributesEnd() { + return mainEnd; } /** * Writes out the attribute information of the specified manifest to the * specified {@code OutputStream} - * + * * @param manifest * the manifest to write out. * @param out * The {@code OutputStream} to write to. * @throws IOException * If an error occurs writing the {@code Manifest}. - * @since Android 1.0 */ static void write(Manifest manifest, OutputStream out) throws IOException { - Charset charset = null; - String encoding = AccessController.doPrivileged(new PriviAction<String>( - "manifest.write.encoding")); //$NON-NLS-1$ - if (encoding != null) { - if (encoding.length() == 0) { - encoding = "UTF8"; //$NON-NLS-1$ - } - charset = Charset.forName(encoding); - } - String version = manifest.mainAttributes.getValue(Attributes.Name.MANIFEST_VERSION); + CharsetEncoder encoder = ThreadLocalCache.utf8Encoder.get(); + ByteBuffer buffer = ThreadLocalCache.byteBuffer.get(); + + String version = manifest.mainAttributes + .getValue(Attributes.Name.MANIFEST_VERSION); if (version != null) { - writeEntry(out, charset, Attributes.Name.MANIFEST_VERSION, version); + writeEntry(out, Attributes.Name.MANIFEST_VERSION, version, encoder, + buffer); Iterator<?> entries = manifest.mainAttributes.keySet().iterator(); while (entries.hasNext()) { Attributes.Name name = (Attributes.Name) entries.next(); if (!name.equals(Attributes.Name.MANIFEST_VERSION)) { - writeEntry(out, charset, name, manifest.mainAttributes.getValue(name)); + writeEntry(out, name, manifest.mainAttributes + .getValue(name), encoder, buffer); } } } out.write(LINE_SEPARATOR); - Iterator<String> i = manifest.entryAttributes.keySet().iterator(); + Iterator<String> i = manifest.getEntries().keySet().iterator(); while (i.hasNext()) { String key = i.next(); - writeEntry(out, charset, NAME_ATTRIBUTE, key); - Attributes attrib = manifest.entryAttributes.get(key); + writeEntry(out, NAME_ATTRIBUTE, key, encoder, buffer); + Attributes attrib = manifest.entries.get(key); Iterator<?> entries = attrib.keySet().iterator(); while (entries.hasNext()) { Attributes.Name name = (Attributes.Name) entries.next(); - writeEntry(out, charset, name, attrib.getValue(name)); + writeEntry(out, name, attrib.getValue(name), encoder, buffer); } out.write(LINE_SEPARATOR); } } - private static void writeEntry(OutputStream os, Charset charset, Attributes.Name name, - String value) throws IOException { - int offset = 0; - int limit = LINE_LENGTH_LIMIT; - byte[] out = (name.toString() + ": ").getBytes("ISO8859_1"); //$NON-NLS-1$ //$NON-NLS-2$ - if (out.length > limit) { - while (out.length - offset >= limit) { - int len = out.length - offset; - if (len > limit) { - len = limit; - } - if (offset > 0) { - os.write(' '); - } - os.write(out, offset, len); - os.write(LINE_SEPARATOR); - offset += len; - limit = LINE_LENGTH_LIMIT - 1; - } - } - int size = out.length - offset; - final byte[] outBuf = new byte[LINE_LENGTH_LIMIT]; - System.arraycopy(out, offset, outBuf, 0, size); - for (int i = 0; i < value.length(); i++) { - char[] oneChar = new char[1]; - oneChar[0] = value.charAt(i); - byte[] buf; - if (oneChar[0] < 128 || charset == null) { - byte[] oneByte = new byte[1]; - oneByte[0] = (byte) oneChar[0]; - buf = oneByte; - } else { - buf = charset.encode(CharBuffer.wrap(oneChar, 0, 1)).array(); - } - if (size + buf.length > limit) { - if (limit != LINE_LENGTH_LIMIT) { - os.write(' '); - } - os.write(outBuf, 0, size); - os.write(LINE_SEPARATOR); - limit = LINE_LENGTH_LIMIT - 1; - size = 0; - } - if (buf.length == 1) { - outBuf[size] = buf[0]; - } else { - System.arraycopy(buf, 0, outBuf, size, buf.length); - } - size += buf.length; + private static void writeEntry(OutputStream os, Attributes.Name name, + String value, CharsetEncoder encoder, ByteBuffer bBuf) + throws IOException { + byte[] out = name.getBytes(); + if (out.length > LINE_LENGTH_LIMIT) { + throw new IOException(Messages.getString( + "archive.33", name, Integer.valueOf(LINE_LENGTH_LIMIT))); //$NON-NLS-1$ } - if (size > 0) { - if (limit != LINE_LENGTH_LIMIT) { - os.write(' '); + + os.write(out); + os.write(VALUE_SEPARATOR); + + encoder.reset(); + bBuf.clear().limit(LINE_LENGTH_LIMIT - out.length - 2); + + CharBuffer cBuf = CharBuffer.wrap(value); + CoderResult r; + + while (true) { + r = encoder.encode(cBuf, bBuf, true); + if (CoderResult.UNDERFLOW == r) { + r = encoder.flush(bBuf); } - os.write(outBuf, 0, size); + os.write(bBuf.array(), bBuf.arrayOffset(), bBuf.position()); os.write(LINE_SEPARATOR); + if (CoderResult.UNDERFLOW == r) { + break; + } + os.write(' '); + bBuf.clear().limit(LINE_LENGTH_LIMIT - 1); } } } diff --git a/archive/src/main/java/java/util/jar/Pack200.java b/archive/src/main/java/java/util/jar/Pack200.java index e0689e0..8145fa1 100644 --- a/archive/src/main/java/java/util/jar/Pack200.java +++ b/archive/src/main/java/java/util/jar/Pack200.java @@ -27,8 +27,6 @@ import java.util.SortedMap; /** * Class factory for {@link Pack200.Packer} and {@link Pack200.Unpacker}. - * - * @since Android 1.0 */ public abstract class Pack200 { @@ -39,8 +37,8 @@ public abstract class Pack200 { /** * Prevent this class from being instantiated. */ - private Pack200(){ - //do nothing + private Pack200() { + // do nothing } /** @@ -50,10 +48,8 @@ public abstract class Pack200 { * {@code 'java.util.jar.Pack200.Packer'}. If this system property is * defined an instance of the specified class is returned, otherwise the * system's default implementation is returned. - * </p> * * @return an instance of {@code Packer} - * @since Android 1.0 */ public static Pack200.Packer newPacker() { return (Packer) AccessController @@ -82,10 +78,8 @@ public abstract class Pack200 { * property {@code 'java.util.jar.Pack200.Unpacker'}. If this system * property is defined an instance of the specified class is returned, * otherwise the system's default implementation is returned. - * </p> * * @return a instance of {@code Unpacker}. - * @since Android 1.0 */ public static Pack200.Unpacker newUnpacker() { return (Unpacker) AccessController @@ -107,150 +101,109 @@ public abstract class Pack200 { /** * The interface defining the API for converting a JAR file to an output * stream in the Pack200 format. - * - * @since Android 1.0 */ public static interface Packer { /** * the format of a class attribute name. - * - * @since Android 1.0 */ static final String CLASS_ATTRIBUTE_PFX = "pack.class.attribute."; //$NON-NLS-1$ /** * the format of a code attribute name. - * - * @since Android 1.0 */ static final String CODE_ATTRIBUTE_PFX = "pack.code.attribute."; //$NON-NLS-1$ /** * the deflation hint to set in the output archive. - * - * @since Android 1.0 */ static final String DEFLATE_HINT = "pack.deflate.hint";//$NON-NLS-1$ /** * the indicated amount of effort to use in compressing the archive. - * - * @since Android 1.0 */ static final String EFFORT = "pack.effort";//$NON-NLS-1$ /** * a String representation for {@code error}. - * - * @since Android 1.0 */ static final String ERROR = "error";//$NON-NLS-1$ /** * a String representation of {@code false}. - * - * @since Android 1.0 */ static final String FALSE = "false";//$NON-NLS-1$ /** * the format of a field attribute name. - * - * @since Android 1.0 */ static final String FIELD_ATTRIBUTE_PFX = "pack.field.attribute.";//$NON-NLS-1$ /** * a String representation for {@code keep}. - * - * @since Android 1.0 */ static final String KEEP = "keep";//$NON-NLS-1$ /** * decide if all elements shall transmit in their original order. - * - * @since Android 1.0 */ static final String KEEP_FILE_ORDER = "pack.keep.file.order";//$NON-NLS-1$ /** * a String representation for {@code latest}. - * - * @since Android 1.0 */ static final String LATEST = "latest";//$NON-NLS-1$ /** * the format of a method attribute name. - * - * @since Android 1.0 */ static final String METHOD_ATTRIBUTE_PFX = "pack.method.attribute.";//$NON-NLS-1$ /** * if it shall attempt to determine the latest modification time if this * is set to {@code LATEST}. - * - * @since Android 1.0 */ static final String MODIFICATION_TIME = "pack.modification.time";//$NON-NLS-1$ /** * a String representation of {@code pass}. - * - * @since Android 1.0 */ static final String PASS = "pass";//$NON-NLS-1$ /** * the file that will not be compressed. - * - * @since Android 1.0 */ static final String PASS_FILE_PFX = "pack.pass.file.";//$NON-NLS-1$ /** * packer progress as a percentage. - * - * @since Android 1.0 */ static final String PROGRESS = "pack.progress";//$NON-NLS-1$ /** * The number of bytes of each archive segment. - * - * @since Android 1.0 */ static final String SEGMENT_LIMIT = "pack.segment.limit";//$NON-NLS-1$ /** * a String representation of {@code strip}. - * - * @since Android 1.0 */ static final String STRIP = "strip";//$NON-NLS-1$ /** * a String representation of {@code true}. - * - * @since Android 1.0 */ static final String TRUE = "true";//$NON-NLS-1$ /** * the action to take if an unknown attribute is encountered. - * - * @since Android 1.0 */ static final String UNKNOWN_ATTRIBUTE = "pack.unknown.attribute";//$NON-NLS-1$ /** * Returns a sorted map of the properties of this packer. - * + * * @return the properties of the packer. - * @since Android 1.0 */ SortedMap<String, String> properties(); @@ -263,7 +216,6 @@ public abstract class Pack200 { * stream of compressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void pack(JarFile in, OutputStream out) throws IOException; @@ -277,7 +229,6 @@ public abstract class Pack200 { * stream of compressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void pack(JarInputStream in, OutputStream out) throws IOException; @@ -301,52 +252,39 @@ public abstract class Pack200 { /** * The interface defining the API for converting a packed stream in the * Pack200 format to a JAR file. - * - * @since Android 1.0 */ public static interface Unpacker { /** * The String indicating if the unpacker should ignore all transmitted * values,can be replaced by either {@code true} or {@code false}. - * - * @since Android 1.0 */ static final String DEFLATE_HINT = "unpack.deflate.hint";//$NON-NLS-1$ /** * a String representation of {@code false}. - * - * @since Android 1.0 */ static final String FALSE = "false";//$NON-NLS-1$ /** * a String representation of {@code keep}. - * - * @since Android 1.0 */ static final String KEEP = "keep";//$NON-NLS-1$ /** * the progress as a {@code percentage}. - * - * @since Android 1.0 */ static final String PROGRESS = "unpack.progress";//$NON-NLS-1$ /** * a String representation of {@code true}. - * - * @since Android 1.0 */ static final String TRUE = "true";//$NON-NLS-1$ /** * Returns a sorted map of the properties of this unpacker. - * + * * @return the properties of unpacker. - * @since Android 1.0 */ SortedMap<String, String> properties(); @@ -359,7 +297,6 @@ public abstract class Pack200 { * JAR output stream of uncompressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void unpack(InputStream in, JarOutputStream out) throws IOException; @@ -373,7 +310,6 @@ public abstract class Pack200 { * JAR output stream of uncompressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void unpack(File in, JarOutputStream out) throws IOException; diff --git a/archive/src/main/java/java/util/zip/Adler32.java b/archive/src/main/java/java/util/zip/Adler32.java index 8eaa18e..a5f77d7 100644 --- a/archive/src/main/java/java/util/zip/Adler32.java +++ b/archive/src/main/java/java/util/zip/Adler32.java @@ -17,14 +17,12 @@ package java.util.zip; - /** * The Adler-32 class is used to compute the {@code Adler32} checksum from a set * of data. Compared to the CRC-32 algorithm it trades reliabilty for speed. * Refer to RFC 1950 for the specification. - * + * * @see CRC32 - * @since Android 1.0 */ public class Adler32 implements java.util.zip.Checksum { @@ -34,7 +32,6 @@ public class Adler32 implements java.util.zip.Checksum { * Returns the {@code Adler32} checksum for all input received. * * @return The checksum for this instance. - * @since Android 1.0 */ public long getValue() { return adler; @@ -42,8 +39,6 @@ public class Adler32 implements java.util.zip.Checksum { /** * Reset this instance to its initial checksum. - * - * @since Android 1.0 */ public void reset() { adler = 1; @@ -55,7 +50,6 @@ public class Adler32 implements java.util.zip.Checksum { * * @param i * the byte to update checksum with. - * @since Android 1.0 */ public void update(int i) { adler = updateByteImpl(i, adler); @@ -66,7 +60,6 @@ public class Adler32 implements java.util.zip.Checksum { * * @param buf * bytes to update checksum with. - * @since Android 1.0 */ public void update(byte[] buf) { update(buf, 0, buf.length); @@ -85,7 +78,6 @@ public class Adler32 implements java.util.zip.Checksum { * @throws ArrayIndexOutOfBoundsException * if {@code offset > buf.length} or {@code nbytes} is negative * or {@code offset + nbytes > buf.length}. - * @since Android 1.0 */ public void update(byte[] buf, int off, int nbytes) { // avoid int overflow, check null buf diff --git a/archive/src/main/java/java/util/zip/CRC32.java b/archive/src/main/java/java/util/zip/CRC32.java index 36dc376..59e8f81 100644 --- a/archive/src/main/java/java/util/zip/CRC32.java +++ b/archive/src/main/java/java/util/zip/CRC32.java @@ -17,12 +17,9 @@ package java.util.zip; - /** * The CRC32 class is used to compute a CRC32 checksum from data provided as * input value. - * - * @since Android 1.0 */ public class CRC32 implements java.util.zip.Checksum { @@ -34,7 +31,6 @@ public class CRC32 implements java.util.zip.Checksum { * Returns the CRC32 checksum for all input received. * * @return The checksum for this instance. - * @since Android 1.0 */ public long getValue() { return crc; @@ -42,8 +38,6 @@ public class CRC32 implements java.util.zip.Checksum { /** * Resets the CRC32 checksum to it initial state. - * - * @since Android 1.0 */ public void reset() { tbytes = crc = 0; @@ -52,10 +46,9 @@ public class CRC32 implements java.util.zip.Checksum { /** * Updates this checksum with the byte value provided as integer. - * + * * @param val * represents the byte to update the checksum. - * @since Android 1.0 */ public void update(int val) { crc = updateByteImpl((byte) val, crc); @@ -66,7 +59,6 @@ public class CRC32 implements java.util.zip.Checksum { * * @param buf * the buffer holding the data to update the checksum with. - * @since Android 1.0 */ public void update(byte[] buf) { update(buf, 0, buf.length); @@ -82,7 +74,6 @@ public class CRC32 implements java.util.zip.Checksum { * the offset in {@code buf} to obtain data from. * @param nbytes * the number of bytes to read from {@code buf}. - * @since Android 1.0 */ public void update(byte[] buf, int off, int nbytes) { // avoid int overflow, check null buf diff --git a/archive/src/main/java/java/util/zip/CheckedInputStream.java b/archive/src/main/java/java/util/zip/CheckedInputStream.java index 659125a..6513e66 100644 --- a/archive/src/main/java/java/util/zip/CheckedInputStream.java +++ b/archive/src/main/java/java/util/zip/CheckedInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.IOException; import java.io.InputStream; @@ -26,8 +25,6 @@ import java.io.InputStream; * same time as the data, on which the checksum is computed, is read from a * stream. The purpose of this checksum is to establish data integrity, * comparing the computed checksum against a published checksum value. - * - * @since Android 1.0 */ public class CheckedInputStream extends java.io.FilterInputStream { @@ -42,7 +39,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * the input stream to calculate checksum from. * @param csum * an entity implementing the checksum algorithm. - * @since Android 1.0 */ public CheckedInputStream(InputStream is, Checksum csum) { super(is); @@ -57,7 +53,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * otherwise. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -84,7 +79,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * end of the filtered stream while reading the data. * @throws IOException * if this stream is closed or some I/O error occurs. - * @since Android 1.0 */ @Override public int read(byte[] buf, int off, int nbytes) throws IOException { @@ -99,7 +93,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * Returns the checksum calculated on the stream read so far. * * @return the updated checksum. - * @since Android 1.0 */ public Checksum getChecksum() { return check; @@ -114,7 +107,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * @throws IOException * if this stream is closed or another I/O error occurs. * @return the number of bytes skipped. - * @since Android 1.0 */ @Override public long skip(long nbytes) throws IOException { diff --git a/archive/src/main/java/java/util/zip/CheckedOutputStream.java b/archive/src/main/java/java/util/zip/CheckedOutputStream.java index 9699492..08fe799 100644 --- a/archive/src/main/java/java/util/zip/CheckedOutputStream.java +++ b/archive/src/main/java/java/util/zip/CheckedOutputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.IOException; import java.io.OutputStream; @@ -26,8 +25,6 @@ import java.io.OutputStream; * of all data written to a stream. The purpose of this checksum is to establish * data integrity, by publishing the checksum to other parties wanting to read * the non corrupted data. - * - * @since Android 1.0 */ public class CheckedOutputStream extends java.io.FilterOutputStream { @@ -42,7 +39,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * the output stream to calculate checksum for. * @param cs * an entity implementing the checksum algorithm. - * @since Android 1.0 */ public CheckedOutputStream(OutputStream os, Checksum cs) { super(os); @@ -53,7 +49,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * Returns the checksum calculated on the stream read so far. * * @return the updated checksum. - * @since Android 1.0 */ public Checksum getChecksum() { return check; @@ -67,7 +62,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * the data value to written to the output stream. * @throws IOException * if an IO error has occurred. - * @since Android 1.0 */ @Override public void write(int val) throws IOException { @@ -88,7 +82,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * number of bytes to write to the output stream. * @throws IOException * if an IO error has occurred. - * @since Android 1.0 */ @Override public void write(byte[] buf, int off, int nbytes) throws IOException { diff --git a/archive/src/main/java/java/util/zip/Checksum.java b/archive/src/main/java/java/util/zip/Checksum.java index 0405c08..901ff7a 100644 --- a/archive/src/main/java/java/util/zip/Checksum.java +++ b/archive/src/main/java/java/util/zip/Checksum.java @@ -20,8 +20,6 @@ package java.util.zip; /** * Holds information about a checksum which was computed with the methods * implementing a checksum algorithm. - * - * @since Android 1.0 */ public interface Checksum { @@ -29,37 +27,32 @@ public interface Checksum { * Returns the current calculated checksum value. * * @return the checksum. - * @since Android 1.0 */ public long getValue(); /** * Resets the checksum value applied before beginning calculations on a new * stream of data. - * - * @since Android 1.0 */ public void reset(); /** - * Updates the checksum value with the given byte. - * - * @param val - * the byte to update the checksum with. - * @since Android 1.0 - */ - public void update(int val); - - /** * Updates the checksum with the given bytes. - * + * * @param buf * the byte array from which to read the bytes. * @param off * the initial position in {@code buf} to read the bytes from. * @param nbytes * the number of bytes to read from {@code buf}. - * @since Android 1.0 */ public void update(byte[] buf, int off, int nbytes); + + /** + * Updates the checksum value with the given byte. + * + * @param val + * the byte to update the checksum with. + */ + public void update(int val); } diff --git a/archive/src/main/java/java/util/zip/DataFormatException.java b/archive/src/main/java/java/util/zip/DataFormatException.java index 9ffe2ab..1e9c5a2 100644 --- a/archive/src/main/java/java/util/zip/DataFormatException.java +++ b/archive/src/main/java/java/util/zip/DataFormatException.java @@ -20,8 +20,6 @@ package java.util.zip; /** * {@code DataFormatException} is used to indicate an error in the format of a * particular data stream which is to be uncompressed. - * - * @since Android 1.0 */ public class DataFormatException extends Exception { @@ -29,8 +27,6 @@ public class DataFormatException extends Exception { /** * Constructs a new {@code DataFormatException} instance. - * - * @since Android 1.0 */ public DataFormatException() { super(); @@ -39,10 +35,9 @@ public class DataFormatException extends Exception { /** * Constructs a new {@code DataFormatException} instance with the specified * message. - * + * * @param detailMessage * the detail message for the exception. - * @since Android 1.0 */ public DataFormatException(String detailMessage) { super(detailMessage); diff --git a/archive/src/main/java/java/util/zip/Deflater.java b/archive/src/main/java/java/util/zip/Deflater.java index f91f1ca..38771a8 100644 --- a/archive/src/main/java/java/util/zip/Deflater.java +++ b/archive/src/main/java/java/util/zip/Deflater.java @@ -17,6 +17,10 @@ package java.util.zip; +// BEGIN android-changed +// import org.apache.harmony.luni.platform.OSResourcesMonitor; +// END android-changed + /** * This class compresses data using the <i>DEFLATE</i> algorithm (see <a * href="http://www.gzip.org/algorithm.txt">specification</a>). @@ -24,83 +28,65 @@ package java.util.zip; * Basically this class is part of the API to the stream based ZLIB compression * library and is used as such by {@code DeflaterOutputStream} and its * descendants. - * </p> * <p> * The typical usage of a {@code Deflater} instance outside this package * consists of a specific call to one of its constructors before being passed to * an instance of {@code DeflaterOutputStream}. - * </p> - * + * * @see DeflaterOutputStream * @see Inflater - * @since Android 1.0 */ public class Deflater { /** * Upper bound for the compression level range. - * - * @since Android 1.0 */ public static final int BEST_COMPRESSION = 9; /** * Lower bound for compression level range. - * - * @since Android 1.0 */ public static final int BEST_SPEED = 1; - + /** * Usage of the default compression level. - * - * @since Android 1.0 */ public static final int DEFAULT_COMPRESSION = -1; - + /** * Default value for compression strategy. - * - * @since Android 1.0 */ public static final int DEFAULT_STRATEGY = 0; - + /** * Default value for compression method. - * - * @since Android 1.0 */ public static final int DEFLATED = 8; - + /** * Possible value for compression strategy. - * - * @since Android 1.0 */ public static final int FILTERED = 1; - + /** * Possible value for compression strategy. - * - * @since Android 1.0 */ public static final int HUFFMAN_ONLY = 2; - + /** * Possible value for compression level. - * - * @since Android 1.0 */ public static final int NO_COMPRESSION = 0; private static final int Z_NO_FLUSH = 0; private static final int Z_FINISH = 4; - + // Fill in the JNI id caches private static native void oneTimeInitialization(); - - // A stub buffer used when deflate() called while inputBuffer has not been set. + + // A stub buffer used when deflate() called while inputBuffer has not been + // set. private static final byte[] STUB_INPUT_BUFFER = new byte[0]; static { @@ -120,21 +106,19 @@ public class Deflater { private byte[] inputBuffer; private int inRead; - + private int inLength; - + /** * Constructs a new {@code Deflater} instance with default compression * level. The strategy can be specified with {@link #setStrategy}, only. A * header is added to the output by default; use constructor {@code * Deflater(level, boolean)} if you need to omit the header. - * - * @since Android 1.0 */ public Deflater() { this(DEFAULT_COMPRESSION, false); } - + /** * Constructs a new {@code Deflater} instance with a specific compression * level. The strategy can be specified with {@code setStrategy}, only. A @@ -143,7 +127,6 @@ public class Deflater { * * @param level * the compression level in the range between 0 and 9. - * @since Android 1.0 */ public Deflater(int level) { this(level, false); @@ -159,7 +142,6 @@ public class Deflater { * the compression level in the range between 0 and 9. * @param noHeader * {@code true} indicates that no ZLIB header should be written. - * @since Android 1.0 */ public Deflater(int level, boolean noHeader) { super(); @@ -167,7 +149,8 @@ public class Deflater { throw new IllegalArgumentException(); } compressLevel = level; - streamHandle = createStream(compressLevel, strategy, noHeader); + streamHandle = createStreamWithMemoryEnsurance(compressLevel, strategy, + noHeader); } /** @@ -178,7 +161,6 @@ public class Deflater { * buffer to write compressed data to. * @return number of bytes of compressed data written to {@code buf}. * @see #deflate(byte[], int, int) - * @since Android 1.0 */ public int deflate(byte[] buf) { return deflate(buf, 0, buf.length); @@ -195,7 +177,6 @@ public class Deflater { * @param nbytes * maximum number of bytes of compressed data to be written. * @return the number of bytes of compressed data written to {@code buf}. - * @since Android 1.0 */ public synchronized int deflate(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -224,8 +205,6 @@ public class Deflater { * finalize()}, it can be called explicitly in order to free native * resources before the next GC cycle. After {@code end()} was called other * methods will typically throw an {@code IllegalStateException}. - * - * @since Android 1.0 */ public synchronized void end() { if (streamHandle != -1) { @@ -245,7 +224,6 @@ public class Deflater { * to it. * * @see #finished - * @since Android 1.0 */ public synchronized void finish() { flushParm = Z_FINISH; @@ -256,7 +234,6 @@ public class Deflater { * compressed. * * @return true if all data has been compressed, false otherwise. - * @since Android 1.0 */ public synchronized boolean finished() { return finished; @@ -271,7 +248,6 @@ public class Deflater { * used. * @see #setDictionary(byte[]) * @see #setDictionary(byte[], int, int) - * @since Android 1.0 */ public synchronized int getAdler() { if (streamHandle == -1) { @@ -287,14 +263,13 @@ public class Deflater { * Returns the total number of bytes of input consumed by the {@code Deflater}. * * @return number of bytes of input read. - * @since Android 1.0 */ public synchronized int getTotalIn() { if (streamHandle == -1) { throw new IllegalStateException(); } - return (int)getTotalInImpl(streamHandle); + return (int) getTotalInImpl(streamHandle); } private synchronized native long getTotalInImpl(long handle); @@ -303,14 +278,13 @@ public class Deflater { * Returns the total number of compressed bytes output by this {@code Deflater}. * * @return number of compressed bytes output. - * @since Android 1.0 */ public synchronized int getTotalOut() { if (streamHandle == -1) { throw new IllegalStateException(); } - return (int)getTotalOutImpl(streamHandle); + return (int) getTotalOutImpl(streamHandle); } private synchronized native long getTotalOutImpl(long handle); @@ -327,7 +301,6 @@ public class Deflater { * @see #finished() * @see #setInput(byte[]) * @see #setInput(byte[], int, int) - * @since Android 1.0 */ public synchronized boolean needsInput() { if (inputBuffer == null) { @@ -343,7 +316,6 @@ public class Deflater { * {@code true} if the {@code Deflater} is to be reused. * * @see #finished - * @since Android 1.0 */ public synchronized void reset() { if (streamHandle == -1) { @@ -367,7 +339,6 @@ public class Deflater { * @param buf * the buffer containing the dictionary data bytes. * @see Deflater#Deflater(int, boolean) - * @since Android 1.0 */ public void setDictionary(byte[] buf) { setDictionary(buf, 0, buf.length); @@ -378,7 +349,7 @@ public class Deflater { * setDictionary() can only be called if this {@code Deflater} supports the writing * of ZLIB headers. This is the default behaviour but can be overridden * using {@code Deflater(int, boolean)}. - * + * * @param buf * the buffer containing the dictionary data bytes. * @param off @@ -386,7 +357,6 @@ public class Deflater { * @param nbytes * the length of the data. * @see Deflater#Deflater(int, boolean) - * @since Android 1.0 */ public synchronized void setDictionary(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -410,7 +380,6 @@ public class Deflater { * * @param buf * the buffer. - * @since Android 1.0 */ public void setInput(byte[] buf) { setInput(buf, 0, buf.length); @@ -427,7 +396,6 @@ public class Deflater { * the offset of the data. * @param nbytes * the length of the data. - * @since Android 1.0 */ public synchronized void setInput(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -463,7 +431,6 @@ public class Deflater { * compression level to use * @exception IllegalArgumentException * If the compression level is invalid. - * @since Android 1.0 */ public synchronized void setLevel(int level) { if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) { @@ -485,7 +452,6 @@ public class Deflater { * @exception IllegalArgumentException * If the strategy specified is not one of FILTERED, * HUFFMAN_ONLY or DEFAULT_STRATEGY. - * @since Android 1.0 */ public synchronized void setStrategy(int strategy) { if (strategy < DEFAULT_STRATEGY || strategy > HUFFMAN_ONLY) { @@ -496,14 +462,14 @@ public class Deflater { } this.strategy = strategy; } - + /** * Returns a long int of total number of bytes read by the {@code Deflater}. This * method performs the same as {@code getTotalIn} except it returns a long value * instead of an integer * + * @see #getTotalIn() * @return total number of bytes read by {@code Deflater}. - * @since Android 1.0 */ public synchronized long getBytesRead() { // Throw NPE here @@ -518,8 +484,8 @@ public class Deflater { * method performs the same as {@code getTotalOut} except it returns a long * value instead of an integer * + * @see #getTotalOut() * @return bytes exactly write by {@code Deflater} - * @since Android 1.0 */ public synchronized long getBytesWritten() { // Throw NPE here @@ -529,5 +495,13 @@ public class Deflater { return getTotalOutImpl(streamHandle); } + private long createStreamWithMemoryEnsurance(int level, int strategy1, + boolean noHeader1) { + // BEGIN android-changed + // OSResourcesMonitor.ensurePhysicalMemoryCapacity(); + // END android-changed + return createStream(level, strategy1, noHeader1); + } + private native long createStream(int level, int strategy1, boolean noHeader1); } diff --git a/archive/src/main/java/java/util/zip/DeflaterOutputStream.java b/archive/src/main/java/java/util/zip/DeflaterOutputStream.java index 773e4c4..03769fb 100644 --- a/archive/src/main/java/java/util/zip/DeflaterOutputStream.java +++ b/archive/src/main/java/java/util/zip/DeflaterOutputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -28,24 +27,19 @@ import org.apache.harmony.archive.internal.nls.Messages; * This class provides an implementation of {@code FilterOutputStream} that * compresses data using the <i>DEFLATE</i> algorithm. Basically it wraps the * {@code Deflater} class and takes care of the buffering. - * + * * @see Deflater - * @since Android 1.0 */ public class DeflaterOutputStream extends FilterOutputStream { static final int BUF_SIZE = 512; /** * The buffer for the data to be written to. - * - * @since Android 1.0 */ protected byte[] buf; /** * The deflater used. - * - * @since Android 1.0 */ protected Deflater def; @@ -61,7 +55,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * @param def * is the specific {@code Deflater} that is used to compress * data. - * @since Android 1.0 */ public DeflaterOutputStream(OutputStream os, Deflater def) { this(os, def, BUF_SIZE); @@ -76,7 +69,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * * @param os * is the OutputStream where to write the compressed data to. - * @since Android 1.0 */ public DeflaterOutputStream(OutputStream os) { this(os, new Deflater()); @@ -94,7 +86,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * data. * @param bsize * is the size to be used for the internal buffer. - * @since Android 1.0 */ public DeflaterOutputStream(OutputStream os, Deflater def, int bsize) { super(os); @@ -114,7 +105,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * * @throws IOException * If an error occurs during deflation. - * @since Android 1.0 */ protected void deflate() throws IOException { int x = 0; @@ -132,7 +122,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * @throws IOException * If an error occurs while closing the data compression * process. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -149,7 +138,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * * @throws IOException * If an error occurs. - * @since Android 1.0 */ public void finish() throws IOException { if (done) { @@ -186,7 +174,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * the number of bytes of data to read from the buffer. * @throws IOException * If an error occurs during writing. - * @since Android 1.0 */ @Override public void write(byte[] buffer, int off, int nbytes) throws IOException { diff --git a/archive/src/main/java/java/util/zip/GZIPInputStream.java b/archive/src/main/java/java/util/zip/GZIPInputStream.java index fc70d62..bb84f5b 100644 --- a/archive/src/main/java/java/util/zip/GZIPInputStream.java +++ b/archive/src/main/java/java/util/zip/GZIPInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -28,49 +27,40 @@ import org.apache.harmony.archive.internal.nls.Messages; * The {@code GZIPInputStream} class is used to read data stored in the GZIP * format, reading and decompressing GZIP data from the underlying stream into * its buffer. - * - * @since Android 1.0 */ -public class GZIPInputStream extends java.util.zip.InflaterInputStream { +public class GZIPInputStream extends InflaterInputStream { + + private static final int FCOMMENT = 16; + + private static final int FEXTRA = 4; + + private static final int FHCRC = 2; + + private static final int FNAME = 8; + + /** + * The magic header for the GZIP format. + */ + public final static int GZIP_MAGIC = 0x8b1f; /** * The checksum algorithm used when handling uncompressed data. - * - * @since Android 1.0 */ protected CRC32 crc = new CRC32(); /** * Indicates the end of the input stream. - * - * @since Android 1.0 */ protected boolean eos = false; /** - * The magic header for the GZIP format. - * - * @since Android 1.0 - */ - public final static int GZIP_MAGIC = 0x8b1f; - - private static final int FHCRC = 2; - - private static final int FEXTRA = 4; - - private static final int FNAME = 8; - - private static final int FCOMMENT = 16; - - /** * Construct a {@code GZIPInputStream} to read from GZIP data from the * underlying stream. - * + * * @param is * the {@code InputStream} to read data from. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPInputStream(InputStream is) throws IOException { this(is, BUF_SIZE); @@ -86,7 +76,6 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { * the internal read buffer size. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPInputStream(InputStream is, int size) throws IOException { super(is, new Inflater(true), size); @@ -134,6 +123,15 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { } } + /** + * Closes this stream and any underlying streams. + */ + @Override + public void close() throws IOException { + eos = true; + super.close(); + } + private long getLong(byte[] buffer, int off) { long l = 0; l |= (buffer[off] & 0xFF); @@ -147,12 +145,23 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { return (buffer[off] & 0xFF) | ((buffer[off + 1] & 0xFF) << 8); } + /** + * Reads and decompresses GZIP data from the underlying stream into the + * given buffer. + * + * @param buffer + * Buffer to receive data + * @param off + * Offset in buffer to store data + * @param nbytes + * Number of bytes to read + */ @Override public int read(byte[] buffer, int off, int nbytes) throws IOException { if (closed) { throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$ } - if(eof){ + if (eof) { return -1; } // avoid int overflow, check null buffer @@ -164,17 +173,15 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { } else if (!eos) { eos = true; // Get non-compressed bytes read by fill - // BEGIN android-changed - // copied from newer version of harmony int size = inf.getRemaining(); final int trailerSize = 8; // crc (4 bytes) + total out (4 - // bytes) + // bytes) byte[] b = new byte[trailerSize]; int copySize = (size > trailerSize) ? trailerSize : size; System.arraycopy(buf, len - size, b, 0, copySize); - readFully(b, copySize, trailerSize - copySize); - // END android-changed + readFully(b, copySize, trailerSize - copySize); + if (getLong(b, 0) != crc.getValue()) { throw new IOException(Messages.getString("archive.20")); //$NON-NLS-1$ } @@ -186,12 +193,6 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { } throw new ArrayIndexOutOfBoundsException(); } - - @Override - public void close() throws IOException { - eos = true; - super.close(); - } private void readFully(byte[] buffer, int offset, int length) throws IOException { diff --git a/archive/src/main/java/java/util/zip/GZIPOutputStream.java b/archive/src/main/java/java/util/zip/GZIPOutputStream.java index fa41e19..f146da1 100644 --- a/archive/src/main/java/java/util/zip/GZIPOutputStream.java +++ b/archive/src/main/java/java/util/zip/GZIPOutputStream.java @@ -17,22 +17,17 @@ package java.util.zip; - import java.io.IOException; import java.io.OutputStream; /** * The {@code GZIPOutputStream} class is used to write data to a stream in the * GZIP storage format. - * - * @since Android 1.0 */ public class GZIPOutputStream extends DeflaterOutputStream { /** * The checksum algorithm used when treating uncompressed data. - * - * @since Android 1.0 */ protected CRC32 crc = new CRC32(); @@ -44,7 +39,6 @@ public class GZIPOutputStream extends DeflaterOutputStream { * the {@code OutputStream} to write data to. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPOutputStream(OutputStream os) throws IOException { this(os, BUF_SIZE); @@ -61,7 +55,6 @@ public class GZIPOutputStream extends DeflaterOutputStream { * the internal buffer size. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPOutputStream(OutputStream os, int size) throws IOException { super(os, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size); @@ -76,10 +69,9 @@ public class GZIPOutputStream extends DeflaterOutputStream { /** * Indicates to the stream that all data has been written out, and any GZIP * terminal data can now be written. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public void finish() throws IOException { @@ -88,24 +80,29 @@ public class GZIPOutputStream extends DeflaterOutputStream { writeLong(crc.tbytes); } + /** + * Write up to nbytes of data from the given buffer, starting at offset off, + * to the underlying stream in GZIP format. + */ @Override public void write(byte[] buffer, int off, int nbytes) throws IOException { super.write(buffer, off, nbytes); crc.update(buffer, off, nbytes); } - private int writeShort(int i) throws IOException { - out.write(i & 0xFF); - out.write((i >> 8) & 0xFF); + private long writeLong(long i) throws IOException { + // Write out the long value as an unsigned int + int unsigned = (int) i; + out.write(unsigned & 0xFF); + out.write((unsigned >> 8) & 0xFF); + out.write((unsigned >> 16) & 0xFF); + out.write((unsigned >> 24) & 0xFF); return i; } - private long writeLong(long i) throws IOException { - // Write out the long value as an unsigned int - out.write((int) (i & 0xFF)); - out.write((int) (i >> 8) & 0xFF); - out.write((int) (i >> 16) & 0xFF); - out.write((int) (i >> 24) & 0xFF); + private int writeShort(int i) throws IOException { + out.write(i & 0xFF); + out.write((i >> 8) & 0xFF); return i; } } diff --git a/archive/src/main/java/java/util/zip/Inflater.java b/archive/src/main/java/java/util/zip/Inflater.java index 9b93e54..cb1ce68 100644 --- a/archive/src/main/java/java/util/zip/Inflater.java +++ b/archive/src/main/java/java/util/zip/Inflater.java @@ -30,45 +30,65 @@ import java.io.FileDescriptor; * Basically this class is part of the API to the stream based ZLIB compression * library and is used as such by {@code InflaterInputStream} and its * descendants. - * </p> * <p> * The typical usage of a {@code Inflater} outside this package consists of a * specific call to one of its constructors before being passed to an instance * of {@code InflaterInputStream}. - * </p> - * + * * @see InflaterInputStream * @see Deflater - * @since Android 1.0 */ public class Inflater { + private static final byte MAGIC_NUMBER = 120; + + static { + oneTimeInitialization(); + } + + // Fill in the JNI id caches + private static native void oneTimeInitialization(); + private boolean finished; // Set by the inflateImpl native - private boolean needsDictionary; // Set by the inflateImpl native + private boolean gotFirstByte = false; - private long streamHandle = -1; + int inLength; int inRead; - - int inLength; - // Fill in the JNI id caches - private static native void oneTimeInitialization(); + private boolean needsDictionary; // Set by the inflateImpl native - static { - oneTimeInitialization(); - } - - private static final byte MAGIC_NUMBER = 120; - private boolean gotFirstByte = false; private boolean pass_magic_number_check = true; - + + private long streamHandle = -1; + + /** + * This constructor creates an inflater that expects a header from the input + * stream. Use {@code Inflater(boolean)} if the input comes without a ZLIB + * header. + */ + public Inflater() { + this(false); + } + + /** + * This constructor allows to create an inflater that expects no header from + * the input stream. + * + * @param noHeader + * {@code true} indicates that no ZLIB header comes with the + * input. + */ + public Inflater(boolean noHeader) { + streamHandle = createStream(noHeader); + } + + private native long createStream(boolean noHeader1); + /** * Release any resources associated with this {@code Inflater}. Any unused * input/output is discarded. This is also called by the finalize method. - * - * @since Android 1.0 */ public synchronized void end() { if (streamHandle != -1) { @@ -91,10 +111,9 @@ public class Inflater { * stream. If deflated bytes remain and {@code needsInput()} returns {@code * true} this method will return {@code false}. This method should be * called after all deflated input is supplied to the {@code Inflater}. - * + * * @return {@code true} if all input has been inflated, {@code false} * otherwise. - * @since Android 1.0 */ public synchronized boolean finished() { return finished; @@ -103,10 +122,9 @@ public class Inflater { /** * Returns the <i>Adler32</i> checksum of either all bytes inflated, or the * checksum of the preset dictionary if one has been supplied. - * + * * @return The <i>Adler32</i> checksum associated with this * {@code Inflater}. - * @since Android 1.0 . */ public synchronized int getAdler() { if (streamHandle == -1) { @@ -118,11 +136,40 @@ public class Inflater { private native synchronized int getAdlerImpl(long handle); /** + * Returns the total number of bytes read by the {@code Inflater}. This + * method performs the same as {@code getTotalIn()} except that it returns a + * {@code long} value instead of an integer. + * + * @return the total number of bytes read. + */ + public synchronized long getBytesRead() { + // Throw NPE here + if (streamHandle == -1) { + throw new NullPointerException(); + } + return getTotalInImpl(streamHandle); + } + + /** + * Returns a the total number of bytes read by the {@code Inflater}. This + * method performs the same as {@code getTotalOut} except it returns a + * {@code long} value instead of an integer. + * + * @return the total bytes written to the output buffer. + */ + public synchronized long getBytesWritten() { + // Throw NPE here + if (streamHandle == -1) { + throw new NullPointerException(); + } + return getTotalOutImpl(streamHandle); + } + + /** * Returns the number of bytes of current input remaining to be read by the * inflater. - * + * * @return the number of bytes of unread input. - * @since Android 1.0 */ public synchronized int getRemaining() { return inLength - inRead; @@ -131,9 +178,8 @@ public class Inflater { /** * Returns total number of bytes of input read by the {@code Inflater}. The * result value is limited by {@code Integer.MAX_VALUE}. - * + * * @return the total number of bytes read. - * @since Android 1.0 */ public synchronized int getTotalIn() { if (streamHandle == -1) { @@ -149,9 +195,8 @@ public class Inflater { /** * Returns total number of bytes written to the output buffer by the {@code * Inflater}. The result value is limited by {@code Integer.MAX_VALUE}. - * + * * @return the total bytes of output data written. - * @since Android 1.0 */ public synchronized int getTotalOut() { if (streamHandle == -1) { @@ -166,14 +211,13 @@ public class Inflater { /** * Inflates bytes from current input and stores them in {@code buf}. - * + * * @param buf * the buffer where decompressed data bytes are written. * @return the number of bytes inflated. * @throws DataFormatException * if the underlying stream is corrupted or was not compressed * using a {@code Deflater}. - * @since Android 1.0 */ public int inflate(byte[] buf) throws DataFormatException { return inflate(buf, 0, buf.length); @@ -182,7 +226,7 @@ public class Inflater { /** * Inflates up to n bytes from the current input and stores them in {@code * buf} starting at {@code off}. - * + * * @param buf * the buffer to write inflated bytes to. * @param off @@ -205,7 +249,7 @@ public class Inflater { if (streamHandle == -1) { throw new IllegalStateException(); } - + if (!pass_magic_number_check) { throw new DataFormatException(); } @@ -213,7 +257,7 @@ public class Inflater { if (needsInput()) { return 0; } - + boolean neededDict = needsDictionary; needsDictionary = false; int result = inflateImpl(buf, off, nbytes, streamHandle); @@ -229,41 +273,15 @@ public class Inflater { int nbytes, long handle); /** - * This constructor creates an inflater that expects a header from the input - * stream. Use {@code Inflater(boolean)} if the input comes without a ZLIB - * header. - * - * @since Android 1.0 - * @since Android 1.0 - */ - public Inflater() { - this(false); - } - - /** - * This constructor allows to create an inflater that expects no header from - * the input stream. - * - * @param noHeader - * {@code true} indicates that no ZLIB header comes with the - * input. - * @since Android 1.0 - */ - public Inflater(boolean noHeader) { - streamHandle = createStream(noHeader); - } - - /** * Indicates whether the input bytes were compressed with a preset * dictionary. This method should be called prior to {@code inflate()} to * determine whether a dictionary is required. If so {@code setDictionary()} * should be called with the appropriate dictionary prior to calling {@code * inflate()}. - * + * * @return {@code true} if a preset dictionary is required for inflation. * @see #setDictionary(byte[]) * @see #setDictionary(byte[], int, int) - * @since Android 1.0 */ public synchronized boolean needsDictionary() { return needsDictionary; @@ -271,11 +289,10 @@ public class Inflater { /** * Indicates that input has to be passed to the inflater. - * + * * @return {@code true} if {@code setInput} has to be called before * inflation can proceed. * @see #setInput(byte[]) - * @since Android 1.0 */ public synchronized boolean needsInput() { return inRead == inLength; @@ -284,8 +301,6 @@ public class Inflater { /** * Resets the {@code Inflater}. Should be called prior to inflating a new * set of data. - * - * @since Android 1.0 */ public synchronized void reset() { if (streamHandle == -1) { @@ -303,11 +318,10 @@ public class Inflater { * Sets the preset dictionary to be used for inflation to {@code buf}. * {@code needsDictionary()} can be called to determine whether the current * input was deflated using a preset dictionary. - * + * * @param buf * The buffer containing the dictionary bytes. * @see #needsDictionary - * @since Android 1.0 */ public synchronized void setDictionary(byte[] buf) { setDictionary(buf, 0, buf.length); @@ -316,7 +330,11 @@ public class Inflater { /** * Like {@code setDictionary(byte[])}, allowing to define a specific region * inside {@code buf} to be used as a dictionary. - * + * <p> + * The dictionary should be set if the {@link #inflate(byte[])} returned + * zero bytes inflated and {@link #needsDictionary()} returns + * <code>true</code>. + * * @param buf * the buffer containing the dictionary data bytes. * @param off @@ -324,7 +342,6 @@ public class Inflater { * @param nbytes * the length of the data. * @see #needsDictionary - * @since Android 1.0 */ public synchronized void setDictionary(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -345,11 +362,10 @@ public class Inflater { /** * Sets the current input to to be decrompressed. This method should only be * called if {@code needsInput()} returns {@code true}. - * + * * @param buf * the input buffer. * @see #needsInput - * @since Android 1.0 */ public synchronized void setInput(byte[] buf) { setInput(buf, 0, buf.length); @@ -360,7 +376,7 @@ public class Inflater { * {@code off} and ending at {@code nbytes - 1} where data is written after * decompression. This method should only be called if {@code needsInput()} * returns {@code true}. - * + * * @param buf * the input buffer. * @param off @@ -368,7 +384,6 @@ public class Inflater { * @param nbytes * the number of bytes to read. * @see #needsInput - * @since Android 1.0 */ public synchronized void setInput(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -394,10 +409,9 @@ public class Inflater { // And at a first glance it doesn't look like the first byte has // to be 120. // END android-note - if(!gotFirstByte && nbytes>0) - { - pass_magic_number_check = (buf[off] == MAGIC_NUMBER || nbytes > 1); - gotFirstByte = true; + if (!gotFirstByte && nbytes > 0) { + pass_magic_number_check = (buf[off] == MAGIC_NUMBER || nbytes > 1); + gotFirstByte = true; } } @@ -407,14 +421,13 @@ public class Inflater { * off} and ending at {@code nbytes - 1}. This method should only be called * if {@code needsInput()} returns {@code true}. * - * @param file + * @param fd * the input file. * @param off * the offset to read from in buffer. * @param nbytes * the number of bytes to read. * @see #needsInput - * @since Android 1.0 */ synchronized int setFileInput(FileDescriptor fd, long off, int nbytes) { if (streamHandle == -1) { @@ -426,38 +439,6 @@ public class Inflater { } // END android-added - /** - * Returns the total number of bytes read by the {@code Inflater}. This - * method performs the same as {@code getTotalIn()} except that it returns a - * {@code long} value instead of an integer. - * - * @return the total number of bytes read. - * @since Android 1.0 - */ - public synchronized long getBytesRead() { - // Throw NPE here - if (streamHandle == -1) { - throw new NullPointerException(); - } - return getTotalInImpl(streamHandle); - } - - /** - * Returns a the total number of bytes read by the {@code Inflater}. This - * method performs the same as {@code getTotalOut} except it returns a - * {@code long} value instead of an integer. - * - * @return the total bytes written to the output buffer. - * @since Android 1.0 - */ - public synchronized long getBytesWritten() { - // Throw NPE here - if (streamHandle == -1) { - throw new NullPointerException(); - } - return getTotalOutImpl(streamHandle); - } - private native synchronized void setInputImpl(byte[] buf, int off, int nbytes, long handle); @@ -465,6 +446,4 @@ public class Inflater { private native synchronized int setFileInputImpl(FileDescriptor fd, long off, int nbytes, long handle); // END android-added - - private native long createStream(boolean noHeader1); } diff --git a/archive/src/main/java/java/util/zip/InflaterInputStream.java b/archive/src/main/java/java/util/zip/InflaterInputStream.java index 5d3bda0..1fd3602 100644 --- a/archive/src/main/java/java/util/zip/InflaterInputStream.java +++ b/archive/src/main/java/java/util/zip/InflaterInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.EOFException; import java.io.FilterInputStream; import java.io.IOException; @@ -31,31 +30,24 @@ import org.apache.harmony.archive.internal.nls.Messages; * (see <a href="http://www.gzip.org/algorithm.txt">specification</a>). * Basically it wraps the {@code Inflater} class and takes care of the * buffering. - * + * * @see Inflater * @see DeflaterOutputStream - * @since Android 1.0 */ public class InflaterInputStream extends FilterInputStream { /** * The inflater used for this stream. - * - * @since Android 1.0 */ protected Inflater inf; /** * The input buffer used for decompression. - * - * @since Android 1.0 */ protected byte[] buf; /** * The length of the buffer. - * - * @since Android 1.0 */ protected int len; @@ -74,10 +66,9 @@ public class InflaterInputStream extends FilterInputStream { * InputStream} from which the compressed data is to be read from. Default * settings for the {@code Inflater} and internal buffer are be used. In * particular the Inflater expects a ZLIB header from the input stream. - * + * * @param is * the {@code InputStream} to read data from. - * @since Android 1.0 */ public InflaterInputStream(InputStream is) { this(is, new Inflater(), BUF_SIZE); @@ -86,12 +77,11 @@ public class InflaterInputStream extends FilterInputStream { /** * This constructor lets you pass a specifically initialized Inflater, * for example one that expects no ZLIB header. - * + * * @param is * the {@code InputStream} to read data from. * @param inf * the specific {@code Inflater} for uncompressing data. - * @since Android 1.0 */ public InflaterInputStream(InputStream is, Inflater inf) { this(is, inf, BUF_SIZE); @@ -100,14 +90,13 @@ public class InflaterInputStream extends FilterInputStream { /** * This constructor lets you specify both the {@code Inflater} as well as * the internal buffer size to be used. - * + * * @param is * the {@code InputStream} to read data from. * @param inf * the specific {@code Inflater} for uncompressing data. * @param bsize * the size to be used for the internal buffer. - * @since Android 1.0 */ public InflaterInputStream(InputStream is, Inflater inf, int bsize) { super(is); @@ -129,11 +118,10 @@ public class InflaterInputStream extends FilterInputStream { /** * Reads a single byte of decompressed data. - * + * * @return the byte read. * @throws IOException * if an error occurs reading the byte. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -147,7 +135,7 @@ public class InflaterInputStream extends FilterInputStream { /** * Reads up to {@code nbytes} of decompressed data and stores it in * {@code buffer} starting at {@code off}. - * + * * @param buffer * the buffer to write data to. * @param off @@ -157,7 +145,6 @@ public class InflaterInputStream extends FilterInputStream { * @return Number of uncompressed bytes read * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int off, int nbytes) throws IOException { @@ -197,7 +184,7 @@ public class InflaterInputStream extends FilterInputStream { if (len == -1) { throw new EOFException(); } - throw (IOException)(new IOException().initCause(e)); + throw (IOException) (new IOException().initCause(e)); } if (result > 0) { return result; @@ -208,20 +195,18 @@ public class InflaterInputStream extends FilterInputStream { return -1; } else if (len == -1) { throw new EOFException(); - // If result == 0, fill() and try again + // If result == 0, fill() and try again } } while (true); } throw new ArrayIndexOutOfBoundsException(); } - /** * Fills the input buffer with data to be decompressed. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ protected void fill() throws IOException { if (closed) { @@ -246,17 +231,21 @@ public class InflaterInputStream extends FilterInputStream { /** * Skips up to n bytes of uncompressed data. - * + * * @param nbytes * the number of bytes to skip. * @return the number of uncompressed bytes skipped. * @throws IOException * if an error occurs skipping. - * @since Android 1.0 */ @Override public long skip(long nbytes) throws IOException { if (nbytes >= 0) { + // BEGIN android-changed + if (buf == null) { + buf = new byte[BUF_SIZE]; + } + // END android-changed long count = 0, rem = 0; while (count < nbytes) { int x = read(buf, 0, @@ -275,11 +264,10 @@ public class InflaterInputStream extends FilterInputStream { /** * Returns whether data can be read from this stream. - * + * * @return 0 if this stream has been closed, 1 otherwise. * @throws IOException * If an error occurs. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -295,10 +283,9 @@ public class InflaterInputStream extends FilterInputStream { /** * Closes the input stream. - * + * * @throws IOException * If an error occurs closing the input stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -309,17 +296,15 @@ public class InflaterInputStream extends FilterInputStream { super.close(); } } - + /** - * This implementation overrides the super type implementation to do nothing - * at all. - * + * Marks the current position in the stream. This implementation overrides + * the super type implementation to do nothing at all. + * * @param readlimit * of no use. - * @since Android 1.0 */ @Override - @SuppressWarnings("unused") public void mark(int readlimit) { // do nothing } @@ -328,22 +313,20 @@ public class InflaterInputStream extends FilterInputStream { * Reset the position of the stream to the last marked position. This * implementation overrides the supertype implementation and always throws * an {@link IOException IOException} when called. - * + * * @throws IOException * if the method is called - * @since Android 1.0 */ @Override - public void reset() throws IOException{ + public void reset() throws IOException { throw new IOException(); } - + /** * Returns whether the receiver implements {@code mark} semantics. This type * does not support {@code mark()}, so always responds {@code false}. - * + * * @return false, always - * @since Android 1.0 */ @Override public boolean markSupported() { diff --git a/archive/src/main/java/java/util/zip/ZipConstants.java b/archive/src/main/java/java/util/zip/ZipConstants.java index d804b0e..d00adc9 100644 --- a/archive/src/main/java/java/util/zip/ZipConstants.java +++ b/archive/src/main/java/java/util/zip/ZipConstants.java @@ -17,7 +17,6 @@ package java.util.zip; - interface ZipConstants { public static final long LOCSIG = 0x4034b50, EXTSIG = 0x8074b50, diff --git a/archive/src/main/java/java/util/zip/ZipEntry.java b/archive/src/main/java/java/util/zip/ZipEntry.java index 2cc7a9c..9774b6a 100644 --- a/archive/src/main/java/java/util/zip/ZipEntry.java +++ b/archive/src/main/java/java/util/zip/ZipEntry.java @@ -36,10 +36,9 @@ import java.util.GregorianCalendar; * itself. For example when reading a <i>ZIP-file</i> you will first retrieve * all its entries in a collection and then read the data for a specific entry * through an input stream. - * + * * @see ZipFile * @see ZipOutputStream - * @since Android 1.0 */ public class ZipEntry implements ZipConstants, Cloneable { String name, comment; @@ -94,26 +93,21 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Zip entry state: Deflated. - * - * @since Android 1.0 */ public static final int DEFLATED = 8; /** * Zip entry state: Stored. - * - * @since Android 1.0 */ public static final int STORED = 0; /** * Constructs a new {@code ZipEntry} with the specified name. - * + * * @param name * the name of the ZIP entry. * @throws IllegalArgumentException * if the name length is outside the range (> 0xFFFF). - * @since Android 1.0 */ public ZipEntry(String name) { if (name == null) { @@ -147,11 +141,10 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the comment for this {@code ZipEntry}. - * + * * @return the comment for this {@code ZipEntry}, or {@code null} if there * is no comment. If we're reading an archive with * {@code ZipInputStream} the comment is not available. - * @since Android 1.0 */ public String getComment() { return comment; @@ -159,10 +152,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the compressed size of this {@code ZipEntry}. - * + * * @return the compressed size, or -1 if the compressed size has not been * set. - * @since Android 1.0 */ public long getCompressedSize() { return compressedSize; @@ -170,9 +162,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the checksum for this {@code ZipEntry}. - * + * * @return the checksum, or -1 if the checksum has not been set. - * @since Android 1.0 */ public long getCrc() { return crc; @@ -180,10 +171,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the extra information for this {@code ZipEntry}. - * + * * @return a byte array containing the extra information, or {@code null} if * there is none. - * @since Android 1.0 */ public byte[] getExtra() { return extra; @@ -191,10 +181,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the compression method for this {@code ZipEntry}. - * + * * @return the compression method, either {@code DEFLATED}, {@code STORED} * or -1 if the compression method has not been set. - * @since Android 1.0 */ public int getMethod() { return compressionMethod; @@ -202,9 +191,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the name of this {@code ZipEntry}. - * + * * @return the entry name. - * @since Android 1.0 */ public String getName() { return name; @@ -212,10 +200,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the uncompressed size of this {@code ZipEntry}. - * + * * @return the uncompressed size, or {@code -1} if the size has not been * set. - * @since Android 1.0 */ public long getSize() { return size; @@ -223,10 +210,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the last modification time of this {@code ZipEntry}. - * + * * @return the last modification time as the number of milliseconds since * Jan. 1, 1970. - * @since Android 1.0 */ public long getTime() { if (time != -1) { @@ -242,10 +228,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Determine whether or not this {@code ZipEntry} is a directory. - * + * * @return {@code true} when this {@code ZipEntry} is a directory, {@code * false} otherwise. - * @since Android 1.0 */ public boolean isDirectory() { return name.charAt(name.length() - 1) == '/'; @@ -253,10 +238,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the comment for this {@code ZipEntry}. - * + * * @param string * the comment for this entry. - * @since Android 1.0 */ public void setComment(String string) { if (string == null || string.length() <= 0xFFFF) { @@ -268,10 +252,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the compressed size for this {@code ZipEntry}. - * + * * @param value * the compressed size (in bytes). - * @since Android 1.0 */ public void setCompressedSize(long value) { compressedSize = value; @@ -279,12 +262,11 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the checksum for this {@code ZipEntry}. - * + * * @param value * the checksum for this entry. * @throws IllegalArgumentException * if {@code value} is < 0 or > 0xFFFFFFFFL. - * @since Android 1.0 */ public void setCrc(long value) { if (value >= 0 && value <= 0xFFFFFFFFL) { @@ -296,12 +278,11 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the extra information for this {@code ZipEntry}. - * + * * @param data * a byte array containing the extra information. * @throws IllegalArgumentException * when the length of data is greater than 0xFFFF bytes. - * @since Android 1.0 */ public void setExtra(byte[] data) { if (data == null || data.length <= 0xFFFF) { @@ -313,13 +294,12 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the compression method for this {@code ZipEntry}. - * + * * @param value * the compression method, either {@code DEFLATED} or {@code * STORED}. * @throws IllegalArgumentException * when value is not {@code DEFLATED} or {@code STORED}. - * @since Android 1.0 */ public void setMethod(int value) { if (value != STORED && value != DEFLATED) { @@ -330,12 +310,11 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the uncompressed size of this {@code ZipEntry}. - * + * * @param value * the uncompressed size for this entry. * @throws IllegalArgumentException * if {@code value} < 0 or {@code value} > 0xFFFFFFFFL. - * @since Android 1.0 */ public void setSize(long value) { if (value >= 0 && value <= 0xFFFFFFFFL) { @@ -347,11 +326,10 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the modification time of this {@code ZipEntry}. - * + * * @param value * the modification time as the number of milliseconds since Jan. * 1, 1970. - * @since Android 1.0 */ public void setTime(long value) { GregorianCalendar cal = new GregorianCalendar(); @@ -372,9 +350,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Returns the string representation of this {@code ZipEntry}. - * + * * @return the string representation of this {@code ZipEntry}. - * @since Android 1.0 */ @Override public String toString() { @@ -401,10 +378,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Constructs a new {@code ZipEntry} using the values obtained from {@code * ze}. - * + * * @param ze * the {@code ZipEntry} from which to obtain values. - * @since Android 1.0 */ public ZipEntry(ZipEntry ze) { name = ze.name; @@ -434,9 +410,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Returns a shallow copy of this entry. - * + * * @return a copy of this entry. - * @since Android 1.0 */ @Override public Object clone() { @@ -445,9 +420,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Returns the hash code for this {@code ZipEntry}. - * + * * @return the hash code of the entry. - * @since Android 1.0 */ @Override public int hashCode() { @@ -471,7 +445,7 @@ public class ZipEntry implements ZipConstants, Cloneable { * readIntLE, so we're going to read the entire header at once * and then parse the results out without using any function calls. * Uglier, but should be much faster. - * + * * Note that some lines look a bit different, because the corresponding * fields or locals are long and so we need to do & 0xffffffffl to avoid * problems induced by sign extension. @@ -549,7 +523,7 @@ public class ZipEntry implements ZipConstants, Cloneable { int count; int len = b.length; int off = 0; - + while (len > 0) { count = in.read(b, off, len); if (count <= 0) diff --git a/archive/src/main/java/java/util/zip/ZipException.java b/archive/src/main/java/java/util/zip/ZipException.java index 590117b..6dab26f 100644 --- a/archive/src/main/java/java/util/zip/ZipException.java +++ b/archive/src/main/java/java/util/zip/ZipException.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.IOException; /** @@ -26,7 +25,6 @@ import java.io.IOException; * * @see ZipFile * @see ZipInputStream - * @since Android 1.0 */ public class ZipException extends IOException { @@ -34,8 +32,6 @@ public class ZipException extends IOException { /** * Constructs a new {@code ZipException} instance. - * - * @since Android 1.0 */ public ZipException() { super(); @@ -44,10 +40,9 @@ public class ZipException extends IOException { /** * Constructs a new {@code ZipException} instance with the specified * message. - * + * * @param detailMessage * the detail message for the exception. - * @since Android 1.0 */ public ZipException(String detailMessage) { super(detailMessage); diff --git a/archive/src/main/java/java/util/zip/ZipFile.java b/archive/src/main/java/java/util/zip/ZipFile.java index f1415d9..653b2c9 100644 --- a/archive/src/main/java/java/util/zip/ZipFile.java +++ b/archive/src/main/java/java/util/zip/ZipFile.java @@ -37,17 +37,13 @@ import java.util.NoSuchElementException; * While {@code ZipInputStream} provides stream based read access to a * <i>ZIP-archive</i>, this class implements more efficient (file based) access * and makes use of the <i>central directory</i> within a <i>ZIP-archive</i>. - * </p> * <p> * Use {@code ZipOutputStream} if you want to create an archive. - * </p> * <p> * A temporary ZIP file can be marked for automatic deletion upon closing it. - * </p> - * + * * @see ZipEntry * @see ZipOutputStream - * @since Android 1.0 */ public class ZipFile implements ZipConstants { @@ -57,28 +53,23 @@ public class ZipFile implements ZipConstants { /** * Open zip file for read. - * - * @since Android 1.0 */ public static final int OPEN_READ = 1; /** * Delete zip file when closed. - * - * @since Android 1.0 */ public static final int OPEN_DELETE = 4; /** * Constructs a new {@code ZipFile} with the specified file. - * + * * @param file * the file to read from. * @throws ZipException * if a ZIP error occurs. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public ZipFile(File file) throws ZipException, IOException { this(file, OPEN_READ); @@ -88,22 +79,31 @@ public class ZipFile implements ZipConstants { * Opens a file as <i>ZIP-archive</i>. "mode" must be {@code OPEN_READ} or * {@code OPEN_DELETE} . The latter sets the "delete on exit" flag through a * file. - * + * * @param file * the ZIP file to read. * @param mode * the mode of the file open operation. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public ZipFile(File file, int mode) throws IOException { - if (mode == (OPEN_READ | OPEN_DELETE)) - fileToDeleteOnClose = file; // file.deleteOnExit(); - else if (mode != OPEN_READ) - throw new IllegalArgumentException("invalid mode"); - fileName = file.getPath(); + if (mode == OPEN_READ || mode == (OPEN_READ | OPEN_DELETE)) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(fileName); + } + if ((mode & OPEN_DELETE) != 0) { + if (security != null) { + security.checkDelete(fileName); + } + fileToDeleteOnClose = file; // file.deleteOnExit(); + } + } else { + throw new IllegalArgumentException(); + } + mRaf = new RandomAccessFile(fileName, "r"); mEntryList = new ArrayList<ZipEntry>(); @@ -124,12 +124,11 @@ public class ZipFile implements ZipConstants { /** * Opens a ZIP archived file. - * + * * @param name * the name of the ZIP file. * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ public ZipFile(String name) throws IOException { this(new File(name), OPEN_READ); @@ -142,10 +141,9 @@ public class ZipFile implements ZipConstants { /** * Closes this ZIP file. - * + * * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ public void close() throws IOException { RandomAccessFile raf = mRaf; @@ -171,9 +169,8 @@ public class ZipFile implements ZipConstants { /** * Returns an enumeration of the entries. The entries are listed in the * order in which they appear in the ZIP archive. - * + * * @return the enumeration of the entries. - * @since Android 1.0 */ public Enumeration<? extends ZipEntry> entries() { return new Enumeration<ZipEntry>() { @@ -195,12 +192,11 @@ public class ZipFile implements ZipConstants { /** * Gets the ZIP entry with the specified name from this {@code ZipFile}. - * + * * @param entryName * the name of the entry in the ZIP file. * @return a {@code ZipEntry} or {@code null} if the entry name does not * exist in the ZIP file. - * @since Android 1.0 */ public ZipEntry getEntry(String entryName) { if (entryName != null) { @@ -213,13 +209,12 @@ public class ZipFile implements ZipConstants { /** * Returns an input stream on the data of the specified {@code ZipEntry}. - * + * * @param entry * the ZipEntry. * @return an input stream of the data contained in the {@code ZipEntry}. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public InputStream getInputStream(ZipEntry entry) throws IOException { /* @@ -259,9 +254,8 @@ public class ZipFile implements ZipConstants { /** * Gets the file name of this {@code ZipFile}. - * + * * @return the file name of this {@code ZipFile}. - * @since Android 1.0 */ public String getName() { return fileName; @@ -269,9 +263,8 @@ public class ZipFile implements ZipConstants { /** * Returns the number of {@code ZipEntries} in this {@code ZipFile}. - * + * * @return the number of entries in this file. - * @since Android 1.0 */ public int size() { return mEntryList.size(); diff --git a/archive/src/main/java/java/util/zip/ZipInputStream.java b/archive/src/main/java/java/util/zip/ZipInputStream.java index 262fa3f..1a35b1c 100644 --- a/archive/src/main/java/java/util/zip/ZipInputStream.java +++ b/archive/src/main/java/java/util/zip/ZipInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -36,18 +35,14 @@ import org.apache.harmony.luni.util.Util; * the so called ZIP entries. Therefore when reading from a {@code * ZipInputStream} first the entry's attributes will be retrieved with {@code * getNextEntry} before its data is read. - * </p> * <p> * While {@code InflaterInputStream} can read a compressed <i>ZIP-archive</i> * entry, this extension can read uncompressed entries as well. - * </p> * <p> * Use {@code ZipFile} if you can access the archive as a file directly. - * </p> - * + * * @see ZipEntry * @see ZipFile - * @since Android 1.0 */ public class ZipInputStream extends InflaterInputStream implements ZipConstants { static final int DEFLATED = 8; @@ -58,7 +53,7 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants static final int ZIPLocalHeaderVersionNeeded = 20; - // BEGI android-removed + // BEGIN android-removed // private boolean zipClosed = false; // END android-removed @@ -82,10 +77,9 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Constructs a new {@code ZipInputStream} from the specified input stream. - * + * * @param stream * the input stream to representing a ZIP archive. - * @since Android 1.0 */ public ZipInputStream(InputStream stream) { super(new PushbackInputStream(stream, BUF_SIZE), new Inflater(true)); @@ -96,10 +90,9 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Closes this {@code ZipInputStream}. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -113,10 +106,9 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Closes the current ZIP entry and positions to read the next entry. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public void closeEntry() throws IOException { // BEGIN android-changed @@ -173,13 +165,12 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Reads the next entry from this {@code ZipInputStream}. - * + * * @return the next {@code ZipEntry} contained in the input stream. * @throws IOException * if the stream is not positioned at the beginning of an entry * or if an other {@code IOException} occurs. * @see ZipEntry - * @since Android 1.0 */ public ZipEntry getNextEntry() throws IOException { if (currentEntry != null) { @@ -274,6 +265,18 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /* Read 4 bytes from the buffer and store it as an int */ + /** + * Reads up to the specified number of uncompressed bytes into the buffer + * starting at the offset. + * + * @param buffer + * a byte array + * @param start + * the starting offset into the buffer + * @param length + * the number of bytes to read + * @return the number of bytes read + */ @Override public int read(byte[] buffer, int start, int length) throws IOException { // BEGIN android-changed @@ -340,13 +343,12 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Skips up to the specified number of bytes in the current ZIP entry. - * + * * @param value * the number of bytes to skip. * @return the number of bytes skipped. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public long skip(long value) throws IOException { @@ -364,15 +366,14 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants return skipped; } throw new IllegalArgumentException(); -} + } /** * Returns 0 if the {@code EOF} has been reached, otherwise returns 1. - * + * * @return 0 after {@code EOF} of current entry, 1 otherwise. * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -389,11 +390,10 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * creates a {@link ZipEntry } with the given name. - * + * * @param name * the name of the entry. * @return the created {@code ZipEntry}. - * @since Android 1.0 */ protected ZipEntry createZipEntry(String name) { return new ZipEntry(name); diff --git a/archive/src/main/java/java/util/zip/ZipOutputStream.java b/archive/src/main/java/java/util/zip/ZipOutputStream.java index 4ddf643..58e781f 100644 --- a/archive/src/main/java/java/util/zip/ZipOutputStream.java +++ b/archive/src/main/java/java/util/zip/ZipOutputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -32,33 +31,26 @@ import org.apache.harmony.archive.internal.nls.Messages; * {@code ZipOutputStream} is used to write {@code ZipEntries} to the underlying * stream. Output from {@code ZipOutputStream} conforms to the {@code ZipFile} * file format. - * </p> * <p> * While {@code DeflaterOutputStream} can write a compressed <i>ZIP-archive</i> * entry, this extension can write uncompressed entries as well. In this case * special rules apply, for this purpose refer to the <a * href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">file format * specification</a>. - * </p> - * + * * @see ZipEntry * @see ZipFile - * @since Android 1.0 */ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { /** * Indicates deflated entries. - * - * @since Android 1.0 */ public static final int DEFLATED = 8; /** * Indicates uncompressed entries. - * - * @since Android 1.0 */ public static final int STORED = 0; @@ -90,7 +82,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @param p1 * the {@code OutputStream} to write the data to. - * @since Android 1.0 */ public ZipOutputStream(OutputStream p1) { super(p1, new Deflater(Deflater.DEFAULT_COMPRESSION, true)); @@ -102,7 +93,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @throws IOException * If an error occurs closing the stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -119,7 +109,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @throws IOException * If an error occurs closing the entry. - * @since Android 1.0 */ public void closeEntry() throws IOException { if (cDir == null) { @@ -205,7 +194,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @throws IOException * if an error occurs while terminating the stream. - * @since Android 1.0 */ @Override public void finish() throws IOException { @@ -253,7 +241,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * @throws IOException * If an error occurs storing the entry. * @see #write - * @since Android 1.0 */ public void putNextEntry(ZipEntry ze) throws java.io.IOException { if (currentEntry != null) { @@ -286,7 +273,8 @@ public class ZipOutputStream extends DeflaterOutputStream implements nameLength = utf8Count(ze.name); if (nameLength > 0xffff) { /* [MSG "archive.2A", "Name too long: {0}"] */ - throw new IllegalArgumentException(Messages.getString("archive.2A", ze.name)); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString( + "archive.2A", ze.name)); //$NON-NLS-1$ } def.setLevel(compressLevel); @@ -338,7 +326,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @param comment * the comment associated with the file. - * @since Android 1.0 */ public void setComment(String comment) { if (comment.length() > 0xFFFF) { @@ -355,7 +342,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * @param level * the compression level (ranging from -1 to 8). * @see Deflater - * @since Android 1.0 */ public void setLevel(int level) { if (level < Deflater.DEFAULT_COMPRESSION @@ -372,7 +358,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @param method * the compression method to use. - * @since Android 1.0 */ public void setMethod(int method) { if (method != STORED && method != DEFLATED) { @@ -398,11 +383,17 @@ public class ZipOutputStream extends DeflaterOutputStream implements } + /** + * Writes data for the current entry to the underlying stream. + * + * @exception IOException + * If an error occurs writing to the stream + */ @Override public void write(byte[] buffer, int off, int nbytes) throws java.io.IOException { // avoid int overflow, check null buf - if ((off > buffer.length) || (nbytes < 0) || (off < 0) + if ((off < 0 || (nbytes < 0) || off > buffer.length) || (buffer.length - off < nbytes)) { throw new IndexOutOfBoundsException(); } diff --git a/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java b/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java index 764f34d..3ba50fa 100644 --- a/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java +++ b/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java @@ -27,7 +27,6 @@ package org.apache.harmony.archive.internal.nls; - import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Locale; @@ -50,7 +49,7 @@ import org.apache.harmony.luni.util.MsgHelp; * is looked up, or resource bundle support is not available, the key itself * will be returned as the associated message. This means that the <em>KEY</em> * should a reasonable human-readable (english) string. - * + * */ public class Messages { @@ -61,7 +60,7 @@ public class Messages { /** * Retrieves a message which has no arguments. - * + * * @param msg * String the key to look up. * @return String the message for that key in the system message bundle. @@ -74,7 +73,7 @@ public class Messages { /** * Retrieves a message which takes 1 argument. - * + * * @param msg * String the key to look up. * @param arg @@ -87,7 +86,7 @@ public class Messages { /** * Retrieves a message which takes 1 integer argument. - * + * * @param msg * String the key to look up. * @param arg @@ -100,7 +99,7 @@ public class Messages { /** * Retrieves a message which takes 1 character argument. - * + * * @param msg * String the key to look up. * @param arg @@ -113,7 +112,7 @@ public class Messages { /** * Retrieves a message which takes 2 arguments. - * + * * @param msg * String the key to look up. * @param arg1 @@ -128,7 +127,7 @@ public class Messages { /** * Retrieves a message which takes several arguments. - * + * * @param msg * String the key to look up. * @param args diff --git a/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties b/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties index 1ae5c5e..e909af0 100644 --- a/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties +++ b/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties @@ -60,8 +60,11 @@ archive.29=Entry already exists: {0} archive.2A=Name too long: {0} archive.2B=String is too long archive.2C=No active entry -archive.2D=Missing version string\: {0} -archive.2E=Line too long -archive.2F=Invalid attribute {0} -archive.30={0} failed verification of {1} -archive.31={0} has invalid digest for {1} in {2} +archive.2D=Missing version attribute\: {0} +archive.2E=Manifest is too long +archive.2F=NUL character in a manifest +archive.30=Invalid attribute {0} +archive.31={0} failed verification of {1} +archive.32={0} has invalid digest for {1} in {2} +archive.33=A length of the encoded header name "{1}" exceeded maximum length {2} +archive.34=A jar verifier does not support more than one entry with the same name diff --git a/archive/src/main/java/org/apache/harmony/archive/util/Util.java b/archive/src/main/java/org/apache/harmony/archive/util/Util.java index bed3e91..b15108a 100644 --- a/archive/src/main/java/org/apache/harmony/archive/util/Util.java +++ b/archive/src/main/java/org/apache/harmony/archive/util/Util.java @@ -30,39 +30,49 @@ public class Util { return false; } - s1 = s1.substring(start1, start1 + length); - s2 = s2.substring(start2, start2 + length); - - return toASCIILowerCase(s1).equals(toASCIILowerCase(s2)); + char c1, c2; + for (int i = 0; i < length; i++) { + if ((c1 = s1.charAt(start1++)) != (c2 = s2.charAt(start2++)) + && toASCIIUpperCase(c1) != toASCIIUpperCase(c2)) { + return false; + } + } + return true; } throw new NullPointerException(); } - public static String toASCIILowerCase(String s) { - int len = s.length(); - StringBuilder buffer = new StringBuilder(len); - for (int i = 0; i < len; i++) { - char c = s.charAt(i); - if ('A' <= c && c <= 'Z') { - buffer.append((char) (c + ('a' - 'A'))); - } else { - buffer.append(c); + public static final boolean equalsIgnoreCase(byte[] buf1, byte[] buf2) { + if (buf1 == buf2) { + return true; + } + + if (buf1 == null || buf2 == null || buf1.length != buf2.length) { + return false; + } + + byte b1, b2; + + for (int i = 0; i < buf1.length; i++) { + if ((b1 = buf1[i]) != (b2 = buf2[i]) + && toASCIIUpperCase(b1) != toASCIIUpperCase(b2)) { + return false; } } - return buffer.toString(); + return true; } - public static String toASCIIUpperCase(String s) { - int len = s.length(); - StringBuilder buffer = new StringBuilder(len); - for (int i = 0; i < len; i++) { - char c = s.charAt(i); - if ('a' <= c && c <= 'z') { - buffer.append((char) (c - ('a' - 'A'))); - } else { - buffer.append(c); - } + static final char toASCIIUpperCase(char c) { + if ('a' <= c && c <= 'z') { + return (char) (c - ('a' - 'A')); + } + return c; + } + + static final byte toASCIIUpperCase(byte b) { + if ('a' <= b && b <= 'z') { + return (byte) (b - ('a' - 'A')); } - return buffer.toString(); + return b; } } diff --git a/archive/src/main/native/java_util_zip_Adler32.c b/archive/src/main/native/java_util_zip_Adler32.c index a7a182a..1b02a11 100644 --- a/archive/src/main/native/java_util_zip_Adler32.c +++ b/archive/src/main/native/java_util_zip_Adler32.c @@ -15,23 +15,25 @@ * limitations under the License. */ +#include "jni.h" #include "hy2sie.h" - #include "zlib.h" - +#include "sieb.h" JNIEXPORT jlong JNICALL Java_java_util_zip_Adler32_updateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong crc) { - PORT_ACCESS_FROM_ENV (env); - jbyte *b; jboolean isCopy; jlong result; b = (*env)->GetPrimitiveArrayCritical (env, buf, &isCopy); + if (b == NULL) { + throwNewOutOfMemoryError(env, ""); + return 0; + } result = (jlong) adler32 ((uLong) crc, (Bytef *) (b + off), (uInt) len); (*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT); @@ -42,9 +44,8 @@ JNIEXPORT jlong JNICALL Java_java_util_zip_Adler32_updateByteImpl (JNIEnv * env, jobject recv, jint val, jlong crc) { - PORT_ACCESS_FROM_ENV (env); - - return adler32 ((uLong) crc, (Bytef *) (&val), 1); + Bytef bytefVal = val; + return adler32 ((uLong) crc, (Bytef *) (&bytefVal), 1); } diff --git a/archive/src/main/native/java_util_zip_CRC32.c b/archive/src/main/native/java_util_zip_CRC32.c index 0688868..cee25e5 100644 --- a/archive/src/main/native/java_util_zip_CRC32.c +++ b/archive/src/main/native/java_util_zip_CRC32.c @@ -16,6 +16,7 @@ */ #include "hy2sie.h" +#include "sieb.h" #include "zlib.h" @@ -28,8 +29,10 @@ Java_java_util_zip_CRC32_updateImpl (JNIEnv * env, jobject recv, jlong result; b = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (b == NULL) + if (b == NULL) { + throwNewOutOfMemoryError(env, ""); return -1; + } result = crc32 ((uLong) crc, (Bytef *) (b + off), (uInt) len); ((*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT)); return result; diff --git a/archive/src/main/native/java_util_zip_Deflater.c b/archive/src/main/native/java_util_zip_Deflater.c index c8bd199..2e0e268 100644 --- a/archive/src/main/native/java_util_zip_Deflater.c +++ b/archive/src/main/native/java_util_zip_Deflater.c @@ -18,11 +18,13 @@ #include "hy2sie.h" #include "zlib.h" -#include "zipsup.h" - +#include "zip.h" +#include "jni.h" +#ifndef HY_ZIP_API void zfree PROTOTYPE ((void *opaque, void *address)); void *zalloc PROTOTYPE ((void *opaque, U_32 items, U_32 size)); +#endif static struct { @@ -52,10 +54,10 @@ Java_java_util_zip_Deflater_setDictionaryImpl (JNIEnv * env, jobject recv, if (err != Z_OK) { jclmem_free_memory (env, dBytes); - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return; } - stream->dict = dBytes; + stream->dict = (U_8*) dBytes; } JNIEXPORT jlong JNICALL @@ -94,9 +96,8 @@ JNIEXPORT jlong JNICALL Java_java_util_zip_Deflater_createStream (JNIEnv * env, jobject recv, jint level, jint strategy, jboolean noHeader) -{ +{ PORT_ACCESS_FROM_ENV (env); - JCLZipStream *jstream; z_stream *stream; int err = 0; @@ -109,7 +110,12 @@ Java_java_util_zip_Deflater_createStream (JNIEnv * env, jobject recv, // results in 2 x 128K being allocated per Deflater, which is // not acceptable. // END android-changed - +#ifdef HY_ZIP_API + VMI_ACCESS_FROM_ENV (env); + VMIZipFunctionTable *zipFuncs; + zipFuncs = (*VMI)->GetZipFunctions(VMI); +#endif + /*Allocate mem for wrapped struct */ jstream = jclmem_allocate_memory (env, sizeof (JCLZipStream)); if (jstream == NULL) @@ -141,11 +147,10 @@ Java_java_util_zip_Deflater_createStream (JNIEnv * env, jobject recv, mlevel, /*Memory allocation for internal compression state. 9 uses the most. */ // END android-changed strategy); - if (err != Z_OK) - { - throwNewIllegalArgumentException (env, ""); - return -1; - } + if (err != Z_OK) { + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); + return -1; + } return (jlong) ((IDATA) jstream); } @@ -170,8 +175,10 @@ Java_java_util_zip_Deflater_setInputImpl (JNIEnv * env, jobject recv, return; } in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (in == NULL) + if (in == NULL) { + throwNewOutOfMemoryError(env, ""); return; + } memcpy (stream->inaddr, (in + off), len); ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT)); stream->stream->next_in = (Bytef *) stream->inaddr; @@ -185,8 +192,6 @@ Java_java_util_zip_Deflater_deflateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong handle, int flushParm) { - PORT_ACCESS_FROM_ENV (env); - jbyte *out; JCLZipStream *stream; jint err = 0; @@ -203,29 +208,34 @@ Java_java_util_zip_Deflater_deflateImpl (JNIEnv * env, jobject recv, sin = stream->stream->total_in; sout = stream->stream->total_out; out = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (out == NULL) + if (out == NULL) { + throwNewOutOfMemoryError(env, ""); return -1; + } stream->stream->next_out = (Bytef *) out + off; err = deflate (stream->stream, flushParm); ((*env)->ReleasePrimitiveArrayCritical (env, buf, out, 0)); - if (err != Z_OK) - { - if (err == Z_STREAM_END) - { - ((*env)-> - SetBooleanField (env, recv, - gCachedFields.finished, - JNI_TRUE)); - return stream->stream->total_out - sout; - } + if (err != Z_OK) { + if (err == Z_MEM_ERROR) { + throwNewOutOfMemoryError(env, ""); + return 0; } + if (err == Z_STREAM_END) + { + ((*env)-> + SetBooleanField (env, recv, + gCachedFields.finished, + JNI_TRUE)); + return stream->stream->total_out - sout; + } + } if (flushParm != Z_FINISH) { /* Need to update the number of input bytes read. */ ((*env)-> SetIntField (env, recv, - gCachedFields.inRead, - (jint) stream->stream->total_in - sin + inBytes)); + gCachedFields.inRead, + (jint) stream->stream->total_in - sin + inBytes)); } return stream->stream->total_out - sout; } @@ -262,8 +272,6 @@ Java_java_util_zip_Deflater_setLevelsImpl (JNIEnv * env, jobject recv, int level, int strategy, jlong handle) { - PORT_ACCESS_FROM_ENV (env); - JCLZipStream *stream; jbyte b = 0; int err = 0; @@ -276,8 +284,9 @@ Java_java_util_zip_Deflater_setLevelsImpl (JNIEnv * env, jobject recv, stream = (JCLZipStream *) ((IDATA) handle); stream->stream->next_out = (Bytef *) & b; err = deflateParams (stream->stream, level, strategy); - if (err != Z_OK) - throwNewIllegalStateException (env, ""); + if (err != Z_OK) { + THROW_ZIP_EXCEPTION(env, err, IllegalStateException); + } } JNIEXPORT void JNICALL diff --git a/archive/src/main/native/java_util_zip_Inflater.c b/archive/src/main/native/java_util_zip_Inflater.c index d3a7d7c..4b30d4e 100644 --- a/archive/src/main/native/java_util_zip_Inflater.c +++ b/archive/src/main/native/java_util_zip_Inflater.c @@ -16,6 +16,7 @@ */ #include "hy2sie.h" +#include "zip.h" #include "zlib.h" #include <memory.h> @@ -24,6 +25,7 @@ #include <fcntl.h> +void throwNewDataFormatException (JNIEnv * env, const char *message); void zfree PROTOTYPE ((void *opaque, void *address)); void *zalloc PROTOTYPE ((void *opaque, U_32 items, U_32 size)); @@ -36,27 +38,6 @@ static struct { } gCachedFields; -// Contents from Harmony's inflater.h was put here: -// -typedef struct JCLZipStream -{ - U_8 *inaddr; - int inCap; - U_8 *dict; - z_stream *stream; -} JCLZipStream; - - - -/** - * Throw java.util.zip.DataFormatException - */ -void -throwNewDataFormatException (JNIEnv * env, const char *message) -{ - jniThrowException(env, "java/util/zip/DataFormatException", message); -} - /* Create a new stream . This stream cannot be used until it has been properly initialized. */ JNIEXPORT jlong JNICALL @@ -69,6 +50,11 @@ Java_java_util_zip_Inflater_createStream (JNIEnv * env, jobject recv, z_stream *stream; int err = 0; int wbits = 15; /*Use MAX for fastest */ +#ifdef HY_ZIP_API + VMI_ACCESS_FROM_ENV (env); + VMIZipFunctionTable *zipFuncs; + zipFuncs = (*VMI)->GetZipFunctions(VMI); +#endif /*Allocate mem for wrapped struct */ jstream = jclmem_allocate_memory (env, sizeof (JCLZipStream)); @@ -104,7 +90,7 @@ Java_java_util_zip_Inflater_createStream (JNIEnv * env, jobject recv, { jclmem_free_memory (env, stream); jclmem_free_memory (env, jstream); - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return -1; } @@ -134,8 +120,10 @@ Java_java_util_zip_Inflater_setInputImpl (JNIEnv * env, jobject recv, stream->stream->next_in = (Bytef *) baseAddr; stream->stream->avail_in = len; in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (in == NULL) + if (in == NULL) { + throwNewOutOfMemoryError(env, ""); return; + } memcpy (baseAddr, (in + off), len); ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT)); return; @@ -176,8 +164,6 @@ Java_java_util_zip_Inflater_inflateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong handle) { - PORT_ACCESS_FROM_ENV (env); - jbyte *out; JCLZipStream *stream = (JCLZipStream *) ((IDATA) handle); jint err = 0; @@ -192,9 +178,10 @@ Java_java_util_zip_Inflater_inflateImpl (JNIEnv * env, jobject recv, sin = stream->stream->total_in; sout = stream->stream->total_out; out = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - - if (out == NULL) + if (out == NULL) { + throwNewOutOfMemoryError(env, ""); return -1; + } stream->stream->next_out = (Bytef *) out + off; err = inflate (stream->stream, Z_SYNC_FLUSH); ((*env)->ReleasePrimitiveArrayCritical (env, buf, out, 0)); @@ -217,7 +204,7 @@ Java_java_util_zip_Inflater_inflateImpl (JNIEnv * env, jobject recv, } else { - throwNewDataFormatException (env, ""); + THROW_ZIP_EXCEPTION(env, err, DataFormatException); return -1; } } @@ -280,7 +267,7 @@ Java_java_util_zip_Inflater_setDictionaryImpl (JNIEnv * env, jobject recv, if (err != Z_OK) { jclmem_free_memory (env, dBytes); - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return; } stream->dict = dBytes; @@ -297,11 +284,19 @@ Java_java_util_zip_Inflater_resetImpl (JNIEnv * env, jobject recv, err = inflateReset (stream->stream); if (err != Z_OK) { - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return; } } +/** + * Throw java.util.zip.DataFormatException + */ +void +throwNewDataFormatException (JNIEnv * env, const char *message) +{ + jniThrowException(env, "java/util/zip/DataFormatException", message); +} JNIEXPORT jlong JNICALL Java_java_util_zip_Inflater_getTotalOutImpl (JNIEnv * env, jobject recv, @@ -311,6 +306,7 @@ Java_java_util_zip_Inflater_getTotalOutImpl (JNIEnv * env, jobject recv, stream = (JCLZipStream *) ((IDATA) handle); return stream->stream->total_out; + } JNIEXPORT jlong JNICALL diff --git a/archive/src/main/native/sieb.c b/archive/src/main/native/sieb.c index ab9430b..6881cf6 100644 --- a/archive/src/main/native/sieb.c +++ b/archive/src/main/native/sieb.c @@ -10,20 +10,6 @@ void throwNewOutOfMemoryError (JNIEnv * env, const char *message) jniThrowException(env, "java/lang/OutOfMemoryError", message); } -// Throw java.lang.IllegalStateException -void throwNewIllegalStateException (JNIEnv * env, const char *message) -{ - jniThrowException(env, "java/lang/IllegalStateException", message); -} - -// Throw java.lang.IllegalArgumentException -void throwNewIllegalArgumentException (JNIEnv * env, const char *message) -{ - jniThrowException(env, "java/lang/IllegalArgumentException", message); -} - - - void * sieb_malloc (JNIEnv * env, size_t byteCnt) { void * adr = malloc(byteCnt); if (adr == 0) { diff --git a/archive/src/main/native/sieb.h b/archive/src/main/native/sieb.h index 536c806..541ad90 100644 --- a/archive/src/main/native/sieb.h +++ b/archive/src/main/native/sieb.h @@ -7,9 +7,8 @@ -void throwNewOutOfMemoryError (JNIEnv * env, const char *message); -void throwNewIllegalArgumentException (JNIEnv * env, const char *message); -void throwNewIllegalStateException (JNIEnv * env, const char *message); +void throwNewOutOfMemoryError (JNIEnv * env, + const char *message); void * sieb_malloc (JNIEnv * env, size_t byteCnt); diff --git a/archive/src/main/native/sub.mk b/archive/src/main/native/sub.mk index 047c319..694c185 100644 --- a/archive/src/main/native/sub.mk +++ b/archive/src/main/native/sub.mk @@ -7,7 +7,8 @@ LOCAL_SRC_FILES := \ java_util_zip_CRC32.c \ java_util_zip_Deflater.c \ java_util_zip_Inflater.c \ - zipalloc.c \ + zip.c \ + zipalloc.c \ sieb.c LOCAL_C_INCLUDES += \ diff --git a/archive/src/main/native/zip.c b/archive/src/main/native/zip.c new file mode 100644 index 0000000..3d15d2a --- /dev/null +++ b/archive/src/main/native/zip.c @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#include "zip.h" +#include "jni.h" + +/** + * Throw java.lang.IllegalStateException + */ +void +throwNewIllegalStateException (JNIEnv * env, const char *message) +{ + jniThrowException(env, "java/lang/IllegalStateException", message); +} + +/** + * Throw java.lang.IllegalArgumentException + */ +void +throwNewIllegalArgumentException (JNIEnv * env, const char *message) +{ + jniThrowException(env, "java/lang/IllegalArgumentException", message); +} diff --git a/archive/src/main/native/zip.h b/archive/src/main/native/zip.h new file mode 100644 index 0000000..1452073 --- /dev/null +++ b/archive/src/main/native/zip.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#if !defined(zip_h) +#define zip_h + +#ifndef HY_ZIP_API +#include "zipsup.h" +#else /* HY_ZIP_API */ +#include "vmizip.h" +#endif /* HY_ZIP_API */ + +#include "hymutex.h" + +typedef struct JCLZipFile +{ + struct JCLZipFile *last; + struct JCLZipFile *next; +#ifndef HY_ZIP_API + HyZipFile hyZipFile; +#else + VMIZipFile hyZipFile; +#endif +} JCLZipFile; + +/* Fake JCLZipFile entry. last, next must be in the same position as JCLZipFile */ +typedef struct JCLZipFileLink +{ + JCLZipFile *last; + JCLZipFile *next; + MUTEX mutex; +} JCLZipFileLink; + +// Contents from Harmony's inflater.h was put here: +// +typedef struct JCLZipStream +{ + U_8 *inaddr; + int inCap; + U_8 *dict; + z_stream *stream; +} JCLZipStream; + +#define THROW_ZIP_EXCEPTION(env, err, type) \ + if (err == Z_MEM_ERROR) { \ + throwNewOutOfMemoryError(env, ""); \ + } else { \ + throwNew##type(env, (const char*) zError(err)); \ + } + +void throwNewIllegalStateException PROTOTYPE((JNIEnv* env, + const char* message)); +void throwNewIllegalArgumentException PROTOTYPE((JNIEnv* env, + const char* message)); + +#endif /* zip_h */ diff --git a/archive/src/main/native/zipsup.c b/archive/src/main/native/zipsup.c index 1bbe51f..22ea7e9 100644 --- a/archive/src/main/native/zipsup.c +++ b/archive/src/main/native/zipsup.c @@ -1438,7 +1438,7 @@ retry: return ZIP_ERR_FILE_CORRUPT; /* should never happen! */ } result = zip_establishCache (portLib, zipFile); - if (result) + if (!result) { /* (silently start operating without a cache if we couldn't make a new one) */ } diff --git a/archive/src/main/native/zipsup.h b/archive/src/main/native/zipsup.h index adc086a..67a2eda 100644 --- a/archive/src/main/native/zipsup.h +++ b/archive/src/main/native/zipsup.h @@ -34,23 +34,17 @@ extern "C" #include "zlib.h" -// Contents from Harmony's inflater.h was put here: -// -typedef struct JCLZipStream -{ - U_8 *inaddr; - U_8 *dict; - z_stream *stream; -} JCLZipStream; - - typedef struct HyZipCachePool HyZipCachePool; HyZipCachePool * zipsup_GetZipCachePool(HyPortLibrary * portLib); +#if defined(HY_LOCAL_ZLIB) +#define HY_ZIP_DLL_NAME "z" +#else #define HY_ZIP_DLL_NAME "hyzlib" +#endif #define ZIP_INTERNAL_MAX 80 #define ZIP_CM_Reduced1 2 @@ -156,18 +150,6 @@ zipsup_GetZipCachePool(HyPortLibrary * portLib); -// Contents from Harmony's zip.h were put in java_util_zip_ZipFile.c -// and here: -typedef struct JCLZipFile -{ - struct JCLZipFile *last; - struct JCLZipFile *next; - HyZipFile hyZipFile; -} JCLZipFile; - - - - #include "hymutex.h" extern MUTEX zip_GlobalMutex; diff --git a/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java b/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java new file mode 100644 index 0000000..9c28dc2 --- /dev/null +++ b/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java @@ -0,0 +1,87 @@ +/* + * 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.harmony.archive.util; + +import junit.framework.TestCase; + +public class UtilTest extends TestCase { + private static final String ASCII_ALPHABET_LC = "abcdefghijklmnopqrstuvwxyz"; + private static final String ASCII_ALPHABET_UC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final byte[] ASCII_ALPHABET_LC_BYTES; + private static final byte[] ASCII_ALPHABET_UC_BYTES; + + static { + ASCII_ALPHABET_LC_BYTES = new byte[ASCII_ALPHABET_LC.length()]; + for (int i = 0; i < ASCII_ALPHABET_LC_BYTES.length; i++) { + final char c = ASCII_ALPHABET_LC.charAt(i); + final byte b = (byte) c; + assert ((char) b) == c; + ASCII_ALPHABET_LC_BYTES[i] = b; + } + + ASCII_ALPHABET_UC_BYTES = new byte[ASCII_ALPHABET_UC.length()]; + for (int i = 0; i < ASCII_ALPHABET_UC_BYTES.length; i++) { + final char c = ASCII_ALPHABET_UC.charAt(i); + final byte b = (byte) c; + assert ((char) b) == c; + ASCII_ALPHABET_UC_BYTES[i] = b; + } + } + + public void testASCIIIgnoreCaseRegionMatches() { + final String s1 = ASCII_ALPHABET_LC; + final String s2 = ASCII_ALPHABET_UC; + for (int i = 0; i < s1.length(); i++) { + assertTrue(Util.ASCIIIgnoreCaseRegionMatches(s1, i, s2, i, s1 + .length() + - i)); + } + } + + public void testToASCIIUpperCaseByte() { + for (int i = 0; i < ASCII_ALPHABET_LC_BYTES.length; i++) { + assertEquals(ASCII_ALPHABET_UC_BYTES[i], Util + .toASCIIUpperCase(ASCII_ALPHABET_LC_BYTES[i])); + } + for (int i = 0; i < ASCII_ALPHABET_UC_BYTES.length; i++) { + assertEquals(ASCII_ALPHABET_UC_BYTES[i], Util + .toASCIIUpperCase(ASCII_ALPHABET_UC_BYTES[i])); + } + } + + public void testToASCIIUpperCaseChar() { + for (int i = 0; i < ASCII_ALPHABET_LC.length(); i++) { + assertEquals(ASCII_ALPHABET_UC.charAt(i), Util + .toASCIIUpperCase(ASCII_ALPHABET_LC.charAt(i))); + } + for (int i = 0; i < ASCII_ALPHABET_UC.length(); i++) { + assertEquals(ASCII_ALPHABET_UC.charAt(i), Util + .toASCIIUpperCase(ASCII_ALPHABET_UC.charAt(i))); + } + } + + public void testEqualsIgnoreCaseByteArrayByteArray() { + assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_LC_BYTES, + ASCII_ALPHABET_LC_BYTES)); + assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_LC_BYTES, + ASCII_ALPHABET_UC_BYTES)); + assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_UC_BYTES, + ASCII_ALPHABET_UC_BYTES)); + } + +} diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java index 0a8b037..0b3d2cf 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java @@ -421,6 +421,27 @@ public class AttributesTest extends TestCase { assertNull(attribute.get(name)); } + /** + * @tests java.util.jar.Attributes.hashCode() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "hashCode", + args = {} + ) + public void test_hashCode_consistent_with_map() { + MockAttributes mockAttr = new MockAttributes(); + mockAttr.putValue("1", "one"); + assertEquals(mockAttr.getMap().hashCode(), mockAttr.hashCode()); + } + + private static class MockAttributes extends Attributes { + public Map<Object, Object> getMap() { + return map; + } + } + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", @@ -470,7 +491,7 @@ public class AttributesTest extends TestCase { } @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "hashCode", args = {} diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java index 40eff3b..90144be 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java @@ -72,6 +72,29 @@ public class JarEntryTest extends TestCase { } /** + * @throws IOException + * @tests java.util.jar.JarEntry#JarEntry(java.util.jar.JarEntry) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "JarEntry", + args = {java.util.jar.JarEntry.class} + ) + public void test_ConstructorLjava_util_jar_JarEntry_on_null() throws IOException { + JarEntry newJarEntry = new JarEntry(jarFile.getJarEntry(entryName)); + assertNotNull(newJarEntry); + + jarEntry = null; + try { + newJarEntry = new JarEntry(jarEntry); + fail("Should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + + /** * @tests java.util.jar.JarEntry#JarEntry(java.util.zip.ZipEntry) */ @TestTargetNew( @@ -163,10 +186,21 @@ public class JarEntryTest extends TestCase { JarEntry jarEntry2 = jarFile.getJarEntry("Test.class"); InputStream in = jarFile.getInputStream(jarEntry1); byte[] buffer = new byte[1024]; + // BEGIN android-changed + // the certificates are non-null too early and in.available() fails + // while (in.available() > 0) { + // assertNull("getCertificates() should be null until the entry is read", + // jarEntry1.getCertificates()); + // assertNull(jarEntry2.getCertificates()); + // in.read(buffer); + // } while (in.read(buffer) >= 0); in.close(); + // END android-changed + assertEquals("the file is fully read", -1, in.read()); assertNotNull(jarEntry1.getCertificates()); assertNotNull(jarEntry2.getCertificates()); + in.close(); } /** @@ -187,8 +221,14 @@ public class JarEntryTest extends TestCase { InputStream in = jarFile.getInputStream(jarEntry); byte[] buffer = new byte[1024]; while (in.available() > 0) { + // BEGIN android-changed + // the code signers are non-null too early + // assertNull("getCodeSigners() should be null until the entry is read", + // jarEntry.getCodeSigners()); + // END android-changed in.read(buffer); } + assertEquals("the file is fully read", -1, in.read()); CodeSigner[] codeSigners = jarEntry.getCodeSigners(); assertEquals(2, codeSigners.length); List<?> certs_bob = codeSigners[0].getSignerCertPath() @@ -240,7 +280,7 @@ public class JarEntryTest extends TestCase { } @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "JarEntry", args = {java.util.jar.JarEntry.class} diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java index 720f78d..96321a4 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java @@ -14,13 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.harmony.archive.tests.java.util.jar; import dalvik.annotation.AndroidOnly; import dalvik.annotation.TestTargetClass; -import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetNew; @@ -73,13 +71,17 @@ public class JarFileTest extends TestCase { private final String jarName3 = "hyts_manifest1.jar"; private final String jarName4 = "hyts_signed.jar"; - + private final String jarName5 = "hyts_signed_inc.jar"; + private final String integrateJar = "Integrate.jar"; + private final String entryName = "foo/bar/A.class"; private final String entryName3 = "coucou/FileAccess.class"; + private final String integrateJarEntry = "Test.class"; + private File resources; // custom security manager @@ -102,7 +104,7 @@ public class JarFileTest extends TestCase { * @tests java.util.jar.JarFile#JarFile(java.io.File) */ @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "JarFile", args = {java.io.File.class} @@ -300,6 +302,27 @@ public class JarFileTest extends TestCase { } /** + * Constructs JarFile object. + * + * @tests java.util.jar.JarFile#JarFile(java.io.File) + * @tests java.util.jar.JarFile#JarFile(java.lang.String) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "JarFile", + args = {java.io.File.class} + ) + public void testConstructor_file() throws IOException { + File f = new File(resources, jarName); + Support_Resources.copyFile(resources, null, jarName); + assertTrue(new JarFile(f).getEntry(entryName).getName().equals( + entryName)); + assertTrue(new JarFile(f.getPath()).getEntry(entryName).getName() + .equals(entryName)); + } + + /** * @tests java.util.jar.JarFile#entries() */ @TestTargetNew( @@ -316,11 +339,11 @@ public class JarFileTest extends TestCase { Support_Resources.copyFile(resources, null, jarName); JarFile jarFile = new JarFile(new File(resources, jarName)); Enumeration<JarEntry> e = jarFile.entries(); - int i = 0; - while (e.hasMoreElements()) { - i++; + int i; + for (i = 0; e.hasMoreElements(); i++) { e.nextElement(); } + assertEquals(jarFile.size(), i); jarFile.close(); assertEquals(6, i); } @@ -336,24 +359,20 @@ public class JarFileTest extends TestCase { JarFile jarFile = new JarFile(new File(resources, jarName)); Enumeration<JarEntry> enumeration = jarFile.entries(); jarFile.close(); - boolean pass = false; try { enumeration.hasMoreElements(); + fail("hasMoreElements() did not detect a closed jar file"); } catch (IllegalStateException e) { - pass = true; } - assertTrue("hasMoreElements did not detect closed jar file", pass); Support_Resources.copyFile(resources, null, jarName); jarFile = new JarFile(new File(resources, jarName)); enumeration = jarFile.entries(); jarFile.close(); - pass = false; try { enumeration.nextElement(); + fail("nextElement() did not detect closed jar file"); } catch (IllegalStateException e) { - pass = true; } - assertTrue("nextElement did not detect closed jar file", pass); } /** @@ -361,7 +380,7 @@ public class JarFileTest extends TestCase { * @tests java.util.jar.JarFile#getJarEntry(java.lang.String) */ @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "getEntry", args = {java.lang.String.class} @@ -442,6 +461,92 @@ public class JarFileTest extends TestCase { } } + + /** + * @tests java.util.jar.JarFile#getJarEntry(java.lang.String) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "getEntry", + args = {java.lang.String.class} + ) + public void testGetJarEntry() throws Exception { + Support_Resources.copyFile(resources, null, jarName); + JarFile jarFile = new JarFile(new File(resources, jarName)); + assertEquals("Error in returned entry", 311, jarFile.getEntry( + entryName).getSize()); + jarFile.close(); + + // tests for signed jars + // test all signed jars in the /Testres/Internal/SignedJars directory + String jarDirUrl = Support_Resources + .getResourceURL("/../internalres/signedjars"); + Vector<String> signedJars = new Vector<String>(); + try { + InputStream is = new URL(jarDirUrl + "/jarlist.txt").openStream(); + while (is.available() > 0) { + StringBuilder linebuff = new StringBuilder(80); // Typical line + // length + done: while (true) { + int nextByte = is.read(); + switch (nextByte) { + case -1: + break done; + case (byte) '\r': + if (linebuff.length() == 0) { + // ignore + } + break done; + case (byte) '\n': + if (linebuff.length() == 0) { + // ignore + } + break done; + default: + linebuff.append((char) nextByte); + } + } + if (linebuff.length() == 0) { + break; + } + String line = linebuff.toString(); + signedJars.add(line); + } + is.close(); + } catch (IOException e) { + // no list of jars found + } + + for (int i = 0; i < signedJars.size(); i++) { + String jarName = signedJars.get(i); + try { + File file = Support_Resources.getExternalLocalFile(jarDirUrl + + "/" + jarName); + jarFile = new JarFile(file, true); + boolean foundCerts = false; + Enumeration<JarEntry> e = jarFile.entries(); + while (e.hasMoreElements()) { + JarEntry entry = e.nextElement(); + InputStream is = jarFile.getInputStream(entry); + is.skip(100000); + is.close(); + Certificate[] certs = entry.getCertificates(); + if (certs != null && certs.length > 0) { + foundCerts = true; + break; + } + } + assertTrue( + "No certificates found during signed jar test for jar \"" + + jarName + "\"", foundCerts); + } catch (IOException e) { + fail("Exception during signed jar test for jar \"" + jarName + + "\": " + e.toString()); + } + } + } + /** * @tests java.util.jar.JarFile#getManifest() */ @@ -540,85 +645,6 @@ public class JarFileTest extends TestCase { } /** - * @throws IOException - * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getInputStream", - args = {java.util.zip.ZipEntry.class} - ) - public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException { - File localFile = null; - try { - Support_Resources.copyFile(resources, null, jarName); - localFile = new File(resources, jarName); - } catch (Exception e) { - fail("Failed to create local file: " + e); - } - - byte[] b = new byte[1024]; - try { - JarFile jf = new JarFile(localFile); - java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName)); - // BEGIN android-removed - // jf.close(); - // END android-removed - assertTrue("Returned invalid stream", is.available() > 0); - int r = is.read(b, 0, 1024); - is.close(); - StringBuffer sb = new StringBuffer(r); - for (int i = 0; i < r; i++) { - sb.append((char) (b[i] & 0xff)); - } - String contents = sb.toString(); - assertTrue("Incorrect stream read", contents.indexOf("bar") > 0); - // BEGIN android-added - jf.close(); - // END android-added - } catch (Exception e) { - fail("Exception during test: " + e.toString()); - } - - try { - JarFile jf = new JarFile(localFile); - InputStream in = jf.getInputStream(new JarEntry("invalid")); - assertNull("Got stream for non-existent entry", in); - } catch (Exception e) { - fail("Exception during test 2: " + e); - } - - try { - Support_Resources.copyFile(resources, null, jarName); - File signedFile = new File(resources, jarName); - JarFile jf = new JarFile(signedFile); - JarEntry jre = new JarEntry("foo/bar/A.class"); - jf.getInputStream(jre); - // InputStream returned in any way, exception can be thrown in case - // of reading from this stream only. - // fail("Should throw ZipException"); - } catch (ZipException ee) { - // expected - } - - try { - Support_Resources.copyFile(resources, null, jarName); - File signedFile = new File(resources, jarName); - JarFile jf = new JarFile(signedFile); - JarEntry jre = new JarEntry("foo/bar/A.class"); - jf.close(); - jf.getInputStream(jre); - // InputStream returned in any way, exception can be thrown in case - // of reading from this stream only. - // The same for IOException - fail("Should throw IllegalStateException"); - } catch (IllegalStateException ee) { - // expected - } - } - - /** * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) */ @TestTargetNew( @@ -660,7 +686,7 @@ public class JarFileTest extends TestCase { } catch (Exception e) { fail("Exception during test 4: " + e); } - + try { JarFile jar = new JarFile(signedFile); JarEntry entry = new JarEntry(entryName3); @@ -682,7 +708,7 @@ public class JarFileTest extends TestCase { } catch (Exception e) { fail("Failed to create local file 5: " + e); } - + try { JarFile jar = new JarFile(signedFile); JarEntry entry = new JarEntry(entryName3); @@ -732,7 +758,37 @@ public class JarFileTest extends TestCase { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { ZipEntry zipEntry = entries.nextElement(); - jarFile.getInputStream(zipEntry); + jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); + } + } + + /** + * The jar is intact, but the entry object is modified. + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "getInputStream", + args = {ZipEntry.class} + ) + public void testJarVerificationModifiedEntry() throws IOException { + Support_Resources.copyFile(resources, null, integrateJar); + File f = new File(resources, integrateJar); + + JarFile jarFile = new JarFile(f); + ZipEntry zipEntry = jarFile.getJarEntry(integrateJarEntry); + zipEntry.setSize(zipEntry.getSize() + 1); + jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); + + jarFile = new JarFile(f); + zipEntry = jarFile.getJarEntry(integrateJarEntry); + zipEntry.setSize(zipEntry.getSize() - 1); + try { + //jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); + jarFile.getInputStream(zipEntry).read(new byte[5000], 0, 5000); + fail("SecurityException expected"); + } catch (SecurityException e) { + // desired } } @@ -781,7 +837,6 @@ public class JarFileTest extends TestCase { Enumeration<JarEntry> entries = jarFile.entries(); int count = 0; while (entries.hasMoreElements()) { - ZipEntry zipEntry = entries.nextElement(); jarFile.getInputStream(zipEntry); count++; @@ -818,7 +873,7 @@ public class JarFileTest extends TestCase { while (in.available() > 0) { in.read(buffer); } - fail("should throw Security Exception"); + fail("SecurityException expected"); } catch (SecurityException e) { // desired } @@ -827,7 +882,7 @@ public class JarFileTest extends TestCase { /* * In the Modified.jar, the main attributes of META-INF/MANIFEST.MF is * tampered manually. Hence the RI 5.0 JarFile.getInputStream of any - * JarEntry will throw security exception, but the apache harmony will not. + * JarEntry will throw security exception. */ @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, @@ -846,7 +901,7 @@ public class JarFileTest extends TestCase { ZipEntry zipEntry = entries.nextElement(); try { jarFile.getInputStream(zipEntry); - fail("should throw Security Exception"); + fail("SecurityException expected"); } catch (SecurityException e) { // desired } @@ -927,4 +982,83 @@ public class JarFileTest extends TestCase { // Can not check IOException } + + /** + * @throws IOException + * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getInputStream", + args = {java.util.zip.ZipEntry.class} + ) + public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException { + File localFile = null; + try { + Support_Resources.copyFile(resources, null, jarName); + localFile = new File(resources, jarName); + } catch (Exception e) { + fail("Failed to create local file: " + e); + } + + byte[] b = new byte[1024]; + try { + JarFile jf = new JarFile(localFile); + java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName)); + // BEGIN android-removed + // jf.close(); + // END android-removed + assertTrue("Returned invalid stream", is.available() > 0); + int r = is.read(b, 0, 1024); + is.close(); + StringBuffer sb = new StringBuffer(r); + for (int i = 0; i < r; i++) { + sb.append((char) (b[i] & 0xff)); + } + String contents = sb.toString(); + assertTrue("Incorrect stream read", contents.indexOf("bar") > 0); + // BEGIN android-added + jf.close(); + // END android-added + } catch (Exception e) { + fail("Exception during test: " + e.toString()); + } + + try { + JarFile jf = new JarFile(localFile); + InputStream in = jf.getInputStream(new JarEntry("invalid")); + assertNull("Got stream for non-existent entry", in); + } catch (Exception e) { + fail("Exception during test 2: " + e); + } + + try { + Support_Resources.copyFile(resources, null, jarName); + File signedFile = new File(resources, jarName); + JarFile jf = new JarFile(signedFile); + JarEntry jre = new JarEntry("foo/bar/A.class"); + jf.getInputStream(jre); + // InputStream returned in any way, exception can be thrown in case + // of reading from this stream only. + // fail("Should throw ZipException"); + } catch (ZipException ee) { + // expected + } + + try { + Support_Resources.copyFile(resources, null, jarName); + File signedFile = new File(resources, jarName); + JarFile jf = new JarFile(signedFile); + JarEntry jre = new JarEntry("foo/bar/A.class"); + jf.close(); + jf.getInputStream(jre); + // InputStream returned in any way, exception can be thrown in case + // of reading from this stream only. + // The same for IOException + fail("Should throw IllegalStateException"); + } catch (IllegalStateException ee) { + // expected + } + } } diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java index e652137..acdad71 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java @@ -48,7 +48,7 @@ public class JarOutputStreamTest extends junit.framework.TestCase { method = "putNextEntry", args = {java.util.zip.ZipEntry.class} ) - public void test_putNextEntryLjava_util_zip_ZipEntry() { + public void test_putNextEntryLjava_util_zip_ZipEntry() throws Exception { // testClass file`s actual extension is .class, since having .class // extension files in source dir causes // problems on eclipse, the extension is changed into .ser or it can be @@ -76,35 +76,30 @@ public class JarOutputStreamTest extends junit.framework.TestCase { File outputJar = null; JarOutputStream jout = null; - try { - // open the output jarfile - outputJar = File.createTempFile("hyts_", ".jar"); - jout = new JarOutputStream(new FileOutputStream(outputJar), - newman); - jout.putNextEntry(new JarEntry(entryName)); - } catch (Exception e) { - fail("Error creating JarOutputStream: " + e); - } + // open the output jarfile + outputJar = File.createTempFile("hyts_", ".jar"); + jout = new JarOutputStream(new FileOutputStream(outputJar), + newman); + jout.putNextEntry(new JarEntry(entryName)); + File resources = Support_Resources.createTempFolder(); - try { - // read in the class file, and output it to the jar - Support_Resources.copyFile(resources, null, testClass); - URL jarURL = new URL((new File(resources, testClass)).toURL() - .toString()); - InputStream jis = jarURL.openStream(); - - byte[] bytes = new byte[1024]; - int len; - while ((len = jis.read(bytes)) != -1) { - jout.write(bytes, 0, len); - } - - jout.flush(); - jout.close(); - jis.close(); - } catch (Exception e) { - fail("Error writing JAR file for testing: " + e); + + // read in the class file, and output it to the jar + Support_Resources.copyFile(resources, null, testClass); + URL jarURL = new URL((new File(resources, testClass)).toURL() + .toString()); + InputStream jis = jarURL.openStream(); + + byte[] bytes = new byte[1024]; + int len; + while ((len = jis.read(bytes)) != -1) { + jout.write(bytes, 0, len); } + + jout.flush(); + jout.close(); + jis.close(); + String res = null; // set up the VM parameters String[] args = new String[2]; diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java index 57e4744..42b2543 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java @@ -14,12 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.harmony.archive.tests.java.util.jar; -import dalvik.annotation.TestTargetClass; -import dalvik.annotation.TestTargets; +import dalvik.annotation.KnownFailure; import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; import java.io.ByteArrayInputStream; @@ -28,14 +27,15 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; import java.net.URL; +import java.net.MalformedURLException; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; import junit.framework.TestCase; + import tests.support.resource.Support_Resources; @TestTargetClass(Manifest.class) @@ -49,6 +49,10 @@ public class ManifestTest extends TestCase { private Manifest m2; + private final String ATT_ENTRY_NAME = "HasAttributes.txt"; + + private final String MANIFEST_NAME = "manifest/hyts_MANIFEST.MF"; + private File resources; @Override @@ -68,6 +72,19 @@ public class ManifestTest extends TestCase { } } + private Manifest getManifest(String fileName) { + try { + Support_Resources.copyFile(resources, null, fileName); + JarFile jarFile = new JarFile(new File(resources, fileName)); + Manifest m = jarFile.getManifest(); + jarFile.close(); + return m; + } catch (Exception e) { + fail("Exception during setup: " + e.toString()); + return null; + } + } + /** * @tests java.util.jar.Manifest#Manifest() */ @@ -87,264 +104,136 @@ public class ManifestTest extends TestCase { } /** - * @tests java.util.jar.Manifest#Manifest(java.io.InputStream) - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "IOException checking missed.", - method = "Manifest", - args = {java.io.InputStream.class} - ) - public void test_ConstructorLjava_io_InputStream() { - // Test for method java.util.jar.Manifest(java.io.InputStream) - /* - * ByteArrayOutputStream baos = new ByteArrayOutputStream(); - * m2.write(baos); InputSteam is = new ByteArrayInputStream - * (baos.toByteArray()); Manifest myManifest = new Manifest (is); - * assertTrue("Manifests should be equal", myManifest.equals(m2)); - */ - - Manifest manifest = null; - InputStream is = null; - try { - is = new URL(Support_Resources.getURL("manifest/hyts_MANIFEST.MF")) - .openStream(); - } catch (MalformedURLException e1) { - fail("Failed to create InputStream object"); - } catch (IOException e1) { - fail("Failed to create InputStream object"); - } - try { - manifest = new Manifest(is); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } - Attributes main = manifest.getMainAttributes(); - assertEquals("Bundle-Name not correct", "ClientSupport", main - .getValue("Bundle-Name")); - assertEquals( - "Bundle-Description not correct", - - "Provides SessionService, AuthenticationService. Extends RegistryService.", - main.getValue("Bundle-Description")); - assertEquals("Bundle-Activator not correct", - "com.ibm.ive.eccomm.client.support.ClientSupportActivator", - main.getValue("Bundle-Activator")); - assertEquals( - "Import-Package not correct", - - "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client", - main.getValue("Import-Package")); - assertEquals( - "Import-Service not correct", - - "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService", - main.getValue("Import-Service")); - assertEquals( - "Export-Package not correct", - - "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0", - main.getValue("Export-Package")); - assertEquals( - "Export-Service not correct", - - "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService", - main.getValue("Export-Service")); - assertEquals("Bundle-Vendor not correct", "IBM", main - .getValue("Bundle-Vendor")); - assertEquals("Bundle-Version not correct", "1.2.0", main - .getValue("Bundle-Version")); - try { - is.close(); - } catch (IOException e1) { - fail("Failed to close InputStream object"); - } - try { - manifest = new Manifest(is); - fail("IOException expected"); - } catch (MalformedURLException e) { - fail("IOException expected"); - } catch (IOException e) { - // expected - } - } - - /** - * @tests java.util.jar.Manifest#clear() + * @tests java.util.jar.Manifest#Manifest(java.util.jar.Manifest) */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "clear", - args = {} + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "Manifest", + args = {java.util.jar.Manifest.class} ) - public void test_clear() { - // Test for method void java.util.jar.Manifest.clear() - m2.clear(); - assertTrue("Should have no entries", m2.getEntries().isEmpty()); - assertTrue("Should have no main attributes", m2.getMainAttributes() - .isEmpty()); + public void testCopyingConstructor() throws IOException { + Manifest firstManifest = new Manifest(new URL(Support_Resources + .getURL(MANIFEST_NAME)).openStream()); + Manifest secondManifest = new Manifest(firstManifest); + assertEquals(firstManifest, secondManifest); } /** - * @tests java.util.jar.Manifest#getAttributes(java.lang.String) + * @tests java.util.jar.Manifest#Manifest(Manifest) */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getAttributes", - args = {java.lang.String.class} + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "Manifest", + args = {java.util.jar.Manifest.class} ) - public void test_getAttributesLjava_lang_String() { - // Test for method java.util.jar.Attributes - // java.util.jar.Manifest.getAttributes(java.lang.String) - assertNull("Should not exist", m2.getAttributes("Doesn't Exist")); - assertEquals("Should exist", "OK", m2 - .getAttributes("HasAttributes.txt").get( - new Attributes.Name("MyAttribute"))); + public void test_ConstructorLjava_util_jar_Manifest() { + // Test for method java.util.jar.Manifest() + Manifest emptyManifest = new Manifest(); + Manifest emptyClone = new Manifest(emptyManifest); + assertTrue("Should have no entries", emptyClone.getEntries().isEmpty()); + assertTrue("Should have no main attributes", emptyClone + .getMainAttributes().isEmpty()); + assertEquals(emptyClone, emptyManifest); + assertEquals(emptyClone, emptyManifest.clone()); } - /** - * @tests java.util.jar.Manifest#getEntries() - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getEntries", - args = {} - ) - public void test_getEntries() { - // Test for method java.util.Map java.util.jar.Manifest.getEntries() - Map<String, Attributes> myMap = m2.getEntries(); - assertNull("Shouldn't exist", myMap.get("Doesn't exist")); - assertEquals("Should exist", "OK", myMap.get("HasAttributes.txt").get( - new Attributes.Name("MyAttribute"))); - + private void assertAttribute(Attributes attr, String name, String value) { + assertEquals("Incorrect " + name, value, attr.getValue(name)); } - /** - * @tests java.util.jar.Manifest#getMainAttributes() - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getMainAttributes", - args = {} - ) - public void test_getMainAttributes() { - // Test for method java.util.jar.Attributes - // java.util.jar.Manifest.getMainAttributes() - Attributes a = m.getMainAttributes(); - assertEquals("Manifest_Version should return 1.0", "1.0", a - .get(Attributes.Name.MANIFEST_VERSION)); + private void checkManifest(Manifest manifest) { + Attributes main = manifest.getMainAttributes(); + assertAttribute(main, "Bundle-Name", "ClientSupport"); + assertAttribute(main, "Bundle-Description", + "Provides SessionService, AuthenticationService. Extends RegistryService."); + assertAttribute(main, "Bundle-Activator", + "com.ibm.ive.eccomm.client.support.ClientSupportActivator"); + assertAttribute( + main, + "Import-Package", + "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client"); + assertAttribute( + main, + "Import-Service", + "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService"); + assertAttribute( + main, + "Export-Package", + "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0"); + assertAttribute( + main, + "Export-Service", + "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService"); + assertAttribute(main, "Bundle-Vendor", "IBM"); + assertAttribute(main, "Bundle-Version", "1.2.0"); } /** - * @tests {@link java.util.jar.Manifest#read(java.io.InputStream) + * @tests java.util.jar.Manifest#Manifest(java.io.InputStream) */ @TestTargetNew( level = TestLevel.COMPLETE, - notes = "", - method = "read", + notes = "IOException checking missed.", + method = "Manifest", args = {java.io.InputStream.class} ) - public void test_readLjava_io_InputStream() { - // Regression for HARMONY-89 - InputStream is = new InputStreamImpl(); - try { - new Manifest().read(is); - fail("Assert 0: Should have thrown IOException"); - } catch (IOException e) { - // expected - } + public void test_ConstructorLjava_io_InputStream() throws IOException { + Manifest m = getManifest(attJarName); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + m.write(baos); + InputStream is = new ByteArrayInputStream(baos.toByteArray()); + Manifest mCopy = new Manifest(is); + assertEquals(m, mCopy); - Manifest manifest = new Manifest(); - try { - manifest.read(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Can nor read manifest"); - } catch (IOException e) { - fail("Can nor read manifest"); - } - Attributes main = manifest.getMainAttributes(); - assertEquals("Bundle-Name not correct", "ClientSupport", main - .getValue("Bundle-Name")); - assertEquals( - "Bundle-Description not correct", - - "Provides SessionService, AuthenticationService. Extends RegistryService.", - main.getValue("Bundle-Description")); - assertEquals("Bundle-Activator not correct", - "com.ibm.ive.eccomm.client.support.ClientSupportActivator", - main.getValue("Bundle-Activator")); - assertEquals( - "Import-Package not correct", - - "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client", - main.getValue("Import-Package")); - assertEquals( - "Import-Service not correct", - - "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService", - main.getValue("Import-Service")); - assertEquals( - "Export-Package not correct", - - "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0", - main.getValue("Export-Package")); - assertEquals( - "Export-Service not correct", - - "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService", - main.getValue("Export-Service")); - assertEquals("Bundle-Vendor not correct", "IBM", main - .getValue("Bundle-Vendor")); - assertEquals("Bundle-Version not correct", "1.2.0", main - .getValue("Bundle-Version")); - } + Manifest manifest = new Manifest(new URL(Support_Resources + .getURL(MANIFEST_NAME)).openStream()); + checkManifest(manifest); - // helper class - class InputStreamImpl extends InputStream { - public InputStreamImpl() { - super(); - } + // regression test for HARMONY-5424 + String manifestContent = "Manifest-Version: 1.0\nCreated-By: Apache\nPackage: \nBuild-Jdk: 1.4.1_01\n\n" + + "Name: \nSpecification-Title: foo\nSpecification-Version: 1.0\nSpecification-Vendor: \n" + + "Implementation-Title: \nImplementation-Version: 1.0\nImplementation-Vendor: \n\n"; + ByteArrayInputStream bis = new ByteArrayInputStream(manifestContent + .getBytes("ISO-8859-1")); - @Override - public int read() { - return 0; - } + + Manifest mf = new Manifest(bis); + assertEquals("Should be 4 main attributes", 4, mf.getMainAttributes() + .size()); + + Map<String, Attributes> entries = mf.getEntries(); + assertEquals("Should be one named entry", 1, entries.size()); + + Attributes namedEntryAttributes = (Attributes) (entries.get("")); + assertEquals("Should be 6 named entry attributes", 6, + namedEntryAttributes.size()); } /** - * @tests java.util.jar.Manifest#Manifest(Manifest) + * @tests java.util.jar.Manifest#clear() */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "Manifest", - args = {java.util.jar.Manifest.class} + level = TestLevel.COMPLETE, + notes = "", + method = "clear", + args = {} ) - public void test_ConstructorLjava_util_jar_Manifest() { - // Test for method java.util.jar.Manifest() - Manifest emptyManifest = new Manifest(); - Manifest emptyClone = new Manifest(emptyManifest); - assertTrue("Should have no entries", emptyClone.getEntries().isEmpty()); - assertTrue("Should have no main attributes", emptyClone - .getMainAttributes().isEmpty()); - assertEquals(emptyClone, emptyManifest); - assertEquals(emptyClone, emptyManifest.clone()); + public void test_clear() { + m2.clear(); + assertTrue("Should have no entries", m2.getEntries().isEmpty()); + assertTrue("Should have no main attributes", m2.getMainAttributes() + .isEmpty()); } @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "clone", - args = {} + level = TestLevel.COMPLETE, + notes = "", + method = "clone", + args = {} ) - public void test_clone() { + public void test_clone() throws IOException { Manifest emptyManifest = new Manifest(); Manifest emptyClone = (Manifest) emptyManifest.clone(); assertTrue("Should have no entries", emptyClone.getEntries().isEmpty()); @@ -354,88 +243,25 @@ public class ManifestTest extends TestCase { assertEquals(emptyManifest.clone().getClass().getName(), "java.util.jar.Manifest"); - Manifest manifest = null; - try { - manifest = new Manifest(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } + Manifest manifest = new Manifest(new URL(Support_Resources + .getURL("manifest/hyts_MANIFEST.MF")).openStream()); Manifest manifestClone = (Manifest) manifest.clone(); - Attributes main = manifestClone.getMainAttributes(); - assertEquals("Bundle-Name not correct", "ClientSupport", main - .getValue("Bundle-Name")); - assertEquals( - "Bundle-Description not correct", - - "Provides SessionService, AuthenticationService. Extends RegistryService.", - main.getValue("Bundle-Description")); - assertEquals("Bundle-Activator not correct", - "com.ibm.ive.eccomm.client.support.ClientSupportActivator", - main.getValue("Bundle-Activator")); - assertEquals( - "Import-Package not correct", - - "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client", - main.getValue("Import-Package")); - assertEquals( - "Import-Service not correct", - - "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService", - main.getValue("Import-Service")); - assertEquals( - "Export-Package not correct", - - "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0", - main.getValue("Export-Package")); - assertEquals( - "Export-Service not correct", - - "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService", - main.getValue("Export-Service")); - assertEquals("Bundle-Vendor not correct", "IBM", main - .getValue("Bundle-Vendor")); - assertEquals("Bundle-Version not correct", "1.2.0", main - .getValue("Bundle-Version")); + manifestClone.getMainAttributes(); + checkManifest(manifestClone); } @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "equals", - args = {java.lang.Object.class} + level = TestLevel.COMPLETE, + notes = "", + method = "equals", + args = {java.lang.Object.class} ) - public void test_equals() { - Manifest manifest1 = null; - Manifest manifest2 = null; + public void test_equals() throws IOException { + Manifest manifest1 = new Manifest(new URL(Support_Resources.getURL( + "manifest/hyts_MANIFEST.MF")).openStream()); + Manifest manifest2 = new Manifest(new URL(Support_Resources.getURL( + "manifest/hyts_MANIFEST.MF")).openStream()); Manifest manifest3 = new Manifest(); - InputStream is = null; - try { - is = new URL(Support_Resources.getURL("manifest/hyts_MANIFEST.MF")) - .openStream(); - } catch (MalformedURLException e1) { - fail("Failed to create InputStream object"); - } catch (IOException e1) { - fail("Failed to create InputStream object"); - } - try { - manifest1 = new Manifest(is); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } - - try { - manifest2 = new Manifest(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } assertTrue(manifest1.equals(manifest1)); assertTrue(manifest1.equals(manifest2)); @@ -444,27 +270,69 @@ public class ManifestTest extends TestCase { } @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "hashCode", - args = {} + level = TestLevel.COMPLETE, + notes = "", + method = "hashCode", + args = {} ) - public void test_hashCode() { - Manifest manifest1 = null; + public void test_hashCode() throws IOException { + Manifest manifest1 = new Manifest(new URL(Support_Resources + .getURL("manifest/hyts_MANIFEST.MF")).openStream()); Manifest manifest2 = new Manifest(); - InputStream is = null; - try { - manifest1 = new Manifest(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } assertEquals(manifest1.hashCode(), manifest1.hashCode()); assertNotSame(manifest1.hashCode(), manifest2.hashCode()); } + /** + * @tests java.util.jar.Manifest#getAttributes(java.lang.String) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getAttributes", + args = {String.class} + ) + public void test_getAttributesLjava_lang_String() { + assertNull("Should not exist", + m2.getAttributes("Doesn't Exist")); + assertEquals("Should exist", "OK", m2.getAttributes("HasAttributes.txt").get( + new Attributes.Name("MyAttribute"))); + } + + /** + * @tests java.util.jar.Manifest#getEntries() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getEntries", + args = {} + ) + public void test_getEntries() { + Map<String, Attributes> myMap = m2.getEntries(); + assertNull("Shouldn't exist", myMap.get("Doesn't exist")); + assertEquals("Should exist", + "OK", myMap.get("HasAttributes.txt").get( + new Attributes.Name("MyAttribute"))); + } + + /** + * @tests java.util.jar.Manifest#getMainAttributes() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getMainAttributes", + args = {} + ) + public void test_getMainAttributes() { + // Test for method java.util.jar.Attributes + // java.util.jar.Manifest.getMainAttributes() + Attributes a = m.getMainAttributes(); + assertEquals("Manifest_Version should return 1.0", "1.0", a.get( + Attributes.Name.MANIFEST_VERSION)); + } + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", @@ -510,4 +378,219 @@ public class ManifestTest extends TestCase { assertTrue(manifest1.equals(manifest2)); } + + /** + * Ensures compatibility with manifests produced by gcc. + * + * @see <a + * href="http://issues.apache.org/jira/browse/HARMONY-5662">HARMONY-5662</a> + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "Manifest", + args = {InputStream.class} + ) + public void testNul() throws IOException { + String manifestContent = + "Manifest-Version: 1.0\nCreated-By: nasty gcc tool\n\n\0"; + + byte[] bytes = manifestContent.getBytes("ISO-8859-1"); + new Manifest(new ByteArrayInputStream(bytes)); // the last NUL is ok + + bytes[bytes.length - 1] = 26; + new Manifest(new ByteArrayInputStream(bytes)); // the last EOF is ok + + bytes[bytes.length - 1] = 'A'; // the last line ignored + new Manifest(new ByteArrayInputStream(bytes)); + + bytes[2] = 0; // NUL char in Manifest + try { + new Manifest(new ByteArrayInputStream(bytes)); + fail("IOException expected"); + } catch (IOException e) { + // desired + } + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + method = "Manifest", + args = {InputStream.class} + ) + @KnownFailure("CharsetDecoder fails with an IllegalStateException") + public void testDecoding() throws IOException { + Manifest m = getManifest(attJarName); + final byte[] bVendor = new byte[] { (byte) 0xd0, (byte) 0x9C, + (byte) 0xd0, (byte) 0xb8, (byte) 0xd0, (byte) 0xbb, + (byte) 0xd0, (byte) 0xb0, (byte) 0xd1, (byte) 0x8f, ' ', + (byte) 0xd0, (byte) 0xb4, (byte) 0xd0, (byte) 0xbe, + (byte) 0xd1, (byte) 0x87, (byte) 0xd1, (byte) 0x83, + (byte) 0xd0, (byte) 0xbd, (byte) 0xd1, (byte) 0x8C, + (byte) 0xd0, (byte) 0xba, (byte) 0xd0, (byte) 0xb0, ' ', + (byte) 0xd0, (byte) 0x9C, (byte) 0xd0, (byte) 0xb0, + (byte) 0xd1, (byte) 0x88, (byte) 0xd0, (byte) 0xb0 }; + + final byte[] bSpec = new byte[] { (byte) 0xe1, (byte) 0x88, + (byte) 0xb0, (byte) 0xe1, (byte) 0x88, (byte) 0x8b, + (byte) 0xe1, (byte) 0x88, (byte) 0x9d, ' ', (byte) 0xe1, + (byte) 0x9a, (byte) 0xa0, (byte) 0xe1, (byte) 0x9a, + (byte) 0xb1, (byte) 0xe1, (byte) 0x9b, (byte) 0x81, + (byte) 0xe1, (byte) 0x9a, (byte) 0xa6, ' ', (byte) 0xd8, + (byte) 0xb3, (byte) 0xd9, (byte) 0x84, (byte) 0xd8, + (byte) 0xa7, (byte) 0xd9, (byte) 0x85, ' ', (byte) 0xd8, + (byte) 0xb9, (byte) 0xd8, (byte) 0xb3, (byte) 0xd9, + (byte) 0x84, (byte) 0xd8, (byte) 0xa7, (byte) 0xd9, + (byte) 0x85, (byte) 0xd8, (byte) 0xa9, ' ', (byte) 0xdc, + (byte) 0xab, (byte) 0xdc, (byte) 0xa0, (byte) 0xdc, + (byte) 0xa1, (byte) 0xdc, (byte) 0x90, ' ', (byte) 0xe0, + (byte) 0xa6, (byte) 0xb6, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbe, (byte) 0xe0, (byte) 0xa6, (byte) 0xa8, + (byte) 0xe0, (byte) 0xa7, (byte) 0x8d, (byte) 0xe0, + (byte) 0xa6, (byte) 0xa4, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbf, ' ', (byte) 0xd0, (byte) 0xa0, (byte) 0xd0, + (byte) 0xb5, (byte) 0xd0, (byte) 0xba, (byte) 0xd1, + (byte) 0x8a, (byte) 0xd0, (byte) 0xb5, (byte) 0xd0, + (byte) 0xbb, ' ', (byte) 0xd0, (byte) 0x9c, (byte) 0xd0, + (byte) 0xb8, (byte) 0xd1, (byte) 0x80, ' ', (byte) 0xe0, + (byte) 0xa6, (byte) 0xb6, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbe, (byte) 0xe0, (byte) 0xa6, (byte) 0xa8, + (byte) 0xe0, (byte) 0xa7, (byte) 0x8d, (byte) 0xe0, + (byte) 0xa6, (byte) 0xa4, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbf, ' ', (byte) 0xe0, (byte) 0xbd, (byte) 0x9e, + (byte) 0xe0, (byte) 0xbd, (byte) 0xb2, (byte) 0xe0, + (byte) 0xbc, (byte) 0x8b, (byte) 0xe0, (byte) 0xbd, + (byte) 0x96, (byte) 0xe0, (byte) 0xbd, (byte) 0x91, + (byte) 0xe0, (byte) 0xbd, (byte) 0xba, ' ', (byte) 0xd0, + (byte) 0x9c, (byte) 0xd0, (byte) 0xb0, (byte) 0xd1, + (byte) 0x88, (byte) 0xd0, (byte) 0xb0, (byte) 0xd1, + (byte) 0x80, ' ', (byte) 0xe1, (byte) 0x8f, (byte) 0x99, + (byte) 0xe1, (byte) 0x8e, (byte) 0xaf, (byte) 0xe1, + (byte) 0x8f, (byte) 0xb1, ' ', (byte) 0xcf, (byte) 0xa8, + (byte) 0xce, (byte) 0xb9, (byte) 0xcf, (byte) 0x81, + (byte) 0xce, (byte) 0xb7, (byte) 0xce, (byte) 0xbd, + (byte) 0xce, (byte) 0xb7, ' ', (byte) 0xde, (byte) 0x90, + (byte) 0xde, (byte) 0xaa, (byte) 0xde, (byte) 0x85, + (byte) 0xde, (byte) 0xa6, ' ', (byte) 0xe0, (byte) 0xbd, + (byte) 0x82, (byte) 0xe0, (byte) 0xbd, (byte) 0x9e, + (byte) 0xe0, (byte) 0xbd, (byte) 0xb2, (byte) 0xe0, + (byte) 0xbc, (byte) 0x8b, (byte) 0xe0, (byte) 0xbd, + (byte) 0x96, (byte) 0xe0, (byte) 0xbd, (byte) 0x91, + (byte) 0xe0, (byte) 0xbd, (byte) 0xba, ' ', (byte) 0xce, + (byte) 0x95, (byte) 0xce, (byte) 0xb9, (byte) 0xcf, + (byte) 0x81, (byte) 0xce, (byte) 0xae, (byte) 0xce, + (byte) 0xbd, (byte) 0xce, (byte) 0xb7, ' ', (byte) 0xd8, + (byte) 0xb5, (byte) 0xd9, (byte) 0x84, (byte) 0xd8, + (byte) 0xad, ' ', (byte) 0xe0, (byte) 0xaa, (byte) 0xb6, + (byte) 0xe0, (byte) 0xaa, (byte) 0xbe, (byte) 0xe0, + (byte) 0xaa, (byte) 0x82, (byte) 0xe0, (byte) 0xaa, + (byte) 0xa4, (byte) 0xe0, (byte) 0xaa, (byte) 0xbf, ' ', + (byte) 0xe5, (byte) 0xb9, (byte) 0xb3, (byte) 0xe5, + (byte) 0x92, (byte) 0x8c, ' ', (byte) 0xd7, (byte) 0xa9, + (byte) 0xd7, (byte) 0x9c, (byte) 0xd7, (byte) 0x95, + (byte) 0xd7, (byte) 0x9d, ' ', (byte) 0xd7, (byte) 0xa4, + (byte) 0xd7, (byte) 0xa8, (byte) 0xd7, (byte) 0x99, + (byte) 0xd7, (byte) 0x93, (byte) 0xd7, (byte) 0x9f, ' ', + (byte) 0xe5, (byte) 0x92, (byte) 0x8c, (byte) 0xe5, + (byte) 0xb9, (byte) 0xb3, ' ', (byte) 0xe5, (byte) 0x92, + (byte) 0x8c, (byte) 0xe5, (byte) 0xb9, (byte) 0xb3, ' ', + (byte) 0xd8, (byte) 0xaa, (byte) 0xd9, (byte) 0x89, + (byte) 0xd9, (byte) 0x86, (byte) 0xda, (byte) 0x86, + (byte) 0xd9, (byte) 0x84, (byte) 0xd9, (byte) 0x89, + (byte) 0xd9, (byte) 0x82, ' ', (byte) 0xe0, (byte) 0xae, + (byte) 0x85, (byte) 0xe0, (byte) 0xae, (byte) 0xae, + (byte) 0xe0, (byte) 0xaf, (byte) 0x88, (byte) 0xe0, + (byte) 0xae, (byte) 0xa4, (byte) 0xe0, (byte) 0xae, + (byte) 0xbf, ' ', (byte) 0xe0, (byte) 0xb0, (byte) 0xb6, + (byte) 0xe0, (byte) 0xb0, (byte) 0xbe, (byte) 0xe0, + (byte) 0xb0, (byte) 0x82, (byte) 0xe0, (byte) 0xb0, + (byte) 0xa4, (byte) 0xe0, (byte) 0xb0, (byte) 0xbf, ' ', + (byte) 0xe0, (byte) 0xb8, (byte) 0xaa, (byte) 0xe0, + (byte) 0xb8, (byte) 0xb1, (byte) 0xe0, (byte) 0xb8, + (byte) 0x99, (byte) 0xe0, (byte) 0xb8, (byte) 0x95, + (byte) 0xe0, (byte) 0xb8, (byte) 0xb4, (byte) 0xe0, + (byte) 0xb8, (byte) 0xa0, (byte) 0xe0, (byte) 0xb8, + (byte) 0xb2, (byte) 0xe0, (byte) 0xb8, (byte) 0x9e, ' ', + (byte) 0xe1, (byte) 0x88, (byte) 0xb0, (byte) 0xe1, + (byte) 0x88, (byte) 0x8b, (byte) 0xe1, (byte) 0x88, + (byte) 0x9d, ' ', (byte) 0xe0, (byte) 0xb7, (byte) 0x83, + (byte) 0xe0, (byte) 0xb7, (byte) 0x8f, (byte) 0xe0, + (byte) 0xb6, (byte) 0xb8, (byte) 0xe0, (byte) 0xb6, + (byte) 0xba, ' ', (byte) 0xe0, (byte) 0xa4, (byte) 0xb6, + (byte) 0xe0, (byte) 0xa4, (byte) 0xbe, (byte) 0xe0, + (byte) 0xa4, (byte) 0xa8, (byte) 0xe0, (byte) 0xa5, + (byte) 0x8d, (byte) 0xe0, (byte) 0xa4, (byte) 0xa4, + (byte) 0xe0, (byte) 0xa4, (byte) 0xbf, (byte) 0xe0, + (byte) 0xa4, (byte) 0x83, ' ', (byte) 0xe1, (byte) 0x83, + (byte) 0x9b, (byte) 0xe1, (byte) 0x83, (byte) 0xa8, + (byte) 0xe1, (byte) 0x83, (byte) 0x95, (byte) 0xe1, + (byte) 0x83, (byte) 0x98, (byte) 0xe1, (byte) 0x83, + (byte) 0x93, (byte) 0xe1, (byte) 0x83, (byte) 0x9d, + (byte) 0xe1, (byte) 0x83, (byte) 0x91, (byte) 0xe1, + (byte) 0x83, (byte) 0x90 }; + // TODO Cannot make the following word work, encoder changes needed + // (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbc, (byte) 0xb2, (byte) 0xed, + // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc, + // (byte) 0xb0, (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbd, (byte) 0x85, (byte) 0xed, + // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc, + // (byte) 0xb0, (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbc, (byte) 0xb9, (byte) 0xed, + // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc, + // (byte) 0xb8, (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbc, (byte) 0xb9, ' ' + + final String vendor = new String(bVendor, "UTF-8"); + final String spec = new String(bSpec, "UTF-8"); + m.getMainAttributes() + .put(Attributes.Name.IMPLEMENTATION_VENDOR, vendor); + m.getAttributes(ATT_ENTRY_NAME).put( + Attributes.Name.IMPLEMENTATION_VENDOR, vendor); + m.getEntries().get(ATT_ENTRY_NAME).put( + Attributes.Name.SPECIFICATION_TITLE, spec); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + m.write(baos); + m = new Manifest(new ByteArrayInputStream(baos.toByteArray())); + + assertEquals(vendor, m.getMainAttributes().get( + Attributes.Name.IMPLEMENTATION_VENDOR)); + assertEquals(vendor, m.getEntries().get(ATT_ENTRY_NAME).get( + Attributes.Name.IMPLEMENTATION_VENDOR)); + assertEquals(spec, m.getAttributes(ATT_ENTRY_NAME).get( + Attributes.Name.SPECIFICATION_TITLE)); + } + + /** + * @tests {@link java.util.jar.Manifest#read(java.io.InputStream) + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "read", + args = {InputStream.class} + ) + public void testRead() { + // Regression for HARMONY-89 + InputStream is = new InputStreamImpl(); + try { + new Manifest().read(is); + fail("IOException expected"); + } catch (IOException e) { + // desired + } + } + + // helper class + private class InputStreamImpl extends InputStream { + public InputStreamImpl() { + super(); + } + + @Override + public int read() { + return 0; + } + } } diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java index 75060bd..1e8ddb4 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java @@ -25,6 +25,8 @@ import dalvik.annotation.TestTargetNew; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -310,7 +312,43 @@ public class GZIPInputStreamTest extends junit.framework.TestCase { } } - @Override + /** + * Regression test for HARMONY-3703. + * @tests java.util.zip.GZIPInputStream#read() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "read", + args = {byte[].class} + ) + public void test_read() throws IOException { + GZIPInputStream gis = null; + int result = 0; + byte[] buffer = new byte[] {1,2,3,4,5,6,7,8,9,10}; + File f = new File(resources.getAbsolutePath() + "test.gz"); + FileOutputStream out = new FileOutputStream(f); + GZIPOutputStream gout = new GZIPOutputStream(out); + + // write 100 bytes to the stream + for(int i = 0; i < 10; i++) { + gout.write(buffer); + } + gout.finish(); + out.write(1); + out.close(); + + gis = new GZIPInputStream(new FileInputStream(f)); + buffer = new byte[100]; + gis.read(buffer); + result = gis.read(); + gis.close(); + f.delete(); + + assertEquals("Incorrect value returned at the end of the file", -1, result); + } + + @Override protected void setUp() { resources = Support_Resources.createTempFolder(); } diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java index 9b23b56..b71ce63 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java @@ -201,8 +201,6 @@ public class GZIPOutputStreamTest extends junit.framework.TestCase { int r = 0; try { outGZIP.write(byteArray, 0, 11); - } catch (ArrayIndexOutOfBoundsException e) { - r = 1; } catch (IndexOutOfBoundsException ee) { r = 1; } diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java index 8b89180..6039c5b 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java @@ -409,6 +409,22 @@ public class InflaterTest extends junit.framework.TestCase { } /** + * @tests java.util.zip.Inflater#Inflater() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "Inflater", + args = {} + ) + public void test_Constructor() { + // test method of java.util.zip.inflater.Inflater() + Inflater inflate = new Inflater(); + assertNotNull("failed to create the instance of inflater", + inflate); + } + + /** * @tests java.util.zip.Inflater#inflate(byte[], int, int) */ @TestTargetNew( @@ -504,27 +520,6 @@ public class InflaterTest extends junit.framework.TestCase { } /** - * @tests java.util.zip.Inflater#Inflater() - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "Inflater", - args = {} - ) - public void test_Constructor() { - // test method of java.util.zip.inflater.Inflater() - try { - Inflater inflate = new Inflater(); - assertNotNull("failed to create the instance of inflater", inflate); - - } catch (Exception e) { - - assertTrue("Inflate () constructor threw an exception", true); - } - } - - /** * @tests java.util.zip.Inflater#Inflater(boolean) */ @TestTargetNew( diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java index 5530a2e..c9e7bb8 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java @@ -67,7 +67,7 @@ public class ZipFileTest extends junit.framework.TestCase { public void checkPermission(Permission perm) { // only check if it's a FilePermission because Locale checks // for a PropertyPermission with action"read" to get system props. - if (perm instanceof FilePermission + if (perm instanceof FilePermission && perm.getActions().equals(forbidenPermissionAction)) { throw new SecurityException(); } @@ -145,7 +145,7 @@ public class ZipFileTest extends junit.framework.TestCase { public void test_ConstructorLjava_lang_String() throws IOException { String oldUserDir = System.getProperty("user.dir"); System.setProperty("user.dir", System.getProperty("java.io.tmpdir")); - + zfile.close(); // about to reopen the same temp file ZipFile zip = new ZipFile(tempFileName); zip.close(); @@ -260,7 +260,7 @@ public class ZipFileTest extends junit.framework.TestCase { method = "entries", args = {} ) - public void test_entries() { + public void test_entries() throws Exception { // Test for method java.util.Enumeration java.util.zip.ZipFile.entries() Enumeration<? extends ZipEntry> enumer = zfile.entries(); int c = 0; @@ -270,20 +270,16 @@ public class ZipFileTest extends junit.framework.TestCase { } assertTrue("Incorrect number of entries returned: " + c, c == 6); + Enumeration<? extends ZipEntry> enumeration = zfile.entries(); + zfile.close(); + zfile = null; + boolean pass = false; try { - Enumeration<? extends ZipEntry> enumeration = zfile.entries(); - zfile.close(); - zfile = null; - boolean pass = false; - try { - enumeration.hasMoreElements(); - } catch (IllegalStateException e) { - pass = true; - } - assertTrue("did not detect closed jar file", pass); - } catch (Exception e) { - fail("Exception during entries test: " + e.toString()); + enumeration.hasMoreElements(); + } catch (IllegalStateException e) { + pass = true; } + assertTrue("did not detect closed jar file", pass); } /** @@ -454,6 +450,99 @@ public class ZipFileTest extends junit.framework.TestCase { } /** + * @tests java.io.InputStream#reset() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + method = "getInputStream", + args = {java.util.zip.ZipEntry.class} + ) + @KnownFailure("ZipEntry.getInputStream().reset() fails with an IOException") + public void test_reset() throws IOException { + // read an uncompressed entry + ZipEntry zentry = zfile.getEntry("File1.txt"); + InputStream is = zfile.getInputStream(zentry); + byte[] rbuf1 = new byte[6]; + byte[] rbuf2 = new byte[6]; + int r1, r2; + r1 = is.read(rbuf1); + assertEquals(rbuf1.length, r1); + r2 = is.read(rbuf2); + assertEquals(rbuf2.length, r2); + + is.reset(); + r2 = is.read(rbuf2); + assertEquals(rbuf2.length, r2); + is.close(); + + // read a compressed entry + byte[] rbuf3 = new byte[4185]; + ZipEntry zentry2 = zfile.getEntry("File3.txt"); + is = zfile.getInputStream(zentry2); + r1 = is.read(rbuf3); + assertEquals(4183, r1); + is.reset(); + + r1 = is.read(rbuf3); + assertEquals(4183, r1); + is.close(); + + is = zfile.getInputStream(zentry2); + r1 = is.read(rbuf3, 0, 3000); + assertEquals(3000, r1); + is.reset(); + r1 = is.read(rbuf3, 0, 3000); + assertEquals(3000, r1); + is.close(); + } + + /** + * @tests java.io.InputStream#reset() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + method = "getInputStream", + args = {java.util.zip.ZipEntry.class} + ) + @KnownFailure("ZipEntry.getInputStream().reset() fails with an IOException") + public void test_reset_subtest0() throws IOException { + // read an uncompressed entry + ZipEntry zentry = zfile.getEntry("File1.txt"); + InputStream is = zfile.getInputStream(zentry); + byte[] rbuf1 = new byte[12]; + byte[] rbuf2 = new byte[12]; + int r = is.read(rbuf1, 0, 4); + assertEquals(4, r); + is.mark(0); + r = is.read(rbuf1); + assertEquals(8, r); + assertEquals(-1, is.read()); + + is.reset(); + r = is.read(rbuf2); + assertEquals(8, r); + assertEquals(-1, is.read()); + is.close(); + + // read a compressed entry + byte[] rbuf3 = new byte[4185]; + ZipEntry zentry2 = zfile.getEntry("File3.txt"); + is = zfile.getInputStream(zentry2); + r = is.read(rbuf3, 0, 3000); + assertEquals(3000, r); + is.mark(0); + r = is.read(rbuf3); + assertEquals(1183, r); + assertEquals(-1, is.read()); + + is.reset(); + r = is.read(rbuf3); + assertEquals(1183, r); + assertEquals(-1, is.read()); + is.close(); + } + + /** * Sets up the fixture, for example, open a network connection. This method * is called before a test is executed. */ diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java index 9a5f63a..8ca551d 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java @@ -170,11 +170,8 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { public void test_setCommentLjava_lang_String() { // There is no way to get the comment back, so no way to determine if // the comment is set correct - try { - zos.setComment("test setComment"); - } catch (Exception e) { - fail("Trying to set comment failed"); - } + zos.setComment("test setComment"); + try { zos.setComment(new String(new byte[0xFFFF + 1])); fail("Comment over 0xFFFF in length should throw exception"); @@ -301,6 +298,17 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { } catch (IndexOutOfBoundsException e) { // expected } + + // Regression for HARMONY-4405 + try { + zip.write(null, 0, -2); + fail("Should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + // Close stream because ZIP is invalid + stream.close(); } /** @@ -337,6 +345,8 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { } catch (IOException e2) { // expected } + + zip1.close(); } @Override |