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
diff --git a/libcore/archive/src/main/java/java/util/jar/Attributes.java b/libcore/archive/src/main/java/java/util/jar/Attributes.java
index 5a4d923..4ee94df 100644
--- a/libcore/archive/src/main/java/java/util/jar/Attributes.java
+++ b/libcore/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 @@
* 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 @@
* {@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 @@
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
* 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 @@
* 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 @@
/**
* 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 @@
/**
* Removes all key/value pairs from this {@code Attributes}.
- *
- * @since Android 1.0
*/
public void clear() {
map.clear();
@@ -321,11 +303,10 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* Stores key/value pairs in this {@code Attributes}.
- *
+ *
* @param key
* the key to associate with value.
* @param value
@@ -399,21 +375,20 @@
* @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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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/libcore/archive/src/main/java/java/util/jar/InitManifest.java b/libcore/archive/src/main/java/java/util/jar/InitManifest.java
index 47fdf1c..bf9c397 100644
--- a/libcore/archive/src/main/java/java/util/jar/InitManifest.java
+++ b/libcore/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;
+ InitManifest(byte[] buf, Attributes main, Attributes.Name ver)
+ throws IOException {
- private final Map<String, Attributes.Name> attributeNames = new HashMap<String, Attributes.Name>();
+ this.buf = buf;
- private final byte[] mainAttributesChunk;
-
- 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);
+ String entryNameValue = value;
+
+ Attributes entry = entries.get(entryNameValue);
+ if (entry == null) {
+ entry = new Attributes(12);
+ }
+
+ while (readHeader()) {
+ entry.put(name, value);
+ }
+
if (chunks != null) {
- chunks.put(name, chunk);
- }
- entries.put(name, current);
- while (it.hasNext()) {
- addAttribute(it.next(), current);
- }
- list.clear();
- }
-
- }
-
- byte[] getMainAttributesChunk() {
- return mainAttributesChunk;
- }
-
- 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;
+ 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;
}
- 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));
- }
+
+ entries.put(entryNameValue, entry);
}
}
- private byte[] nextChunk(InputStream in, List<String> lines)
- throws IOException {
- if (inbufCount == -1) {
- return null;
- }
- 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';
- } else {
- if (out.size() == 0) {
- continue;
- }
- out.write('\r');
- }
- lastCr = false;
- } else if (next == '\r') {
- lastCr = true;
- 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) {
- 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;
- }
- buffer[pos++] = next;
- }
+ int getPos() {
+ return pos;
}
- private boolean readLines(InputStream in, List<String> lines)
- throws IOException {
- if (inbufCount == -1) {
+ /**
+ * 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;
}
- 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;
+ readName();
+ linebreak = 0;
+ readValue();
+ // if the last line break is missed, the line
+ // is ignored by the reference implementation
+ return linebreak > 0;
+ }
+
+ 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 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$
}
- if (inbufCount == inbuf.length && in.available() == 0) {
- /* archive.2E = "line too long" */
- throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$
- }
- inbufPos = 0;
+
+ name = new Attributes.Name(nameBuffer);
+ return;
}
- next = inbuf[inbufPos++];
- if (lastCr) {
- if (next != '\n') {
- inbufPos--;
- next = '\r';
- }
- lastCr = false;
- } else if (next == '\r') {
- lastCr = true;
- continue;
+
+ 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 (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;
+ }
+ if (i > 0) {
+ throw new IOException(Messages.getString(
+ "archive.30", wrap(mark, buf.length))); //$NON-NLS-1$
}
}
- /* Get the next attribute and add it */
- private void addAttribute(String line, Attributes current)
- 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());
+ private void readValue() throws IOException {
+ byte next;
+ 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 {
+ linebreak++;
+ }
+ continue;
+ case '\r':
+ lastCr = true;
+ linebreak++;
+ continue;
+ case ' ':
+ if (linebreak == 1) {
+ decode(mark, last, false);
+ mark = pos;
+ linebreak = 0;
+ continue;
+ }
}
- attributeNames.put(header, name);
+
+ if (linebreak >= 1) {
+ pos--;
+ break;
+ }
+ last = pos;
}
- if (hdrIdx + 1 >= line.length() || line.charAt(hdrIdx + 1) != ' ') {
- throw new IOException(Messages.getString("archive.2F", line)); //$NON-NLS-1$
+
+ decode(mark, last, true);
+ while (CoderResult.OVERFLOW == decoder.flush(cBuf)) {
+ enlargeBuffer();
}
- // +2 due to required SPACE char
- current.put(name, line.substring(hdrIdx + 2, line.length()));
+ value = new String(cBuf.array(), cBuf.arrayOffset(), cBuf.position());
+ }
+
+ private void decode(int mark, int pos, boolean endOfInput)
+ throws IOException {
+ ByteBuffer bBuf = ByteBuffer.wrap(buf, mark, pos - mark);
+ while (CoderResult.OVERFLOW == decoder.decode(bBuf, cBuf, endOfInput)) {
+ enlargeBuffer();
+ }
+ }
+
+ 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/libcore/archive/src/main/java/java/util/jar/JarEntry.java b/libcore/archive/src/main/java/java/util/jar/JarEntry.java
index b8dabee..869e4b4 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarEntry.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarEntry.java
@@ -33,27 +33,27 @@
/**
* 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 @@
*
* @param entry
* The ZipEntry to obtain values from.
- * @since Android 1.0
*/
public JarEntry(ZipEntry entry) {
super(entry);
@@ -78,7 +77,6 @@
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @return the code signers for the JAR entry.
* @see CodeSigner
- * @since Android 1.0
*/
public CodeSigner[] getCodeSigners() {
if (null == signers) {
@@ -155,7 +150,7 @@
}
private CodeSigner[] getCodeSigners(Certificate[] certs) {
- if(null == certs) {
+ if (null == certs) {
return null;
}
diff --git a/libcore/archive/src/main/java/java/util/jar/JarException.java b/libcore/archive/src/main/java/java/util/jar/JarException.java
index f18d639..d6943c4 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarException.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarException.java
@@ -22,8 +22,6 @@
/**
* 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 @@
/**
* Constructs a new {@code JarException} instance.
- *
- * @since Android 1.0
*/
public JarException() {
super();
@@ -41,10 +37,9 @@
/**
* 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/libcore/archive/src/main/java/java/util/jar/JarFile.java b/libcore/archive/src/main/java/java/util/jar/JarFile.java
index 9af9056..d6e8339 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarFile.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarFile.java
@@ -29,7 +29,6 @@
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 @@
/**
* {@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,63 +66,69 @@
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;
}
- return r;
}
+ // BEGIN android-added
+ @Override
+ public int available() throws IOException {
+ if (count > 0) {
+ return super.available();
+ } else {
+ return 0;
+ }
+ }
+ // END android-added
+
@Override
public long skip(long nbytes) throws IOException {
long cnt = 0, rem = 0;
@@ -146,12 +148,11 @@
/**
* 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 @@
/**
* 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 @@
/**
* Create a new {@code JarFile} using the contents of file.
- *
+ *
* @param file
* the JAR file as {@link File}.
* @param verify
@@ -188,7 +188,6 @@
* {@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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 JarEntry nextElement() {
JarEntry je = new JarEntry(ze.nextElement());
- je.parentJar = jf;
+ je.parentJar = jf;
return je;
}
}
@@ -270,11 +266,10 @@
/**
* 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 @@
/**
* 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 @@
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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
// 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/libcore/archive/src/main/java/java/util/jar/JarInputStream.java b/libcore/archive/src/main/java/java/util/jar/JarInputStream.java
index ef353ab..c803183 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarInputStream.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarInputStream.java
@@ -24,14 +24,13 @@
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 @@
/**
* Constructs a new {@code JarInputStream} from an input stream.
- *
+ *
* @param stream
* the input stream containing the JAR file.
* @param verify
@@ -59,7 +58,6 @@
* @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 @@
if (verify) {
verifier.setManifest(manifest);
if (manifest != null) {
- verifier.mainAttributesChunk = manifest
- .getMainAttributesChunk();
+ verifier.mainAttributesEnd = manifest
+ .getMainAttributesEnd();
}
}
@@ -103,13 +101,12 @@
/**
* 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 @@
* 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 @@
* @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 @@
/**
* 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 @@
* @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 @@
throw e;
}
} else {
- verifier.verifySignatures(
- (JarVerifier.VerifierEntry) verStream,
- jarEntry);
+ ((JarVerifier.VerifierEntry) verStream).verify();
}
}
} else {
@@ -194,7 +186,6 @@
* @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/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java b/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java
index 640f4ef..e901d87 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java
@@ -25,8 +25,6 @@
/**
* 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 @@
* @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/libcore/archive/src/main/java/java/util/jar/JarVerifier.java b/libcore/archive/src/main/java/java/util/jar/JarVerifier.java
index b9173f2..6c1ee93 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarVerifier.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarVerifier.java
@@ -31,13 +31,12 @@
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 @@
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 @@
* 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 @@
}
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 @@
}
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 @@
* 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 @@
/**
* 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 @@
* <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 @@
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 @@
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 @@
} 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 @@
}
/**
- * 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 @@
}
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 @@
} 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 @@
* 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 @@
/**
* 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 @@
* 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/libcore/archive/src/main/java/java/util/jar/Manifest.java b/libcore/archive/src/main/java/java/util/jar/Manifest.java
index 3b0d89a..b28f3fb 100644
--- a/libcore/archive/src/main/java/java/util/jar/Manifest.java
+++ b/libcore/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>();
- private HashMap<String, byte[]> chunks;
+ static class Chunk {
+ int start;
+ int end;
+
+ Chunk(int start, int end) {
+ this.start = start;
+ this.end = end;
+ }
+ }
+
+ private HashMap<String, Chunk> chunks;
/**
- * The data chunk of main attributes in the manifest is needed in
+ * Manifest bytes are used for delayed entry parsing.
+ */
+ private InitManifest im;
+
+ /**
+ * 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
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 @@
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;
- }
+ 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$
}
- 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();
+
+ 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);
}
- 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;
- }
- if (size > 0) {
- if (limit != LINE_LENGTH_LIMIT) {
- os.write(' ');
- }
- 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/libcore/archive/src/main/java/java/util/jar/Pack200.java b/libcore/archive/src/main/java/java/util/jar/Pack200.java
index e0689e0..8145fa1 100644
--- a/libcore/archive/src/main/java/java/util/jar/Pack200.java
+++ b/libcore/archive/src/main/java/java/util/jar/Pack200.java
@@ -27,8 +27,6 @@
/**
* Class factory for {@link Pack200.Packer} and {@link Pack200.Unpacker}.
- *
- * @since Android 1.0
*/
public abstract class Pack200 {
@@ -39,8 +37,8 @@
/**
* Prevent this class from being instantiated.
*/
- private Pack200(){
- //do nothing
+ private Pack200() {
+ // do nothing
}
/**
@@ -50,10 +48,8 @@
* {@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 @@
* 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 @@
/**
* 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 @@
* 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 @@
* 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 @@
/**
* 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 @@
* 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 @@
* 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/libcore/archive/src/main/java/java/util/zip/Adler32.java b/libcore/archive/src/main/java/java/util/zip/Adler32.java
index 8eaa18e..a5f77d7 100644
--- a/libcore/archive/src/main/java/java/util/zip/Adler32.java
+++ b/libcore/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 @@
* 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 @@
/**
* Reset this instance to its initial checksum.
- *
- * @since Android 1.0
*/
public void reset() {
adler = 1;
@@ -55,7 +50,6 @@
*
* @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 @@
*
* @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 @@
* @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/libcore/archive/src/main/java/java/util/zip/CRC32.java b/libcore/archive/src/main/java/java/util/zip/CRC32.java
index 36dc376..59e8f81 100644
--- a/libcore/archive/src/main/java/java/util/zip/CRC32.java
+++ b/libcore/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 @@
* 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 @@
/**
* Resets the CRC32 checksum to it initial state.
- *
- * @since Android 1.0
*/
public void reset() {
tbytes = crc = 0;
@@ -52,10 +46,9 @@
/**
* 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 @@
*
* @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 @@
* 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/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java b/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java
index 659125a..6513e66 100644
--- a/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java
+++ b/libcore/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 @@
* 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 @@
* 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 @@
* otherwise.
* @throws IOException
* if an {@code IOException} occurs.
- * @since Android 1.0
*/
@Override
public int read() throws IOException {
@@ -84,7 +79,6 @@
* 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 @@
* 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 @@
* @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/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java b/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java
index 9699492..08fe799 100644
--- a/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java
+++ b/libcore/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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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/libcore/archive/src/main/java/java/util/zip/Checksum.java b/libcore/archive/src/main/java/java/util/zip/Checksum.java
index 0405c08..901ff7a 100644
--- a/libcore/archive/src/main/java/java/util/zip/Checksum.java
+++ b/libcore/archive/src/main/java/java/util/zip/Checksum.java
@@ -20,8 +20,6 @@
/**
* 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 @@
* 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/libcore/archive/src/main/java/java/util/zip/DataFormatException.java b/libcore/archive/src/main/java/java/util/zip/DataFormatException.java
index 9ffe2ab..1e9c5a2 100644
--- a/libcore/archive/src/main/java/java/util/zip/DataFormatException.java
+++ b/libcore/archive/src/main/java/java/util/zip/DataFormatException.java
@@ -20,8 +20,6 @@
/**
* {@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 @@
/**
* Constructs a new {@code DataFormatException} instance.
- *
- * @since Android 1.0
*/
public DataFormatException() {
super();
@@ -39,10 +35,9 @@
/**
* 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/libcore/archive/src/main/java/java/util/zip/Deflater.java b/libcore/archive/src/main/java/java/util/zip/Deflater.java
index f91f1ca..38771a8 100644
--- a/libcore/archive/src/main/java/java/util/zip/Deflater.java
+++ b/libcore/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 @@
* 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 @@
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 @@
*
* @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 @@
* 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 @@
throw new IllegalArgumentException();
}
compressLevel = level;
- streamHandle = createStream(compressLevel, strategy, noHeader);
+ streamHandle = createStreamWithMemoryEnsurance(compressLevel, strategy,
+ noHeader);
}
/**
@@ -178,7 +161,6 @@
* 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 @@
* @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 @@
* 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 @@
* to it.
*
* @see #finished
- * @since Android 1.0
*/
public synchronized void finish() {
flushParm = Z_FINISH;
@@ -256,7 +234,6 @@
* 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 @@
* used.
* @see #setDictionary(byte[])
* @see #setDictionary(byte[], int, int)
- * @since Android 1.0
*/
public synchronized int getAdler() {
if (streamHandle == -1) {
@@ -287,14 +263,13 @@
* 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 @@
* 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 @@
* @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 @@
* {@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 @@
* @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 @@
* 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 @@
* @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 @@
*
* @param buf
* the buffer.
- * @since Android 1.0
*/
public void setInput(byte[] buf) {
setInput(buf, 0, buf.length);
@@ -427,7 +396,6 @@
* 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 @@
* 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 @@
* @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 @@
}
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 @@
* 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 @@
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/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java b/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java
index 773e4c4..03769fb 100644
--- a/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java
+++ b/libcore/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 @@
* 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 @@
* @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 @@
*
* @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 @@
* 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 @@
*
* @throws IOException
* If an error occurs during deflation.
- * @since Android 1.0
*/
protected void deflate() throws IOException {
int x = 0;
@@ -132,7 +122,6 @@
* @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 @@
*
* @throws IOException
* If an error occurs.
- * @since Android 1.0
*/
public void finish() throws IOException {
if (done) {
@@ -186,7 +174,6 @@
* 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/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java b/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
index fc70d62..bb84f5b 100644
--- a/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
+++ b/libcore/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 @@
* 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 @@
* 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 @@
}
}
+ /**
+ * 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 @@
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 @@
} 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 @@
}
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/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java b/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java
index fa41e19..f146da1 100644
--- a/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java
+++ b/libcore/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 @@
* 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 @@
* 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 @@
/**
* 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 @@
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/libcore/archive/src/main/java/java/util/zip/Inflater.java b/libcore/archive/src/main/java/java/util/zip/Inflater.java
index 9b93e54..cb1ce68 100644
--- a/libcore/archive/src/main/java/java/util/zip/Inflater.java
+++ b/libcore/archive/src/main/java/java/util/zip/Inflater.java
@@ -30,45 +30,65 @@
* 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 boolean finished; // Set by the inflateImpl native
-
- private boolean needsDictionary; // Set by the inflateImpl native
-
- private long streamHandle = -1;
-
- int inRead;
-
- int inLength;
-
- // Fill in the JNI id caches
- private static native void oneTimeInitialization();
+ private static final byte MAGIC_NUMBER = 120;
static {
oneTimeInitialization();
}
-
- private static final byte MAGIC_NUMBER = 120;
+
+ // Fill in the JNI id caches
+ private static native void oneTimeInitialization();
+
+ private boolean finished; // Set by the inflateImpl native
+
private boolean gotFirstByte = false;
+
+ int inLength;
+
+ int inRead;
+
+ private boolean needsDictionary; // Set by the inflateImpl native
+
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 @@
* 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 @@
/**
* 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 @@
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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
if (streamHandle == -1) {
throw new IllegalStateException();
}
-
+
if (!pass_magic_number_check) {
throw new DataFormatException();
}
@@ -213,7 +257,7 @@
if (needsInput()) {
return 0;
}
-
+
boolean neededDict = needsDictionary;
needsDictionary = false;
int result = inflateImpl(buf, off, nbytes, streamHandle);
@@ -229,41 +273,15 @@
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 @@
/**
* 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 @@
/**
* 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 @@
* 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 @@
/**
* 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 @@
* @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 @@
/**
* 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 @@
* {@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 @@
* @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 @@
// 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 @@
* 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 @@
}
// 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 @@
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/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
index 5d3bda0..1fd3602 100644
--- a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
+++ b/libcore/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 @@
* (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 @@
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
* @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 @@
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 @@
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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
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 @@
* 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/libcore/archive/src/main/java/java/util/zip/ZipConstants.java b/libcore/archive/src/main/java/java/util/zip/ZipConstants.java
index d804b0e..d00adc9 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipConstants.java
+++ b/libcore/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/libcore/archive/src/main/java/java/util/zip/ZipEntry.java b/libcore/archive/src/main/java/java/util/zip/ZipEntry.java
index 2cc7a9c..9774b6a 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipEntry.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipEntry.java
@@ -36,10 +36,9 @@
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* Gets the name of this {@code ZipEntry}.
- *
+ *
* @return the entry name.
- * @since Android 1.0
*/
public String getName() {
return name;
@@ -212,10 +200,9 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
* 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 @@
int count;
int len = b.length;
int off = 0;
-
+
while (len > 0) {
count = in.read(b, off, len);
if (count <= 0)
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipException.java b/libcore/archive/src/main/java/java/util/zip/ZipException.java
index 590117b..6dab26f 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipException.java
+++ b/libcore/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 @@
*
* @see ZipFile
* @see ZipInputStream
- * @since Android 1.0
*/
public class ZipException extends IOException {
@@ -34,8 +32,6 @@
/**
* Constructs a new {@code ZipException} instance.
- *
- * @since Android 1.0
*/
public ZipException() {
super();
@@ -44,10 +40,9 @@
/**
* 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/libcore/archive/src/main/java/java/util/zip/ZipFile.java b/libcore/archive/src/main/java/java/util/zip/ZipFile.java
index f1415d9..653b2c9 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipFile.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipFile.java
@@ -37,17 +37,13 @@
* 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 @@
/**
* 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 @@
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
index 262fa3f..1a35b1c 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
+++ b/libcore/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 @@
* 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 @@
static final int ZIPLocalHeaderVersionNeeded = 20;
- // BEGI android-removed
+ // BEGIN android-removed
// private boolean zipClosed = false;
// END android-removed
@@ -82,10 +77,9 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/* 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 @@
/**
* 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 @@
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 @@
/**
* 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/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
index 4ddf643..58e781f 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
+++ b/libcore/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 @@
* {@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 @@
*
* @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 @@
*
* @throws IOException
* If an error occurs closing the stream.
- * @since Android 1.0
*/
@Override
public void close() throws IOException {
@@ -119,7 +109,6 @@
*
* @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 @@
*
* @throws IOException
* if an error occurs while terminating the stream.
- * @since Android 1.0
*/
@Override
public void finish() throws IOException {
@@ -253,7 +241,6 @@
* @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 @@
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 @@
*
* @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 @@
* @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 @@
*
* @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 @@
}
+ /**
+ * 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/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
index 764f34d..3ba50fa 100644
--- a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
+++ b/libcore/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 @@
* 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 @@
/**
* 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 @@
/**
* Retrieves a message which takes 1 argument.
- *
+ *
* @param msg
* String the key to look up.
* @param arg
@@ -87,7 +86,7 @@
/**
* Retrieves a message which takes 1 integer argument.
- *
+ *
* @param msg
* String the key to look up.
* @param arg
@@ -100,7 +99,7 @@
/**
* Retrieves a message which takes 1 character argument.
- *
+ *
* @param msg
* String the key to look up.
* @param arg
@@ -113,7 +112,7 @@
/**
* Retrieves a message which takes 2 arguments.
- *
+ *
* @param msg
* String the key to look up.
* @param arg1
@@ -128,7 +127,7 @@
/**
* Retrieves a message which takes several arguments.
- *
+ *
* @param msg
* String the key to look up.
* @param args
diff --git a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties
index 1ae5c5e..e909af0 100644
--- a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties
+++ b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties
@@ -60,8 +60,11 @@
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/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java b/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java
index bed3e91..b15108a 100644
--- a/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java
+++ b/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java
@@ -30,39 +30,49 @@
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 buffer.toString();
+ return c;
+ }
+
+ static final byte toASCIIUpperCase(byte b) {
+ if ('a' <= b && b <= 'z') {
+ return (byte) (b - ('a' - 'A'));
+ }
+ return b;
}
}
diff --git a/libcore/archive/src/main/native/java_util_zip_Adler32.c b/libcore/archive/src/main/native/java_util_zip_Adler32.c
index a7a182a..1b02a11 100644
--- a/libcore/archive/src/main/native/java_util_zip_Adler32.c
+++ b/libcore/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 @@
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/libcore/archive/src/main/native/java_util_zip_CRC32.c b/libcore/archive/src/main/native/java_util_zip_CRC32.c
index 0688868..cee25e5 100644
--- a/libcore/archive/src/main/native/java_util_zip_CRC32.c
+++ b/libcore/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 @@
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/libcore/archive/src/main/native/java_util_zip_Deflater.c b/libcore/archive/src/main/native/java_util_zip_Deflater.c
index c8bd199..2e0e268 100644
--- a/libcore/archive/src/main/native/java_util_zip_Deflater.c
+++ b/libcore/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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
int level, int strategy,
jlong handle)
{
- PORT_ACCESS_FROM_ENV (env);
-
JCLZipStream *stream;
jbyte b = 0;
int err = 0;
@@ -276,8 +284,9 @@
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/libcore/archive/src/main/native/java_util_zip_Inflater.c b/libcore/archive/src/main/native/java_util_zip_Inflater.c
index d3a7d7c..4b30d4e 100644
--- a/libcore/archive/src/main/native/java_util_zip_Inflater.c
+++ b/libcore/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 @@
} 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 @@
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 @@
{
jclmem_free_memory (env, stream);
jclmem_free_memory (env, jstream);
- throwNewIllegalArgumentException (env, "");
+ THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException);
return -1;
}
@@ -134,8 +120,10 @@
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 @@
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 @@
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 @@
}
else
{
- throwNewDataFormatException (env, "");
+ THROW_ZIP_EXCEPTION(env, err, DataFormatException);
return -1;
}
}
@@ -280,7 +267,7 @@
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 @@
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 @@
stream = (JCLZipStream *) ((IDATA) handle);
return stream->stream->total_out;
+
}
JNIEXPORT jlong JNICALL
diff --git a/libcore/archive/src/main/native/sieb.c b/libcore/archive/src/main/native/sieb.c
index ab9430b..6881cf6 100644
--- a/libcore/archive/src/main/native/sieb.c
+++ b/libcore/archive/src/main/native/sieb.c
@@ -10,20 +10,6 @@
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/libcore/archive/src/main/native/sieb.h b/libcore/archive/src/main/native/sieb.h
index 536c806..541ad90 100644
--- a/libcore/archive/src/main/native/sieb.h
+++ b/libcore/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/libcore/archive/src/main/native/sub.mk b/libcore/archive/src/main/native/sub.mk
index 047c319..694c185 100644
--- a/libcore/archive/src/main/native/sub.mk
+++ b/libcore/archive/src/main/native/sub.mk
@@ -7,7 +7,8 @@
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/libcore/archive/src/main/native/zip.c b/libcore/archive/src/main/native/zip.c
new file mode 100644
index 0000000..3d15d2a
--- /dev/null
+++ b/libcore/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/libcore/archive/src/main/native/zip.h b/libcore/archive/src/main/native/zip.h
new file mode 100644
index 0000000..1452073
--- /dev/null
+++ b/libcore/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/libcore/archive/src/main/native/zipsup.c b/libcore/archive/src/main/native/zipsup.c
index 1bbe51f..22ea7e9 100644
--- a/libcore/archive/src/main/native/zipsup.c
+++ b/libcore/archive/src/main/native/zipsup.c
@@ -1438,7 +1438,7 @@
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/libcore/archive/src/main/native/zipsup.h b/libcore/archive/src/main/native/zipsup.h
index adc086a..67a2eda 100644
--- a/libcore/archive/src/main/native/zipsup.h
+++ b/libcore/archive/src/main/native/zipsup.h
@@ -34,23 +34,17 @@
#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 @@
-// 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/libcore/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java b/libcore/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java
new file mode 100644
index 0000000..9c28dc2
--- /dev/null
+++ b/libcore/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/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java
index 0a8b037..0b3d2cf 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java
@@ -421,6 +421,27 @@
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 @@
}
@TestTargetNew(
- level = TestLevel.COMPLETE,
+ level = TestLevel.PARTIAL_COMPLETE,
notes = "",
method = "hashCode",
args = {}
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java
index 40eff3b..90144be 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java
@@ -72,6 +72,29 @@
}
/**
+ * @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 @@
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 @@
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 @@
}
@TestTargetNew(
- level = TestLevel.COMPLETE,
+ level = TestLevel.PARTIAL_COMPLETE,
notes = "",
method = "JarEntry",
args = {java.util.jar.JarEntry.class}
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java
index 720f78d..96321a4 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java
+++ b/libcore/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 @@
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 @@
* @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 @@
}
/**
+ * 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 @@
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 @@
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 @@
* @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 @@
}
}
+
+ /**
+ * @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 @@
}
/**
- * @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 @@
} catch (Exception e) {
fail("Exception during test 4: " + e);
}
-
+
try {
JarFile jar = new JarFile(signedFile);
JarEntry entry = new JarEntry(entryName3);
@@ -682,7 +708,7 @@
} 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 @@
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 @@
Enumeration<JarEntry> entries = jarFile.entries();
int count = 0;
while (entries.hasMoreElements()) {
-
ZipEntry zipEntry = entries.nextElement();
jarFile.getInputStream(zipEntry);
count++;
@@ -818,7 +873,7 @@
while (in.available() > 0) {
in.read(buffer);
}
- fail("should throw Security Exception");
+ fail("SecurityException expected");
} catch (SecurityException e) {
// desired
}
@@ -827,7 +882,7 @@
/*
* 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 @@
ZipEntry zipEntry = entries.nextElement();
try {
jarFile.getInputStream(zipEntry);
- fail("should throw Security Exception");
+ fail("SecurityException expected");
} catch (SecurityException e) {
// desired
}
@@ -927,4 +982,83 @@
// 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/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java
index e652137..acdad71 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java
@@ -48,7 +48,7 @@
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 @@
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);
- }
+ // 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();
- jout.flush();
- jout.close();
- jis.close();
- } catch (Exception e) {
- fail("Error writing JAR file for testing: " + e);
+ 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/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java
index 57e4744..42b2543 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java
+++ b/libcore/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.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 @@
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 @@
}
}
+ 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,245 +104,29 @@
}
/**
- * @tests java.util.jar.Manifest#Manifest(java.io.InputStream)
+ * @tests java.util.jar.Manifest#Manifest(java.util.jar.Manifest)
*/
@TestTargetNew(
- level = TestLevel.COMPLETE,
- notes = "IOException checking missed.",
- method = "Manifest",
- args = {java.io.InputStream.class}
+ level = TestLevel.PARTIAL_COMPLETE,
+ notes = "",
+ method = "Manifest",
+ args = {java.util.jar.Manifest.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()
- */
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- notes = "",
- method = "clear",
- args = {}
- )
- 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());
- }
-
- /**
- * @tests java.util.jar.Manifest#getAttributes(java.lang.String)
- */
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- notes = "",
- method = "getAttributes",
- args = {java.lang.String.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")));
- }
-
- /**
- * @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")));
-
- }
-
- /**
- * @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));
- }
-
- /**
- * @tests {@link java.util.jar.Manifest#read(java.io.InputStream)
- */
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- notes = "",
- method = "read",
- 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
- }
-
- 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"));
- }
-
- // helper class
- class InputStreamImpl extends InputStream {
- public InputStreamImpl() {
- super();
- }
-
- @Override
- public int read() {
- return 0;
- }
+ 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#Manifest(Manifest)
*/
@TestTargetNew(
- level = TestLevel.COMPLETE,
- notes = "",
- method = "Manifest",
- args = {java.util.jar.Manifest.class}
+ level = TestLevel.PARTIAL_COMPLETE,
+ notes = "",
+ method = "Manifest",
+ args = {java.util.jar.Manifest.class}
)
public void test_ConstructorLjava_util_jar_Manifest() {
// Test for method java.util.jar.Manifest()
@@ -338,13 +139,101 @@
assertEquals(emptyClone, emptyManifest.clone());
}
+ private void assertAttribute(Attributes attr, String name, String value) {
+ assertEquals("Incorrect " + name, value, attr.getValue(name));
+ }
+
+ 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 java.util.jar.Manifest#Manifest(java.io.InputStream)
+ */
@TestTargetNew(
level = TestLevel.COMPLETE,
- notes = "",
- method = "clone",
- args = {}
+ notes = "IOException checking missed.",
+ method = "Manifest",
+ args = {java.io.InputStream.class}
)
- public void test_clone() {
+ 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(new URL(Support_Resources
+ .getURL(MANIFEST_NAME)).openStream());
+ checkManifest(manifest);
+
+ // 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"));
+
+
+ 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#clear()
+ */
+ @TestTargetNew(
+ level = TestLevel.COMPLETE,
+ notes = "",
+ method = "clear",
+ args = {}
+ )
+ 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 = {}
+ )
+ 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 @@
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 @@
}
@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 @@
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/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java
index 75060bd..1e8ddb4 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java
@@ -25,6 +25,8 @@
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 @@
}
}
- @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/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java
index 9b23b56..b71ce63 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java
@@ -201,8 +201,6 @@
int r = 0;
try {
outGZIP.write(byteArray, 0, 11);
- } catch (ArrayIndexOutOfBoundsException e) {
- r = 1;
} catch (IndexOutOfBoundsException ee) {
r = 1;
}
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java
index 8b89180..6039c5b 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java
@@ -409,6 +409,22 @@
}
/**
+ * @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 @@
}
/**
- * @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/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java
index 5530a2e..c9e7bb8 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java
@@ -67,7 +67,7 @@
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 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 @@
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 @@
}
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 @@
}
/**
+ * @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/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java
index 9a5f63a..8ca551d 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java
@@ -170,11 +170,8 @@
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 @@
} 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 @@
} catch (IOException e2) {
// expected
}
+
+ zip1.close();
}
@Override
diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java
new file mode 100644
index 0000000..d7c2ab8
--- /dev/null
+++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java
@@ -0,0 +1,117 @@
+/*
+ * 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.luni.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * The class contains static {@link java.io.InputStream} utilities.
+ */
+public class InputStreamExposer {
+
+ /**
+ * Provides access to a protected underlying buffer of
+ * <code>ByteArrayInputStream</code>.
+ */
+ private static final Field BAIS_BUF;
+
+ /**
+ * Provides access to a protected position in the underlying buffer of
+ * <code>ByteArrayInputStream</code>.
+ */
+ private static final Field BAIS_POS;
+
+ static {
+ final Field[] f = new Field[2];
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ try {
+ f[0] = ByteArrayInputStream.class.getDeclaredField("buf");
+ f[0].setAccessible(true);
+ f[1] = ByteArrayInputStream.class.getDeclaredField("pos");
+ f[1].setAccessible(true);
+ } catch (NoSuchFieldException nsfe) {
+ throw new InternalError(nsfe.getLocalizedMessage());
+ }
+ return null;
+ }
+ });
+ BAIS_BUF = f[0];
+ BAIS_POS = f[1];
+ }
+
+ /**
+ * Reads all bytes from {@link java.io.ByteArrayInputStream} using its
+ * underlying buffer directly.
+ *
+ * @return an underlying buffer, if a current position is at the buffer
+ * beginning, and an end position is at the buffer end, or a copy of
+ * the underlying buffer part.
+ */
+ private static byte[] expose(ByteArrayInputStream bais) {
+ byte[] buffer, buf;
+ int pos;
+ synchronized (bais) {
+ int available = bais.available();
+ try {
+ buf = (byte[]) BAIS_BUF.get(bais);
+ pos = BAIS_POS.getInt(bais);
+ } catch (IllegalAccessException iae) {
+ throw new InternalError(iae.getLocalizedMessage());
+ }
+ if (pos == 0 && available == buf.length) {
+ buffer = buf;
+ } else {
+ buffer = new byte[available];
+ System.arraycopy(buf, pos, buffer, 0, available);
+ }
+ bais.skip(available);
+ }
+ return buffer;
+ }
+
+ /**
+ * The utility method for reading the whole input stream into a snapshot
+ * buffer. To speed up the access it works with an underlying buffer for a
+ * given {@link java.io.ByteArrayInputStream}.
+ *
+ * @param is
+ * the stream to be read.
+ * @return the snapshot wrapping the buffer where the bytes are read to.
+ * @throws UnsupportedOperationException if the input stream data cannot be exposed
+ */
+ public static byte[] expose(InputStream is) throws IOException, UnsupportedOperationException {
+ // BEGIN android-changed
+ // if (is instanceof ExposedByteArrayInputStream) {
+ // return ((ExposedByteArrayInputStream) is).expose();
+ // }
+
+ if (is.getClass().equals(ByteArrayInputStream.class)) {
+ return expose((ByteArrayInputStream) is);
+ }
+
+ // We don't know how to do this
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java
new file mode 100644
index 0000000..cdfe0c8
--- /dev/null
+++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java
@@ -0,0 +1,103 @@
+/*
+ * 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.luni.util;
+
+import java.lang.ref.SoftReference;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+
+/**
+ * The class extends the functionality of {@link java.lang.ThreadLocal} with
+ * possibility of discarding the thread local storage content when a heap is
+ * exhausted.
+ */
+public class ThreadLocalCache<T> {
+
+ private SoftReference<ThreadLocal<T>> storage = new SoftReference<ThreadLocal<T>>(
+ null);
+
+ private ThreadLocal<T> getThreadLocal() {
+ ThreadLocal<T> tls = storage.get();
+ if (tls == null) {
+ tls = new ThreadLocal<T>() {
+ public T initialValue() {
+ return ThreadLocalCache.this.initialValue();
+ }
+ };
+ storage = new SoftReference<ThreadLocal<T>>(tls);
+ }
+ return tls;
+ }
+
+ /**
+ * Returns the initial value for the cache for the current thread.
+ */
+ protected T initialValue() {
+ return null;
+ }
+
+ /**
+ * Returns the thread local value of this object.
+ */
+ public T get() {
+ return getThreadLocal().get();
+ }
+
+ /**
+ * Sets the value of this variable for the current thread. Might be useful
+ * for expanding the thread local cache.
+ */
+ public void set(T value) {
+ getThreadLocal().set(value);
+ }
+
+ /**
+ * Discards the cache for all threads.
+ */
+ public void remove() {
+ storage.clear();
+ }
+
+ public static ThreadLocalCache<CharsetDecoder> utf8Decoder = new ThreadLocalCache<CharsetDecoder>() {
+ protected CharsetDecoder initialValue() {
+ return Charset.forName("UTF-8").newDecoder();
+ }
+ };
+
+ public static ThreadLocalCache<CharsetEncoder> utf8Encoder = new ThreadLocalCache<CharsetEncoder>() {
+ protected CharsetEncoder initialValue() {
+ return Charset.forName("UTF-8").newEncoder();
+ }
+ };
+
+ public static ThreadLocalCache<java.nio.ByteBuffer> byteBuffer = new ThreadLocalCache<java.nio.ByteBuffer>() {
+ protected java.nio.ByteBuffer initialValue() {
+ return java.nio.ByteBuffer.allocate(72); // >=
+ // Manifest.LINE_LENGTH_LIMIT
+ }
+ };
+
+ public static ThreadLocalCache<CharBuffer> charBuffer = new ThreadLocalCache<CharBuffer>() {
+ protected CharBuffer initialValue() {
+ return CharBuffer.allocate(72); // no specific requirement
+ }
+ };
+
+}