summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NativeCode.mk11
-rw-r--r--dalvik/src/main/java/dalvik/system/VMDebug.java10
-rw-r--r--dom/src/test/java/org/w3c/domts/JAXPDOMTestDocumentBuilderFactory.java5
-rw-r--r--luni/src/main/java/java/io/BufferedWriter.java3
-rw-r--r--luni/src/main/java/java/io/CharArrayWriter.java3
-rw-r--r--luni/src/main/java/java/io/File.java208
-rw-r--r--luni/src/main/java/java/io/ObjectInputStream.java283
-rw-r--r--luni/src/main/java/java/io/ObjectOutputStream.java256
-rw-r--r--luni/src/main/java/java/io/ObjectStreamClass.java129
-rw-r--r--luni/src/main/java/java/io/ObjectStreamField.java67
-rw-r--r--luni/src/main/java/java/io/OutputStreamWriter.java5
-rw-r--r--luni/src/main/java/java/io/Writer.java2
-rw-r--r--luni/src/main/java/java/lang/AbstractStringBuilder.java41
-rw-r--r--luni/src/main/java/java/lang/Class.java31
-rw-r--r--luni/src/main/java/java/lang/ClassCache.java24
-rw-r--r--luni/src/main/java/java/lang/String.java65
-rw-r--r--luni/src/main/java/java/lang/Thread.java3
-rw-r--r--luni/src/main/java/java/lang/Void.java3
-rw-r--r--luni/src/main/java/java/lang/reflect/Array.java6
-rw-r--r--luni/src/main/java/java/lang/reflect/Constructor.java8
-rw-r--r--luni/src/main/java/java/lang/reflect/Field.java31
-rw-r--r--luni/src/main/java/java/lang/reflect/InvocationHandler.java8
-rw-r--r--luni/src/main/java/java/lang/reflect/Method.java88
-rw-r--r--luni/src/main/java/java/net/DatagramPacket.java6
-rw-r--r--luni/src/main/java/java/net/DatagramSocket.java8
-rw-r--r--luni/src/main/java/java/net/Inet6Address.java3
-rw-r--r--luni/src/main/java/java/net/InetSocketAddress.java32
-rw-r--r--luni/src/main/java/java/net/NetworkInterface.java3
-rw-r--r--luni/src/main/java/java/net/Proxy.java12
-rw-r--r--luni/src/main/java/java/net/SocketOptions.java11
-rw-r--r--luni/src/main/java/java/nio/DatagramChannelImpl.java5
-rw-r--r--luni/src/main/java/java/nio/SelectorImpl.java6
-rw-r--r--luni/src/main/java/java/nio/ServerSocketChannelImpl.java2
-rw-r--r--luni/src/main/java/java/security/cert/X509CertSelector.java4
-rw-r--r--luni/src/main/java/java/text/ChoiceFormat.java5
-rw-r--r--luni/src/main/java/java/text/MessageFormat.java3
-rw-r--r--luni/src/main/java/java/util/AbstractQueue.java212
-rw-r--r--luni/src/main/java/java/util/ArrayDeque.java1285
-rw-r--r--luni/src/main/java/java/util/ArrayList.java20
-rw-r--r--luni/src/main/java/java/util/Arrays.java12
-rw-r--r--luni/src/main/java/java/util/Deque.java611
-rw-r--r--luni/src/main/java/java/util/EnumMap.java4
-rw-r--r--luni/src/main/java/java/util/EnumSet.java2
-rw-r--r--luni/src/main/java/java/util/HugeEnumSet.java9
-rw-r--r--luni/src/main/java/java/util/IdentityHashMap.java14
-rw-r--r--luni/src/main/java/java/util/MiniEnumSet.java9
-rw-r--r--luni/src/main/java/java/util/NavigableMap.java488
-rw-r--r--luni/src/main/java/java/util/NavigableSet.java385
-rw-r--r--luni/src/main/java/java/util/Queue.java221
-rw-r--r--luni/src/main/java/java/util/TreeMap.java2
-rw-r--r--luni/src/main/java/java/util/concurrent/SynchronousQueue.java3
-rw-r--r--luni/src/main/java/java/util/jar/Attributes.java2
-rw-r--r--luni/src/main/java/java/util/jar/JarFile.java18
-rw-r--r--luni/src/main/java/java/util/jar/Manifest.java90
-rw-r--r--luni/src/main/java/java/util/prefs/AbstractPreferences.java3
-rw-r--r--luni/src/main/java/java/util/prefs/XMLParser.java23
-rw-r--r--luni/src/main/java/java/util/zip/Adler32.java2
-rw-r--r--luni/src/main/java/java/util/zip/CRC32.java4
-rw-r--r--luni/src/main/java/java/util/zip/Checksum.java3
-rw-r--r--luni/src/main/java/java/util/zip/Deflater.java7
-rw-r--r--luni/src/main/java/java/util/zip/DeflaterOutputStream.java1
-rw-r--r--luni/src/main/java/java/util/zip/GZIPInputStream.java16
-rw-r--r--luni/src/main/java/java/util/zip/GZIPOutputStream.java15
-rw-r--r--luni/src/main/java/java/util/zip/ZipInputStream.java48
-rw-r--r--luni/src/main/java/java/util/zip/ZipOutputStream.java26
-rw-r--r--luni/src/main/java/javax/crypto/spec/PBEKeySpec.java7
-rw-r--r--luni/src/main/java/javax/crypto/spec/PSource.java4
-rw-r--r--luni/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java5
-rw-r--r--luni/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java5
-rw-r--r--luni/src/main/java/libcore/base/EmptyArray.java31
-rw-r--r--luni/src/main/java/libcore/base/Streams.java17
-rw-r--r--luni/src/main/java/libcore/internal/StringPool.java52
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java71
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfoDB.java9
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java4
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/util/Base64.java3
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/util/InputStreamHelper.java191
-rw-r--r--luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java6
-rw-r--r--luni/src/main/java/org/apache/harmony/xml/ExpatPullParser.java2
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java3
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java5
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java11
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java5
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java4
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java8
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSocketFactoryImpl.java6
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java3
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java6
-rw-r--r--luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java3
-rw-r--r--luni/src/main/java/org/apache/xml/serializer/ToHTMLStream.java4
-rw-r--r--luni/src/main/java/org/xml/sax/ext/Attributes2Impl.java6
-rw-r--r--luni/src/main/native/Register.cpp2
-rw-r--r--luni/src/main/native/java_io_File.cpp63
-rw-r--r--luni/src/main/native/java_io_ObjectInputStream.cpp55
-rw-r--r--luni/src/main/native/java_io_ObjectOutputStream.cpp80
-rw-r--r--luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp8
-rw-r--r--luni/src/main/native/readlink.cpp42
-rw-r--r--luni/src/main/native/readlink.h25
-rw-r--r--luni/src/main/native/realpath.cpp125
-rw-r--r--luni/src/main/native/sub.mk3
-rw-r--r--luni/src/test/java/libcore/internal/StringPoolTest.java44
-rw-r--r--luni/src/test/java/libcore/java/io/FileTest.java14
-rw-r--r--luni/src/test/java/libcore/java/lang/ArrayIndexOutOfBoundsExceptionTest.java12
-rw-r--r--luni/src/test/java/libcore/java/lang/ArrayStoreExceptionTest.java33
-rw-r--r--luni/src/test/java/libcore/java/lang/ClassCastExceptionTest.java123
-rw-r--r--luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java11
-rw-r--r--luni/src/test/java/libcore/java/net/URLConnectionTest.java20
-rw-r--r--luni/src/test/java/libcore/java/text/OldSimpleDateFormatTest.java6
-rw-r--r--luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java (renamed from luni/src/test/java/libcore/java/util/zip/GzipTest.java)31
-rw-r--r--luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java50
-rw-r--r--luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java62
-rw-r--r--luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java63
-rw-r--r--luni/src/test/java/libcore/xml/ExpatPullParserTest.java (renamed from luni/src/test/java/org/apache/harmony/xml/ExpatPullParserTest.java)6
-rw-r--r--luni/src/test/java/libcore/xml/ExpatSaxParserTest.java (renamed from luni/src/test/java/org/apache/harmony/xml/ExpatSaxParserTest.java)19
-rw-r--r--luni/src/test/java/libcore/xml/JaxenXPathTestSuite.java (renamed from luni/src/test/java/org/apache/harmony/xml/JaxenXPathTestSuite.java)2
-rw-r--r--luni/src/test/java/libcore/xml/KxmlPullParserTest.java (renamed from luni/src/test/java/org/apache/harmony/xml/KxmlPullParserTest.java)2
-rw-r--r--luni/src/test/java/libcore/xml/NamespacedAttributesLookupTest.java (renamed from luni/src/test/java/org/apache/harmony/xml/NamespacedAttributesLookupTest.java)2
-rw-r--r--luni/src/test/java/libcore/xml/PullParserTest.java716
-rw-r--r--luni/src/test/java/libcore/xml/XsltXPathConformanceTestSuite.java (renamed from luni/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java)2
-rw-r--r--luni/src/test/java/org/apache/harmony/xml/PullParserTest.java181
-rw-r--r--support/src/test/java/tests/net/StuckServer.java57
-rw-r--r--support/src/test/java/tests/resources/GZIPInputStream/hyts_gInput.txt.gzbin42 -> 0 bytes
-rw-r--r--xml/src/main/java/org/kxml2/io/KXmlParser.java1763
-rw-r--r--xml/src/main/java/org/xmlpull/v1/XmlPullParser.java7
124 files changed, 5217 insertions, 3808 deletions
diff --git a/NativeCode.mk b/NativeCode.mk
index 238a875..b5cc769 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -95,15 +95,16 @@ endif
# Define the rules.
LOCAL_SRC_FILES := $(core_src_files)
LOCAL_C_INCLUDES := $(core_c_includes)
-
-ifneq ($(TARGET_SIMULATOR),true)
-LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
-endif
-
LOCAL_SHARED_LIBRARIES := $(core_shared_libraries)
LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libjavacore
+
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
+LOCAL_SHARED_LIBRARIES += libstlport
+endif
+
include $(BUILD_STATIC_LIBRARY)
# Deal with keystores required for security. Note: The path to this file
diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
index 0298680..1eb589e 100644
--- a/dalvik/src/main/java/dalvik/system/VMDebug.java
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -397,9 +397,15 @@ public final class VMDebug {
private static void startClassPrep() {}
/**
- * Returns a count of the extant instances of a class.
+ * Counts the instances of a class.
*
+ * @param klass the class to be counted.
+ * @param assignable if false, direct instances of klass are
+ * counted. If true, instances that are
+ * assignable to klass, as defined by
+ * {@link Class#isAssignableFrom} are counted.
+ * @returns the number of matching instances.
* @hide
*/
- public static native long countInstancesOfClass(Class cls);
+ public static native long countInstancesOfClass(Class klass, boolean assignable);
}
diff --git a/dom/src/test/java/org/w3c/domts/JAXPDOMTestDocumentBuilderFactory.java b/dom/src/test/java/org/w3c/domts/JAXPDOMTestDocumentBuilderFactory.java
index c2d105e..0ca8f1f 100644
--- a/dom/src/test/java/org/w3c/domts/JAXPDOMTestDocumentBuilderFactory.java
+++ b/dom/src/test/java/org/w3c/domts/JAXPDOMTestDocumentBuilderFactory.java
@@ -12,6 +12,7 @@
package org.w3c.domts;
+import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -120,7 +121,9 @@ public class JAXPDOMTestDocumentBuilderFactory
try {
LoadErrorHandler errorHandler = new LoadErrorHandler();
builder.setErrorHandler(errorHandler);
- doc = builder.parse(url.openStream(), url.toString());
+ InputStream stream = url.openStream();
+ doc = builder.parse(stream, url.toString());
+ stream.close();
parseException = errorHandler.getFirstException();
}
catch (Exception ex) {
diff --git a/luni/src/main/java/java/io/BufferedWriter.java b/luni/src/main/java/java/io/BufferedWriter.java
index a91c0fa..e17f735 100644
--- a/luni/src/main/java/java/io/BufferedWriter.java
+++ b/luni/src/main/java/java/io/BufferedWriter.java
@@ -281,7 +281,8 @@ public class BufferedWriter extends Writer {
return;
}
if (offset > str.length() - count || offset < 0) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("str.length=" + str.length()
+ + " offset=" + offset + " count=" + count);
}
if (pos == 0 && count >= buf.length) {
char[] chars = new char[count];
diff --git a/luni/src/main/java/java/io/CharArrayWriter.java b/luni/src/main/java/java/io/CharArrayWriter.java
index 03cfeba..8ac1493 100644
--- a/luni/src/main/java/java/io/CharArrayWriter.java
+++ b/luni/src/main/java/java/io/CharArrayWriter.java
@@ -231,7 +231,8 @@ public class CharArrayWriter extends Writer {
// removed redundant check, used (offset | len) < 0
// instead of (offset < 0) || (len < 0) to safe one operation
if ((offset | len) < 0 || len > str.length() - offset) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("str.length=" + str.length()
+ + " offset=" + offset + " len=" + len);
}
// END android-changed
synchronized (lock) {
diff --git a/luni/src/main/java/java/io/File.java b/luni/src/main/java/java/io/File.java
index ffc20aa..1e1d625 100644
--- a/luni/src/main/java/java/io/File.java
+++ b/luni/src/main/java/java/io/File.java
@@ -105,7 +105,7 @@ public class File implements Serializable, Comparable<File> {
/**
* The path we return from getAbsolutePath, and pass down to native code.
*/
- private String absolutePath;
+ private transient String absolutePath;
static {
// The default protection domain grants access to these properties.
@@ -262,8 +262,8 @@ public class File implements Serializable, Comparable<File> {
/**
* Lists the file system roots. The Java platform may support zero or more
* file systems, each with its own platform-dependent root. Further, the
- * canonical pathname of any file on the system will always begin with one
- * of the returned file system roots.
+ * {@link #getCanonicalPath canonical} path of any file on the system will
+ * always begin with one of the returned file system roots.
*
* @return the array of file system roots.
*/
@@ -436,9 +436,12 @@ public class File implements Serializable, Comparable<File> {
private static native boolean existsImpl(String path);
/**
- * Returns the absolute path of this file.
+ * Returns the absolute path of this file. An absolute path is a path that starts at a root
+ * of the file system. On Android, there is only one root: {@code /}.
*
- * @return the absolute file path.
+ * <p>A common use for absolute paths is when passing paths to a {@code Process} as
+ * command-line arguments, to remove the requirement implied by relative paths, that the
+ * child must have the same working directory as its parent.
*/
public String getAbsolutePath() {
return absolutePath;
@@ -446,191 +449,46 @@ public class File implements Serializable, Comparable<File> {
/**
* Returns a new file constructed using the absolute path of this file.
- *
- * @return a new file from this file's absolute path.
- * @see java.lang.SecurityManager#checkPropertyAccess
+ * Equivalent to {@code new File(this.getAbsolutePath())}.
*/
public File getAbsoluteFile() {
return new File(this.getAbsolutePath());
}
/**
- * Returns the absolute path of this file with all references resolved. An
- * <em>absolute</em> path is one that begins at the root of the file
- * system. The canonical path is one in which all references have been
- * resolved. For the cases of '..' and '.', where the file system supports
- * parent and working directory respectively, these are removed and replaced
- * with a direct directory reference. If the file does not exist,
- * getCanonicalPath() may not resolve any references and simply returns an
- * absolute path name or throws an IOException.
+ * Returns the canonical path of this file.
+ * An <i>absolute</i> path is one that begins at the root of the file system.
+ * A <i>canonical</i> path is an absolute path with symbolic links
+ * and references to "." or ".." resolved. If a path element does not exist (or
+ * is not searchable), there is a conflict between interpreting canonicalization
+ * as a textual operation (where "a/../b" is "b" even if "a" does not exist) .
+ *
+ * <p>Most callers should use {@link #getAbsolutePath} instead. A canonical path is
+ * significantly more expensive to compute, and not generally useful. The primary
+ * use for canonical paths is determining whether two paths point to the same file by
+ * comparing the canonicalized paths.
+ *
+ * <p>It can be actively harmful to use a canonical path, specifically because
+ * canonicalization removes symbolic links. It's wise to assume that a symbolic link
+ * is present for a reason, and that that reason is because the link may need to change.
+ * Canonicalization removes this layer of indirection. Good code should generally avoid
+ * caching canonical paths.
*
* @return the canonical path of this file.
* @throws IOException
* if an I/O error occurs.
*/
public String getCanonicalPath() throws IOException {
- // BEGIN android-removed
- // Caching the canonical path is bogus. Users facing specific
- // performance problems can perform their own caching, with
- // eviction strategies that are appropriate for their application.
- // A VM-wide cache with no mechanism to evict stale elements is a
- // disservice to applications that need up-to-date data.
- // String canonPath = FileCanonPathCache.get(absPath);
- // if (canonPath != null) {
- // return canonPath;
- // }
- // END android-removed
-
- // TODO: rewrite getCanonicalPath, resolve, and resolveLink.
-
- String result = absolutePath;
- if (separatorChar == '/') {
- // resolve the full path first
- result = resolveLink(result, result.length(), false);
- // resolve the parent directories
- result = resolve(result);
- }
- int numSeparators = 1;
- for (int i = 0; i < result.length(); ++i) {
- if (result.charAt(i) == separatorChar) {
- numSeparators++;
- }
- }
- int[] sepLocations = new int[numSeparators];
- int rootLoc = 0;
- if (separatorChar != '/') {
- if (result.charAt(0) == '\\') {
- rootLoc = (result.length() > 1 && result.charAt(1) == '\\') ? 1 : 0;
- } else {
- rootLoc = 2; // skip drive i.e. c:
- }
- }
-
- char[] newResult = new char[result.length() + 1];
- int newLength = 0, lastSlash = 0, foundDots = 0;
- sepLocations[lastSlash] = rootLoc;
- for (int i = 0; i <= result.length(); ++i) {
- if (i < rootLoc) {
- newResult[newLength++] = result.charAt(i);
- } else {
- if (i == result.length() || result.charAt(i) == separatorChar) {
- if (i == result.length() && foundDots == 0) {
- break;
- }
- if (foundDots == 1) {
- /* Don't write anything, just reset and continue */
- foundDots = 0;
- continue;
- }
- if (foundDots > 1) {
- /* Go back N levels */
- lastSlash = lastSlash > (foundDots - 1) ? lastSlash - (foundDots - 1) : 0;
- newLength = sepLocations[lastSlash] + 1;
- foundDots = 0;
- continue;
- }
- sepLocations[++lastSlash] = newLength;
- newResult[newLength++] = separatorChar;
- continue;
- }
- if (result.charAt(i) == '.') {
- foundDots++;
- continue;
- }
- /* Found some dots within text, write them out */
- if (foundDots > 0) {
- for (int j = 0; j < foundDots; j++) {
- newResult[newLength++] = '.';
- }
- }
- newResult[newLength++] = result.charAt(i);
- foundDots = 0;
- }
- }
- // remove trailing slash
- if (newLength > (rootLoc + 1) && newResult[newLength - 1] == separatorChar) {
- newLength--;
- }
- return new String(newResult, 0, newLength);
- }
-
- /*
- * Resolve symbolic links in the parent directories.
- */
- private static String resolve(String path) throws IOException {
- int last = 1;
- String linkPath = path;
- String bytes;
- boolean done;
- for (int i = 1; i <= path.length(); i++) {
- if (i == path.length() || path.charAt(i) == separatorChar) {
- done = i >= path.length() - 1;
- // if there is only one segment, do nothing
- if (done && linkPath.length() == 1) {
- return path;
- }
- boolean inPlace = false;
- if (linkPath.equals(path)) {
- bytes = path;
- // if there are no symbolic links, truncate the path instead of copying
- if (!done) {
- inPlace = true;
- path = path.substring(0, i);
- }
- } else {
- int nextSize = i - last + 1;
- int linkSize = linkPath.length();
- if (linkPath.charAt(linkSize - 1) == separatorChar) {
- linkSize--;
- }
- bytes = linkPath.substring(0, linkSize) +
- path.substring(last - 1, last - 1 + nextSize);
- // the full path has already been resolved
- }
- if (done) {
- return bytes;
- }
- linkPath = resolveLink(bytes, inPlace ? i : bytes.length(), true);
- if (inPlace) {
- // path[i] = '/';
- path = path.substring(0, i) + '/' + (i + 1 < path.length() ? path.substring(i + 1) : "");
- }
- last = i + 1;
- }
- }
- throw new InternalError();
+ return realpath(absolutePath);
}
- /*
- * Resolve a symbolic link. While the path resolves to an existing path,
- * keep resolving. If an absolute link is found, resolve the parent
- * directories if resolveAbsolute is true.
+ /**
+ * TODO: move this stuff to libcore.os.
+ * @hide
*/
- private static String resolveLink(String path, int length, boolean resolveAbsolute) throws IOException {
- boolean restart = false;
- do {
- String fragment = path.substring(0, length);
- String target = readlink(fragment);
- if (target.equals(fragment)) {
- break;
- }
- if (target.charAt(0) == separatorChar) {
- // The link target was an absolute path, so we may need to start again.
- restart = resolveAbsolute;
- path = target + path.substring(length);
- } else {
- path = path.substring(0, path.lastIndexOf(separatorChar, length - 1) + 1) + target;
- }
- length = path.length();
- } while (existsImpl(path));
- // resolve the parent directories
- if (restart) {
- return resolve(path);
- }
- return path;
- }
-
- private static native String readlink(String filePath);
+ public static native void symlink(String oldPath, String newPath);
+ private static native String realpath(String path);
+ private static native String readlink(String path);
/**
* Returns a new file created using the canonical path of this file.
diff --git a/luni/src/main/java/java/io/ObjectInputStream.java b/luni/src/main/java/java/io/ObjectInputStream.java
index 25e5574..aa9e7e1 100644
--- a/luni/src/main/java/java/io/ObjectInputStream.java
+++ b/luni/src/main/java/java/io/ObjectInputStream.java
@@ -26,6 +26,7 @@ import dalvik.system.VMStack;
import java.io.EmulatedFields.ObjectSlot;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -35,6 +36,7 @@ import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import libcore.base.EmptyArray;
import org.apache.harmony.luni.util.PriviAction;
/**
@@ -53,8 +55,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
// BEGIN android-note
// this is non-static to avoid sync contention. Would static be faster?
// END android-note
- private InputStream emptyStream = new ByteArrayInputStream(
- new byte[0]);
+ private InputStream emptyStream = new ByteArrayInputStream(EmptyArray.BYTE);
// To put into objectsRead when reading unsharedObject
private static final Object UNSHARED_OBJ = new Object(); // $NON-LOCK-1$
@@ -392,20 +393,16 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
try {
- Method method = implementationClass
- .getMethod(
- "readFields",
- ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES);
+ Method method = implementationClass.getMethod("readFields",
+ EmptyArray.CLASS);
if (method.getDeclaringClass() != thisClass) {
return Boolean.TRUE;
}
} catch (NoSuchMethodException ignored) {
}
try {
- Method method = implementationClass
- .getMethod(
- "readUnshared",
- ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES);
+ Method method = implementationClass.getMethod("readUnshared",
+ EmptyArray.CLASS);
if (method.getDeclaringClass() != thisClass) {
return Boolean.TRUE;
}
@@ -471,8 +468,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
primitiveData = new ByteArrayInputStream(readBlockData());
return;
case TC_BLOCKDATALONG:
- primitiveData = new ByteArrayInputStream(
- readBlockDataLong());
+ primitiveData = new ByteArrayInputStream(readBlockDataLong());
return;
case TC_RESET:
resetState();
@@ -811,9 +807,8 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
return readNewClassDesc(false);
case TC_PROXYCLASSDESC:
Class<?> proxyClass = readNewProxyClassDesc();
- ObjectStreamClass streamClass = ObjectStreamClass
- .lookup(proxyClass);
- streamClass.setLoadFields(new ObjectStreamField[0]);
+ ObjectStreamClass streamClass = ObjectStreamClass.lookup(proxyClass);
+ streamClass.setLoadFields(ObjectStreamClass.NO_FIELDS);
registerObjectRead(streamClass, nextHandle(), false);
checkedSetSuperClassDesc(streamClass, readClassDesc());
return streamClass;
@@ -1116,8 +1111,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
* @throws NotActiveException
* if this stream is currently not reading an object.
*/
- public GetField readFields() throws IOException, ClassNotFoundException,
- NotActiveException {
+ public GetField readFields() throws IOException, ClassNotFoundException, NotActiveException {
// We can't be called from just anywhere. There are rules.
if (currentObject == null) {
throw new NotActiveException();
@@ -1150,8 +1144,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
*/
private void readFieldValues(EmulatedFieldsForLoading emulatedFields)
throws OptionalDataException, InvalidClassException, IOException {
- EmulatedFields.ObjectSlot[] slots = emulatedFields.emulatedFields()
- .slots();
+ EmulatedFields.ObjectSlot[] slots = emulatedFields.emulatedFields().slots();
for (ObjectSlot element : slots) {
element.defaulted = false;
Class<?> type = element.field.getType();
@@ -1212,111 +1205,109 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
* @see #readFields
* @see #readObject()
*/
- private void readFieldValues(Object obj, ObjectStreamClass classDesc)
- throws OptionalDataException, ClassNotFoundException, IOException {
+ private void readFieldValues(Object obj, ObjectStreamClass classDesc) throws OptionalDataException, ClassNotFoundException, IOException {
// Now we must read all fields and assign them to the receiver
ObjectStreamField[] fields = classDesc.getLoadFields();
- fields = (null == fields ? new ObjectStreamField[] {} : fields);
+ fields = (null == fields ? ObjectStreamClass.NO_FIELDS : fields);
Class<?> declaringClass = classDesc.forClass();
if (declaringClass == null && mustResolve) {
throw new ClassNotFoundException(classDesc.getName());
}
for (ObjectStreamField fieldDesc : fields) {
-
- // BEGIN android-removed
- // // get associated Field
- // long fieldID = fieldDesc.getFieldID(accessor, declaringClass);
- // END android-removed
-
- // Code duplication starts, just because Java is typed
- if (fieldDesc.isPrimitive()) {
- try {
- // BEGIN android-changed
- switch (fieldDesc.getTypeCode()) {
- case 'B':
- setFieldByte(obj, declaringClass, fieldDesc.getName(),
- input.readByte());
- break;
- case 'C':
- setFieldChar(obj, declaringClass, fieldDesc.getName(),
- input.readChar());
- break;
- case 'D':
- setFieldDouble(obj, declaringClass, fieldDesc.getName(),
- input.readDouble());
- break;
- case 'F':
- setFieldFloat(obj, declaringClass, fieldDesc.getName(),
- input.readFloat());
- break;
- case 'I':
- setFieldInt(obj, declaringClass, fieldDesc.getName(),
- input.readInt());
- break;
- case 'J':
- setFieldLong(obj, declaringClass, fieldDesc.getName(),
- input.readLong());
- break;
- case 'S':
- setFieldShort(obj, declaringClass, fieldDesc.getName(),
- input.readShort());
- break;
- case 'Z':
- setFieldBool(obj, declaringClass, fieldDesc.getName(),
- input.readBoolean());
- break;
- default:
- throw new StreamCorruptedException("Invalid typecode: " +
- fieldDesc.getTypeCode());
+ Field field = classDesc.getReflectionField(fieldDesc);
+ // We may not have been able to find the field, but we still need to read the value
+ // and do the other checking, so there's no null check on 'field' here.
+ try {
+ switch (fieldDesc.getTypeCode()) {
+ case 'B':
+ byte b = input.readByte();
+ if (field != null) {
+ field.setByte(obj, b);
}
- // END android-changed
- } catch (NoSuchFieldError ignored) {
- }
- } else {
- // Object type (array included).
- String fieldName = fieldDesc.getName();
- boolean setBack = false;
- // BEGIN android-added
- ObjectStreamField field = classDesc.getField(fieldName);
- // END android-added
- if (mustResolve && fieldDesc == null) {
- setBack = true;
- mustResolve = false;
- }
- Object toSet;
- if (fieldDesc != null && fieldDesc.isUnshared()) {
- toSet = readUnshared();
- } else {
- toSet = readObject();
- }
- if (setBack) {
- mustResolve = true;
- }
- if (fieldDesc != null) {
- if (toSet != null) {
- // BEGIN android-changed
- // Get the field type from the local field rather than
- // from the stream's supplied data. That's the field
- // we'll be setting, so that's the one that needs to be
- // validated.
- Class<?> fieldType = field.getTypeInternal();
- // END android-added
- Class<?> valueType = toSet.getClass();
- if (!fieldType.isAssignableFrom(valueType)) {
- throw new ClassCastException(classDesc.getName() + "." + fieldName +
- " - " + fieldType + " not compatible with " + valueType);
- }
- try {
+ break;
+ case 'C':
+ char c = input.readChar();
+ if (field != null) {
+ field.setChar(obj, c);
+ }
+ break;
+ case 'D':
+ double d = input.readDouble();
+ if (field != null) {
+ field.setDouble(obj, d);
+ }
+ break;
+ case 'F':
+ float f = input.readFloat();
+ if (field != null) {
+ field.setFloat(obj, f);
+ }
+ break;
+ case 'I':
+ int i = input.readInt();
+ if (field != null) {
+ field.setInt(obj, i);
+ }
+ break;
+ case 'J':
+ long j = input.readLong();
+ if (field != null) {
+ field.setLong(obj, j);
+ }
+ break;
+ case 'S':
+ short s = input.readShort();
+ if (field != null) {
+ field.setShort(obj, s);
+ }
+ break;
+ case 'Z':
+ boolean z = input.readBoolean();
+ if (field != null) {
+ field.setBoolean(obj, z);
+ }
+ break;
+ case 'L':
+ case '[':
+ String fieldName = fieldDesc.getName();
+ boolean setBack = false;
+ ObjectStreamField localFieldDesc = classDesc.getField(fieldName);
+ if (mustResolve && fieldDesc == null) {
+ setBack = true;
+ mustResolve = false;
+ }
+ boolean unshared = fieldDesc != null && fieldDesc.isUnshared();
+ Object toSet = unshared ? readUnshared() : readObject();
+ if (setBack) {
+ mustResolve = true;
+ }
+ if (fieldDesc != null) {
+ if (toSet != null) {
// BEGIN android-changed
- setFieldObject(obj, declaringClass, fieldName, field.getTypeString(),
- toSet);
- // END android-changed
- } catch (NoSuchFieldError e) {
- // Ignored
+ // Get the field type from the local field rather than
+ // from the stream's supplied data. That's the field
+ // we'll be setting, so that's the one that needs to be
+ // validated.
+ Class<?> fieldType = localFieldDesc.getTypeInternal();
+ // END android-added
+ Class<?> valueType = toSet.getClass();
+ if (!fieldType.isAssignableFrom(valueType)) {
+ throw new ClassCastException(classDesc.getName() + "." + fieldName + " - " + fieldType + " not compatible with " + valueType);
+ }
+ if (field != null) {
+ field.set(obj, toSet);
+ }
}
}
+ break;
+ default:
+ throw new StreamCorruptedException("Invalid typecode: " + fieldDesc.getTypeCode());
}
+ } catch (IllegalAccessException iae) {
+ // ObjectStreamField should have called setAccessible(true).
+ throw new AssertionError(iae);
+ } catch (NoSuchFieldError ignored) {
}
}
}
@@ -1405,8 +1396,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
throw new NotActiveException();
}
- ArrayList<ObjectStreamClass> streamClassList = new ArrayList<ObjectStreamClass>(
- 32);
+ ArrayList<ObjectStreamClass> streamClassList = new ArrayList<ObjectStreamClass>(32);
ObjectStreamClass nextStreamClass = classDesc;
while (nextStreamClass != null) {
streamClassList.add(0, nextStreamClass);
@@ -1827,7 +1817,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
// Resolve the field signatures using the class loader of the
// resolved class
ObjectStreamField[] fields = newClassDesc.getLoadFields();
- fields = (null == fields ? new ObjectStreamField[] {} : fields);
+ fields = (null == fields ? ObjectStreamClass.NO_FIELDS : fields);
ClassLoader loader = newClassDesc.forClass() == null ? callerClassLoader
: newClassDesc.forClass().getClassLoader();
for (ObjectStreamField element : fields) {
@@ -1966,8 +1956,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
Constructor<?> constructor = null;
if (constructorClass != null) {
try {
- constructor = constructorClass
- .getDeclaredConstructor(ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES);
+ constructor = constructorClass.getDeclaredConstructor(EmptyArray.CLASS);
} catch (NoSuchMethodException nsmEx) {
// Ignored
}
@@ -2052,15 +2041,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
Object result, registeredResult = null;
if (objectClass != null) {
-
- // BEGIN android-changed
- // long constructor = classDesc.getConstructor();
- // if (constructor == ObjectStreamClass.CONSTRUCTOR_IS_NOT_RESOLVED) {
- // constructor = accessor.getMethodID(resolveConstructorClass(objectClass, wasSerializable, wasExternalizable), null, new Class[0]);
- // classDesc.setConstructor(constructor);
- // }
Class constructorClass = resolveConstructorClass(objectClass, wasSerializable, wasExternalizable);
- // END android-changed
// Now we know which class to instantiate and which constructor to
// run. We are allowed to run the constructor.
@@ -2582,60 +2563,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput,
return object;
}
- // BEGIN android-added
-
- /*
- * These methods set the value of a field named fieldName of instance. The
- * field is declared by declaringClass. The field is the same type as the
- * value parameter.
- *
- * these methods could be implemented non-natively on top of
- * java.lang.reflect at the expense of extra object creation
- * (java.lang.reflect.Field). Otherwise Serialization could not fetch
- * private fields, except by the use of a native method like this one.
- *
- * @throws NoSuchFieldError If the field does not exist.
- */
- private static native void setFieldByte(Object instance,
- Class<?> declaringClass, String fieldName, byte value)
- throws NoSuchFieldError;
-
-
- private static native void setFieldChar(Object instance,
- Class<?> declaringClass, String fieldName, char value)
- throws NoSuchFieldError;
-
-
- private static native void setFieldDouble(Object instance,
- Class<?> declaringClass, String fieldName, double value)
- throws NoSuchFieldError;
-
- private static native void setFieldFloat(Object instance,
- Class<?> declaringClass, String fieldName, float value)
- throws NoSuchFieldError;
-
- private static native void setFieldInt(Object instance,
- Class<?> declaringClass, String fieldName, int value)
- throws NoSuchFieldError;
-
- private static native void setFieldLong(Object instance,
- Class<?> declaringClass, String fieldName, long value)
- throws NoSuchFieldError;
-
- private static native void setFieldObject(Object instance,
- Class<?> declaringClass, String fieldName, String fieldTypeName,
- Object value) throws NoSuchFieldError;
-
- private static native void setFieldShort(Object instance,
- Class<?> declaringClass, String fieldName, short value)
- throws NoSuchFieldError;
-
- private static native void setFieldBool(Object instance,
- Class<?> declaringClass, String fieldName, boolean value)
- throws NoSuchFieldError;
-
- // END android-added
-
/**
* Skips {@code length} bytes on the source stream. This method should not
* be used to skip bytes at any arbitrary position, just when reading
diff --git a/luni/src/main/java/java/io/ObjectOutputStream.java b/luni/src/main/java/java/io/ObjectOutputStream.java
index 2c5eb7a..e2e5c75 100644
--- a/luni/src/main/java/java/io/ObjectOutputStream.java
+++ b/luni/src/main/java/java/io/ObjectOutputStream.java
@@ -17,20 +17,17 @@
package java.io;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.ByteOrder;
import java.nio.charset.ModifiedUtf8;
import java.util.IdentityHashMap;
+import libcore.base.EmptyArray;
import libcore.io.SizeOf;
import org.apache.harmony.luni.platform.OSMemory;
-// BEGIN android-note
-// Harmony uses ObjectAccessors to access fields through JNI. Android has not
-// yet migrated that API. As a consequence, there's a lot of changes here...
-// END android-note
-
/**
* A specialized {@link OutputStream} that is able to write (serialize) Java
* objects as well as primitive data types (int, byte, char etc.). The data can
@@ -44,6 +41,8 @@ import org.apache.harmony.luni.platform.OSMemory;
public class ObjectOutputStream extends OutputStream implements ObjectOutput,
ObjectStreamConstants {
+ private static final Class<?>[] WRITE_UNSHARED_PARAM_TYPES = new Class[] { Object.class };
+
/*
* Mask to zero SC_BLOC_DATA bit.
*/
@@ -289,16 +288,14 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput,
if (implementationClass != thisClass) {
boolean mustCheck = false;
try {
- Method method = implementationClass.getMethod("putFields",
- ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES);
+ Method method = implementationClass.getMethod("putFields", EmptyArray.CLASS);
mustCheck = method.getDeclaringClass() != thisClass;
} catch (NoSuchMethodException e) {
}
if (!mustCheck) {
try {
- Method method = implementationClass.getMethod(
- "writeUnshared",
- ObjectStreamClass.UNSHARED_PARAM_TYPES);
+ Method method = implementationClass.getMethod("writeUnshared",
+ WRITE_UNSHARED_PARAM_TYPES);
mustCheck = method.getDeclaringClass() != thisClass;
} catch (NoSuchMethodException e) {
}
@@ -523,7 +520,6 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput,
output.flush();
}
- // BEGIN android-added
/*
* These methods get the value of a field named fieldName of object
* instance. The field is declared by declaringClass. The field is the same
@@ -536,34 +532,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput,
*
* @throws NoSuchFieldError If the field does not exist.
*/
-
- private static native boolean getFieldBool(Object instance,
- Class<?> declaringClass, String fieldName);
-
- private static native byte getFieldByte(Object instance,
- Class<?> declaringClass, String fieldName);
-
- private static native char getFieldChar(Object instance,
- Class<?> declaringClass, String fieldName);
-
- private static native double getFieldDouble(Object instance,
- Class<?> declaringClass, String fieldName);
-
- private static native float getFieldFloat(Object instance,
- Class<?> declaringClass, String fieldName);
-
- private static native int getFieldInt(Object instance,
- Class<?> declaringClass, String fieldName);
-
- private static native long getFieldLong(Object instance,
- Class<?> declaringClass, String fieldName);
-
- private static native Object getFieldObj(Object instance,
- Class<?> declaringClass, String fieldName, String fieldTypeName);
-
- private static native short getFieldShort(Object instance,
- Class<?> declaringClass, String fieldName);
- // END android-added
+ private static native Object getFieldL(Object instance, Class<?> declaringClass, String fieldName, String fieldTypeName);
/**
* Return the next <code>Integer</code> handle to be used to indicate cyclic
@@ -1025,11 +994,9 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput,
* @see #writeFields
* @see #writeObject(Object)
*/
- private void writeFieldValues(EmulatedFieldsForDumping emulatedFields)
- throws IOException {
- EmulatedFields accessibleSimulatedFields = emulatedFields
- .emulatedFields(); // Access internal fields which we can
- // set/get. Users can't do this.
+ private void writeFieldValues(EmulatedFieldsForDumping emulatedFields) throws IOException {
+ // Access internal fields which we can set/get. Users can't do this.
+ EmulatedFields accessibleSimulatedFields = emulatedFields.emulatedFields();
EmulatedFields.ObjectSlot[] slots = accessibleSimulatedFields.slots();
for (int i = 0; i < slots.length; i++) {
EmulatedFields.ObjectSlot slot = slots[i];
@@ -1037,29 +1004,21 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput,
Class<?> type = slot.getField().getType();
// WARNING - default values exist for each primitive type
if (type == Integer.TYPE) {
- output.writeInt(fieldValue != null ? ((Integer) fieldValue)
- .intValue() : 0);
+ output.writeInt(fieldValue != null ? ((Integer) fieldValue).intValue() : 0);
} else if (type == Byte.TYPE) {
- output.writeByte(fieldValue != null ? ((Byte) fieldValue)
- .byteValue() : (byte) 0);
+ output.writeByte(fieldValue != null ? ((Byte) fieldValue).byteValue() : (byte) 0);
} else if (type == Character.TYPE) {
- output.writeChar(fieldValue != null ? ((Character) fieldValue)
- .charValue() : (char) 0);
+ output.writeChar(fieldValue != null ? ((Character) fieldValue).charValue() : (char) 0);
} else if (type == Short.TYPE) {
- output.writeShort(fieldValue != null ? ((Short) fieldValue)
- .shortValue() : (short) 0);
+ output.writeShort(fieldValue != null ? ((Short) fieldValue).shortValue() : (short) 0);
} else if (type == Boolean.TYPE) {
- output.writeBoolean(fieldValue != null ? ((Boolean) fieldValue)
- .booleanValue() : false);
+ output.writeBoolean(fieldValue != null ? ((Boolean) fieldValue).booleanValue() : false);
} else if (type == Long.TYPE) {
- output.writeLong(fieldValue != null ? ((Long) fieldValue)
- .longValue() : (long) 0);
+ output.writeLong(fieldValue != null ? ((Long) fieldValue).longValue() : (long) 0);
} else if (type == Float.TYPE) {
- output.writeFloat(fieldValue != null ? ((Float) fieldValue)
- .floatValue() : (float) 0);
+ output.writeFloat(fieldValue != null ? ((Float) fieldValue).floatValue() : (float) 0);
} else if (type == Double.TYPE) {
- output.writeDouble(fieldValue != null ? ((Double) fieldValue)
- .doubleValue() : (double) 0);
+ output.writeDouble(fieldValue != null ? ((Double) fieldValue).doubleValue() : (double) 0);
} else {
// Either array or Object
writeObject(fieldValue);
@@ -1067,7 +1026,6 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput,
}
}
-
/**
* Writes a collection of field values for the fields described by class
* descriptor {@code classDesc} (an {@code ObjectStreamClass}).
@@ -1086,72 +1044,59 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput,
*
* @see #writeObject(Object)
*/
- private void writeFieldValues(Object obj, ObjectStreamClass classDesc)
- throws IOException {
- ObjectStreamField[] fields = classDesc.fields();
+ private void writeFieldValues(Object obj, ObjectStreamClass classDesc) throws IOException {
Class<?> declaringClass = classDesc.forClass();
- for(ObjectStreamField fieldDesc : fields) {
+ for (ObjectStreamField fieldDesc : classDesc.fields()) {
try {
-
- // BEGIN android-changed
- // // get associated Field
- // long fieldID = fieldDesc.getFieldID(accessor, declaringClass);
-
- // Code duplication starts, just because Java is typed
- if (fieldDesc.isPrimitive()) {
- switch (fieldDesc.getTypeCode()) {
- case 'B':
- output.writeByte(getFieldByte(obj, declaringClass,
- fieldDesc.getName()));
- break;
- case 'C':
- output.writeChar(getFieldChar(obj, declaringClass,
- fieldDesc.getName()));
- break;
- case 'D':
- output.writeDouble(getFieldDouble(obj,
- declaringClass, fieldDesc.getName()));
- break;
- case 'F':
- output.writeFloat(getFieldFloat(obj,
- declaringClass, fieldDesc.getName()));
- break;
- case 'I':
- output.writeInt(getFieldInt(obj, declaringClass,
- fieldDesc.getName()));
- break;
- case 'J':
- output.writeLong(getFieldLong(obj, declaringClass,
- fieldDesc.getName()));
- break;
- case 'S':
- output.writeShort(getFieldShort(obj,
- declaringClass, fieldDesc.getName()));
- break;
- case 'Z':
- output.writeBoolean(getFieldBool(obj,
- declaringClass, fieldDesc.getName()));
- break;
- default:
- throw new IOException("Invalid typecode: " +
- fieldDesc.getTypeCode());
- }
- } else {
- // Object type (array included).
- Object objField = getFieldObj(obj, declaringClass, fieldDesc
- .getName(), fieldDesc.getTypeString());
+ Field field = classDesc.getReflectionField(fieldDesc);
+ if (field == null) {
+ throw new InvalidClassException(classDesc.getName() + " doesn't have a field " + fieldDesc.getName() + " of type " + fieldDesc.getTypeCode());
+ }
+ switch (fieldDesc.getTypeCode()) {
+ case 'B':
+ output.writeByte(field.getByte(obj));
+ break;
+ case 'C':
+ output.writeChar(field.getChar(obj));
+ break;
+ case 'D':
+ output.writeDouble(field.getDouble(obj));
+ break;
+ case 'F':
+ output.writeFloat(field.getFloat(obj));
+ break;
+ case 'I':
+ output.writeInt(field.getInt(obj));
+ break;
+ case 'J':
+ output.writeLong(field.getLong(obj));
+ break;
+ case 'S':
+ output.writeShort(field.getShort(obj));
+ break;
+ case 'Z':
+ output.writeBoolean(field.getBoolean(obj));
+ break;
+ case 'L':
+ case '[':
+ // Reference types ('L' and '[').
+ Object objField = field.get(obj);
if (fieldDesc.isUnshared()) {
writeUnshared(objField);
} else {
writeObject(objField);
}
+ break;
+ default:
+ throw new IOException("Invalid typecode: " + fieldDesc.getTypeCode());
}
- // END android-changed
+ } catch (IllegalAccessException iae) {
+ // ObjectStreamField should have called setAccessible(true).
+ throw new AssertionError(iae);
} catch (NoSuchFieldError nsf) {
// The user defined serialPersistentFields but did not provide
- // the glue to transfer values,
- // (in writeObject) so we end up using the default mechanism and
- // fail to set the emulated field
+ // the glue to transfer values in writeObject, so we ended up using
+ // the default mechanism but failed to set the emulated field.
throw new InvalidClassException(classDesc.getName());
}
}
@@ -1673,40 +1618,39 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput,
writeObject(object, true);
}
- private void writeObject(Object object, boolean unshared)
- throws IOException {
+ private void writeObject(Object object, boolean unshared) throws IOException {
boolean setOutput = (primitiveTypes == output);
if (setOutput) {
primitiveTypes = null;
}
- // This is the spec'ed behavior in JDK 1.2. Very bizarre way to allow
+ // This is the specified behavior in JDK 1.2. Very bizarre way to allow
// behavior overriding.
if (subclassOverridingImplementation && !unshared) {
writeObjectOverride(object);
- } else {
+ return;
+ }
- try {
- // First we need to flush primitive types if they were written
- drain();
- // Actual work, and class-based replacement should be computed
- // if needed.
- writeObjectInternal(object, unshared, true, true);
- if (setOutput) {
- primitiveTypes = output;
- }
- } catch (IOException ioEx1) {
- // This will make it pass through until the top caller. It also
- // lets it pass through the nested exception.
- if (nestedLevels == 0 && ioEx1 != nestedException) {
- try {
- writeNewException(ioEx1);
- } catch (IOException ioEx2) {
- nestedException.fillInStackTrace();
- throw nestedException;
- }
+ try {
+ // First we need to flush primitive types if they were written
+ drain();
+ // Actual work, and class-based replacement should be computed
+ // if needed.
+ writeObjectInternal(object, unshared, true, true);
+ if (setOutput) {
+ primitiveTypes = output;
+ }
+ } catch (IOException ioEx1) {
+ // This will make it pass through until the top caller. It also
+ // lets it pass through the nested exception.
+ if (nestedLevels == 0 && ioEx1 != nestedException) {
+ try {
+ writeNewException(ioEx1);
+ } catch (IOException ioEx2) {
+ nestedException.fillInStackTrace();
+ throw nestedException;
}
- throw ioEx1; // and then we propagate the original exception
}
+ throw ioEx1; // and then we propagate the original exception
}
}
@@ -1906,8 +1850,8 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput,
return classDesc;
}
- private Integer writeNewEnum(Object object, Class<?> theClass,
- boolean unshared) throws IOException {
+ private Integer writeNewEnum(Object object, Class<?> theClass, boolean unshared)
+ throws IOException {
// write new Enum
EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save
// null it, to make sure one will be computed if needed
@@ -1932,18 +1876,22 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput,
Class<?> declaringClass = classDesc.getSuperclass().forClass();
// Only write field "name" for enum class, which is the second field of
// enum, that is fields[1]. Ignore all non-fields and fields.length < 2
- if (null != fields && fields.length > 1) {
- // BEGIN android-changed
- String str = (String) getFieldObj(object, declaringClass, fields[1]
- .getName(), fields[1].getTypeString());
- // END android-changed
-
- Integer strhandle = null;
- if (!unshared) {
- strhandle = dumpCycle(str);
+ if (fields != null && fields.length > 1) {
+ Field field = classDesc.getReflectionField(fields[1]);
+ if (field == null) {
+ throw new NoSuchFieldError();
}
- if (null == strhandle) {
- writeNewString(str, unshared);
+ try {
+ String str = (String) field.get(object);
+ Integer strHandle = null;
+ if (!unshared) {
+ strHandle = dumpCycle(str);
+ }
+ if (strHandle == null) {
+ writeNewString(str, unshared);
+ }
+ } catch (IllegalAccessException iae) {
+ throw new AssertionError(iae);
}
}
diff --git a/luni/src/main/java/java/io/ObjectStreamClass.java b/luni/src/main/java/java/io/ObjectStreamClass.java
index 38bce57..7317d97 100644
--- a/luni/src/main/java/java/io/ObjectStreamClass.java
+++ b/luni/src/main/java/java/io/ObjectStreamClass.java
@@ -31,8 +31,10 @@ import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
import java.util.WeakHashMap;
+import libcore.base.EmptyArray;
import org.apache.harmony.luni.platform.OSMemory;
import org.apache.harmony.luni.util.PriviAction;
@@ -57,42 +59,19 @@ public class ObjectStreamClass implements Serializable {
static final long CONSTRUCTOR_IS_NOT_RESOLVED = -1;
- private static final int CLASS_MODIFIERS_MASK;
+ private static final int CLASS_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.FINAL |
+ Modifier.INTERFACE | Modifier.ABSTRACT;
- private static final int FIELD_MODIFIERS_MASK;
+ private static final int FIELD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE |
+ Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE |
+ Modifier.TRANSIENT;
- private static final int METHOD_MODIFIERS_MASK;
+ private static final int METHOD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE |
+ Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED |
+ Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT;
- private static final Class<?>[] READ_PARAM_TYPES;
-
- private static final Class<?>[] WRITE_PARAM_TYPES;
-
- static final Class<?>[] EMPTY_CONSTRUCTOR_PARAM_TYPES;
-
- private static final Class<Void> VOID_CLASS;
-
- static final Class<?>[] UNSHARED_PARAM_TYPES;
-
- static {
- CLASS_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.FINAL
- | Modifier.INTERFACE | Modifier.ABSTRACT;
- FIELD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE
- | Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL
- | Modifier.VOLATILE | Modifier.TRANSIENT;
- METHOD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE
- | Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL
- | Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.ABSTRACT
- | Modifier.STRICT;
-
- READ_PARAM_TYPES = new Class[1];
- WRITE_PARAM_TYPES = new Class[1];
- READ_PARAM_TYPES[0] = ObjectInputStream.class;
- WRITE_PARAM_TYPES[0] = ObjectOutputStream.class;
- EMPTY_CONSTRUCTOR_PARAM_TYPES = new Class[0];
- VOID_CLASS = Void.TYPE;
- UNSHARED_PARAM_TYPES = new Class[1];
- UNSHARED_PARAM_TYPES[0] = Object.class;
- }
+ private static final Class<?>[] READ_PARAM_TYPES = new Class[] { ObjectInputStream.class };
+ private static final Class<?>[] WRITE_PARAM_TYPES = new Class[] { ObjectOutputStream.class };
/**
* Constant indicating that the class has no Serializable fields.
@@ -190,6 +169,11 @@ public class ObjectStreamClass implements Serializable {
// Array of ObjectStreamField describing the serialized fields of this class
private transient ObjectStreamField[] loadFields;
+ // ObjectStreamField doesn't override hashCode or equals, so this is equivalent to an
+ // IdentityHashMap, which is fine for our purposes.
+ private transient HashMap<ObjectStreamField, Field> reflectionFields =
+ new HashMap<ObjectStreamField, Field>();
+
// MethodID for deserialization constructor
private transient long constructor = CONSTRUCTOR_IS_NOT_RESOLVED;
@@ -201,6 +185,28 @@ public class ObjectStreamClass implements Serializable {
return constructor;
}
+ Field getReflectionField(ObjectStreamField osf) {
+ synchronized (reflectionFields) {
+ Field field = reflectionFields.get(osf);
+ if (field != null) {
+ return field;
+ }
+ }
+
+ try {
+ Class<?> declaringClass = forClass();
+ Field field = declaringClass.getDeclaredField(osf.getName());
+ field.setAccessible(true);
+ synchronized (reflectionFields) {
+ reflectionFields.put(osf, field);
+ }
+ return reflectionFields.get(osf);
+ } catch (NoSuchFieldException ex) {
+ // The caller messed up. We'll return null and won't try to resolve this again.
+ return null;
+ }
+ }
+
/*
* If an ObjectStreamClass describes an Externalizable class, it (the
* descriptor) should not have field descriptors (ObjectStreamField) at all.
@@ -246,13 +252,12 @@ public class ObjectStreamClass implements Serializable {
Field[] declaredFields = null;
// Compute the SUID
- if(serializable || externalizable) {
+ if (serializable || externalizable) {
if (result.isEnum() || result.isProxy()) {
result.setSerialVersionUID(0L);
} else {
declaredFields = cl.getDeclaredFields();
- result.setSerialVersionUID(computeSerialVersionUID(cl,
- declaredFields));
+ result.setSerialVersionUID(computeSerialVersionUID(cl, declaredFields));
}
}
@@ -294,12 +299,9 @@ public class ObjectStreamClass implements Serializable {
}
result.methodWriteReplace = findMethod(cl, "writeReplace");
result.methodReadResolve = findMethod(cl, "readResolve");
- result.methodWriteObject = findPrivateMethod(cl, "writeObject",
- WRITE_PARAM_TYPES);
- result.methodReadObject = findPrivateMethod(cl, "readObject",
- READ_PARAM_TYPES);
- result.methodReadObjectNoData = findPrivateMethod(cl,
- "readObjectNoData", EMPTY_CONSTRUCTOR_PARAM_TYPES);
+ result.methodWriteObject = findPrivateMethod(cl, "writeObject", WRITE_PARAM_TYPES);
+ result.methodReadObject = findPrivateMethod(cl, "readObject", READ_PARAM_TYPES);
+ result.methodReadObjectNoData = findPrivateMethod(cl, "readObjectNoData", EmptyArray.CLASS);
if (result.hasMethodWriteObject()) {
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
}
@@ -318,8 +320,7 @@ public class ObjectStreamClass implements Serializable {
void buildFieldDescriptors(Field[] declaredFields) {
// We could find the field ourselves in the collection, but calling
// reflect is easier. Optimize if needed.
- final Field f = ObjectStreamClass.fieldSerialPersistentFields(this
- .forClass());
+ final Field f = ObjectStreamClass.fieldSerialPersistentFields(this.forClass());
// If we could not find the emulated fields, we'll have to compute
// dumpable fields from reflect fields
boolean useReflectFields = f == null; // Assume we will compute the
@@ -335,23 +336,18 @@ public class ObjectStreamClass implements Serializable {
// static field, pass null
_fields = (ObjectStreamField[]) f.get(null);
} catch (IllegalAccessException ex) {
- // WARNING - what should we do if we have no access ? This
- // should not happen.
- throw new RuntimeException(ex);
+ throw new AssertionError(ex);
}
} else {
// Compute collection of dumpable fields based on reflect fields
- List<ObjectStreamField> serializableFields = new ArrayList<ObjectStreamField>(
- declaredFields.length);
+ List<ObjectStreamField> serializableFields =
+ new ArrayList<ObjectStreamField>(declaredFields.length);
// Filter, we are only interested in fields that are serializable
- for (int i = 0; i < declaredFields.length; i++) {
- Field declaredField = declaredFields[i];
+ for (Field declaredField : declaredFields) {
int modifiers = declaredField.getModifiers();
- boolean shouldBeSerialized = !(Modifier.isStatic(modifiers) || Modifier
- .isTransient(modifiers));
- if (shouldBeSerialized) {
- ObjectStreamField field = new ObjectStreamField(
- declaredField.getName(), declaredField.getType());
+ if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
+ ObjectStreamField field = new ObjectStreamField(declaredField.getName(),
+ declaredField.getType());
serializableFields.add(field);
}
}
@@ -360,12 +356,10 @@ public class ObjectStreamClass implements Serializable {
_fields = NO_FIELDS; // If no serializable fields, share the
// special value so that users can test
} else {
- // Now convert from Vector to array
- _fields = new ObjectStreamField[serializableFields.size()];
- _fields = serializableFields.toArray(_fields);
+ _fields = serializableFields.toArray(new ObjectStreamField[serializableFields.size()]);
}
}
- ObjectStreamField.sortFields(_fields);
+ Arrays.sort(_fields);
// assign offsets
int primOffset = 0, objectOffset = 0;
for (int i = 0; i < _fields.length; i++) {
@@ -925,12 +919,7 @@ public class ObjectStreamClass implements Serializable {
*/
public static ObjectStreamClass lookup(Class<?> cl) {
ObjectStreamClass osc = lookupStreamClass(cl);
-
- if (osc.isSerializable() || osc.isExternalizable()) {
- return osc;
- }
-
- return null;
+ return (osc.isSerializable() || osc.isExternalizable()) ? osc : null;
}
/**
@@ -944,7 +933,7 @@ public class ObjectStreamClass implements Serializable {
* @since 1.6
*/
public static ObjectStreamClass lookupAny(Class<?> cl) {
- return lookupStreamClass(cl);
+ return lookupStreamClass(cl);
}
/**
@@ -958,8 +947,7 @@ public class ObjectStreamClass implements Serializable {
* @return the corresponding descriptor
*/
static ObjectStreamClass lookupStreamClass(Class<?> cl) {
- WeakHashMap<Class<?>,ObjectStreamClass> tlc = getCache();
-
+ WeakHashMap<Class<?>, ObjectStreamClass> tlc = getCache();
ObjectStreamClass cachedValue = tlc.get(cl);
if (cachedValue == null) {
cachedValue = createClassDesc(cl);
@@ -1031,8 +1019,7 @@ public class ObjectStreamClass implements Serializable {
Class<?>[] param) {
try {
Method method = cl.getDeclaredMethod(methodName, param);
- if (Modifier.isPrivate(method.getModifiers())
- && method.getReturnType() == VOID_CLASS) {
+ if (Modifier.isPrivate(method.getModifiers()) && method.getReturnType() == Void.TYPE) {
method.setAccessible(true);
return method;
}
diff --git a/luni/src/main/java/java/io/ObjectStreamField.java b/luni/src/main/java/java/io/ObjectStreamField.java
index 686ea15..7e05f40 100644
--- a/luni/src/main/java/java/io/ObjectStreamField.java
+++ b/luni/src/main/java/java/io/ObjectStreamField.java
@@ -17,19 +17,10 @@
package java.io;
-// BEGIN android-note
-// Harmony uses ObjectAccessors to access fields through JNI. Android has not
-// yet migrated that API.
-// END android-note
-
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Comparator;
-// BEGIN android-removed
-// import org.apache.harmony.misc.accessors.ObjectAccessor;
-// END android-removed
-
/**
* Describes a field for the purpose of serialization. Classes can define the
* collection of fields that are serialized, which may be different from the set
@@ -40,9 +31,6 @@ import java.util.Comparator;
*/
public class ObjectStreamField implements Comparable<Object> {
- static final int FIELD_IS_NOT_RESOLVED = -1;
- static final int FIELD_IS_ABSENT = -2;
-
// Declared name of the field
private String name;
@@ -59,23 +47,6 @@ public class ObjectStreamField implements Comparable<Object> {
private boolean isDeserialized;
- private long assocFieldID = FIELD_IS_NOT_RESOLVED;
-
- // BEGIN android-removed
- // long getFieldID(ObjectAccessor accessor, Class<?> declaringClass) {
- // if (assocFieldID != FIELD_IS_NOT_RESOLVED) {
- // return assocFieldID;
- // } else {
- // try {
- // assocFieldID = accessor.getFieldID(declaringClass, name);
- // } catch(NoSuchFieldError e) {
- // assocFieldID = FIELD_IS_ABSENT;
- // }
- // return assocFieldID;
- // }
- // }
- // END android-removed
-
/**
* Constructs an ObjectStreamField with the specified name and type.
*
@@ -114,8 +85,7 @@ public class ObjectStreamField implements Comparable<Object> {
throw new NullPointerException();
}
this.name = name;
- this.type = (cl.getClassLoader() == null) ? cl
- : new WeakReference<Class<?>>(cl);
+ this.type = (cl.getClassLoader() == null) ? cl : new WeakReference<Class<?>>(cl);
this.unshared = unshared;
}
@@ -165,19 +135,6 @@ public class ObjectStreamField implements Comparable<Object> {
return this.getName().compareTo(f.getName());
}
- // BEGIN android-removed
- // There shouldn't be an implementation of these methods.
- // @Override
- // public boolean equals(Object arg0) {
- // return (arg0 instanceof ObjectStreamField) && (compareTo(arg0) == 0);
- // }
- //
- // @Override
- // public int hashCode() {
- // return getName().hashCode();
- // }
- // END android-removed
-
/**
* Gets the name of this field.
*
@@ -325,27 +282,7 @@ public class ObjectStreamField implements Comparable<Object> {
*/
@Override
public String toString() {
- return this.getClass().getName() + '(' + getName() + ':'
- + getTypeInternal() + ')';
- }
-
- /**
- * Sorts the fields for dumping. Primitive types come first, then regular
- * types.
- *
- * @param fields
- * ObjectStreamField[] fields to be sorted
- */
- static void sortFields(ObjectStreamField[] fields) {
- // Sort if necessary
- if (fields.length > 1) {
- Comparator<ObjectStreamField> fieldDescComparator = new Comparator<ObjectStreamField>() {
- public int compare(ObjectStreamField f1, ObjectStreamField f2) {
- return f1.compareTo(f2);
- }
- };
- Arrays.sort(fields, fieldDescComparator);
- }
+ return this.getClass().getName() + '(' + getName() + ':' + getTypeInternal() + ')';
}
void resolve(ClassLoader loader) {
diff --git a/luni/src/main/java/java/io/OutputStreamWriter.java b/luni/src/main/java/java/io/OutputStreamWriter.java
index e1a515b..57c2d55 100644
--- a/luni/src/main/java/java/io/OutputStreamWriter.java
+++ b/luni/src/main/java/java/io/OutputStreamWriter.java
@@ -285,13 +285,14 @@ public class OutputStreamWriter extends Writer {
public void write(String str, int offset, int count) throws IOException {
synchronized (lock) {
if (count < 0) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException(count);
}
if (str == null) {
throw new NullPointerException("str == null");
}
if (offset < 0 || offset > str.length() - count) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("str.length=" + str.length()
+ + " offset=" + offset + " count=" + count);
}
checkStatus();
CharBuffer chars = CharBuffer.wrap(str, offset, count + offset);
diff --git a/luni/src/main/java/java/io/Writer.java b/luni/src/main/java/java/io/Writer.java
index ca909f3..89755ff 100644
--- a/luni/src/main/java/java/io/Writer.java
+++ b/luni/src/main/java/java/io/Writer.java
@@ -170,7 +170,7 @@ public abstract class Writer implements Appendable, Closeable, Flushable {
*/
public void write(String str, int offset, int count) throws IOException {
if (count < 0) { // other cases tested by getChars()
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException(count);
}
char[] buf = new char[count];
str.getChars(offset, offset + count, buf, 0);
diff --git a/luni/src/main/java/java/lang/AbstractStringBuilder.java b/luni/src/main/java/java/lang/AbstractStringBuilder.java
index 2aa0e86..3e854b9 100644
--- a/luni/src/main/java/java/lang/AbstractStringBuilder.java
+++ b/luni/src/main/java/java/lang/AbstractStringBuilder.java
@@ -19,6 +19,7 @@ package java.lang;
import java.io.InvalidObjectException;
import java.util.Arrays;
+import libcore.base.EmptyArray;
/**
* A modifiable {@link CharSequence sequence of characters} for use in creating
@@ -59,7 +60,7 @@ abstract class AbstractStringBuilder {
*/
final void set(char[] val, int len) throws InvalidObjectException {
if (val == null) {
- val = new char[0];
+ val = EmptyArray.CHAR;
}
if (val.length < len) {
throw new InvalidObjectException("count out of range");
@@ -211,7 +212,7 @@ abstract class AbstractStringBuilder {
*/
public char charAt(int index) {
if (index < 0 || index >= count) {
- throw new StringIndexOutOfBoundsException(index);
+ throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
}
return value[index];
}
@@ -241,12 +242,14 @@ abstract class AbstractStringBuilder {
return;
}
}
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException(
+ "start=" + start + " end=" + end + " length=" + count);
}
final void deleteCharAt0(int location) {
if (0 > location || location >= count) {
- throw new StringIndexOutOfBoundsException(location);
+ throw new StringIndexOutOfBoundsException(
+ "location=" + location + " length=" + count);
}
int length = count - location - 1;
if (length > 0) {
@@ -305,14 +308,15 @@ abstract class AbstractStringBuilder {
*/
public void getChars(int start, int end, char[] dst, int dstStart) {
if (start > count || end > count || start > end) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException(
+ "start=" + start + " end=" + end + " length=" + count);
}
System.arraycopy(value, start, dst, dstStart, end - start);
}
final void insert0(int index, char[] chars) {
if (0 > index || index > count) {
- throw new StringIndexOutOfBoundsException(index);
+ throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
}
if (chars.length != 0) {
move(chars.length, index);
@@ -332,11 +336,9 @@ abstract class AbstractStringBuilder {
}
return;
}
- throw new StringIndexOutOfBoundsException("offset " + start
- + ", length " + length
- + ", char[].length " + chars.length);
}
- throw new StringIndexOutOfBoundsException(index);
+ throw new StringIndexOutOfBoundsException("index=" + index + " chars.length=" + chars.length
+ + " start=" + start + " length=" + length + " this.length=" + count);
}
final void insert0(int index, char ch) {
@@ -363,7 +365,7 @@ abstract class AbstractStringBuilder {
count += min;
}
} else {
- throw new StringIndexOutOfBoundsException(index);
+ throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
}
}
@@ -450,7 +452,8 @@ abstract class AbstractStringBuilder {
return;
}
}
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException(
+ "start=" + start + " end=" + end + " length=" + count);
}
final void reverse0() {
@@ -543,7 +546,7 @@ abstract class AbstractStringBuilder {
*/
public void setCharAt(int index, char ch) {
if (0 > index || index >= count) {
- throw new StringIndexOutOfBoundsException(index);
+ throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
}
if (shared) {
value = value.clone();
@@ -604,7 +607,7 @@ abstract class AbstractStringBuilder {
// Remove String sharing for more performance
return new String(value, start, count - start);
}
- throw new StringIndexOutOfBoundsException(start);
+ throw new StringIndexOutOfBoundsException("start=" + start + " length=" + count);
}
/**
@@ -629,7 +632,8 @@ abstract class AbstractStringBuilder {
// Remove String sharing for more performance
return new String(value, start, end - start);
}
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException(
+ "start=" + start + " end=" + end + " length=" + count);
}
/**
@@ -834,7 +838,7 @@ abstract class AbstractStringBuilder {
*/
public int codePointAt(int index) {
if (index < 0 || index >= count) {
- throw new StringIndexOutOfBoundsException(index);
+ throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
}
return Character.codePointAt(value, index, count);
}
@@ -854,7 +858,7 @@ abstract class AbstractStringBuilder {
*/
public int codePointBefore(int index) {
if (index < 1 || index > count) {
- throw new StringIndexOutOfBoundsException(index);
+ throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
}
return Character.codePointBefore(value, index);
}
@@ -878,7 +882,8 @@ abstract class AbstractStringBuilder {
*/
public int codePointCount(int beginIndex, int endIndex) {
if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("beginIndex=" + beginIndex
+ + " endIndex" + endIndex + " length=" + count);
}
return Character.codePointCount(value, beginIndex, endIndex
- beginIndex);
diff --git a/luni/src/main/java/java/lang/Class.java b/luni/src/main/java/java/lang/Class.java
index 451da89..eb721b8 100644
--- a/luni/src/main/java/java/lang/Class.java
+++ b/luni/src/main/java/java/lang/Class.java
@@ -658,8 +658,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe
* access.
* @see #getField(String)
*/
- public Field getDeclaredField(String name)
- throws NoSuchFieldException, SecurityException {
+ public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException {
checkDeclaredMemberAccess();
Field[] fields = getClassCache().getDeclaredFields();
@@ -871,6 +870,26 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe
}
/**
+ * Finds and returns a field with a given name and signature. Use
+ * this with one of the field lists returned by instances of ClassCache.
+ *
+ * @param list non-null; the list of fields to search through
+ * @return non-null; the matching field
+ * @throws NoSuchFieldException thrown if the field does not exist
+ */
+ private Field findFieldByName(Field[] list, String name) throws NoSuchFieldException {
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+ for (Field field : list) {
+ if (field.getName().equals(name)) {
+ return field;
+ }
+ }
+ throw new NoSuchFieldException("No field '" + name + "' in " + this);
+ }
+
+ /**
* Returns an array containing {@code Field} objects for all public fields
* for the class C represented by this {@code Class}. Fields may be declared
* in C, the interfaces it implements or in the superclasses of C. The
@@ -1467,7 +1486,9 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe
if (clazz.isAssignableFrom(this)) {
return (Class<? extends U>)this;
}
- throw new ClassCastException();
+ String actualClassName = this.getName();
+ String desiredClassName = clazz.getName();
+ throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName);
}
/**
@@ -1487,7 +1508,9 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe
} else if (this.isInstance(obj)) {
return (T)obj;
}
- throw new ClassCastException();
+ String actualClassName = obj.getClass().getName();
+ String desiredClassName = this.getName();
+ throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName);
}
/**
diff --git a/luni/src/main/java/java/lang/ClassCache.java b/luni/src/main/java/java/lang/ClassCache.java
index 5588f62..55b787f 100644
--- a/luni/src/main/java/java/lang/ClassCache.java
+++ b/luni/src/main/java/java/lang/ClassCache.java
@@ -235,7 +235,7 @@ import org.apache.harmony.kernel.vm.ReflectionAccess;
public static Method findMethodByName(Method[] list, String name,
Class<?>[] parameterTypes) throws NoSuchMethodException {
if (name == null) {
- throw new NullPointerException("Method name must not be null.");
+ throw new NullPointerException("name == null");
}
for (Method method : list) {
if (method.getName().equals(name)
@@ -425,28 +425,6 @@ import org.apache.harmony.kernel.vm.ReflectionAccess;
}
/**
- * Finds and returns a field with a given name and signature. Use
- * this with one of the field lists returned by instances of this class.
- *
- * @param list non-null; the list of fields to search through
- * @return non-null; the matching field
- * @throws NoSuchFieldException thrown if the field does not exist
- */
- public static Field findFieldByName(Field[] list, String name)
- throws NoSuchFieldException {
- if (name == null) {
- throw new NullPointerException("Field name must not be null.");
- }
- for (Field field : list) {
- if (field.getName().equals(name)) {
- return field;
- }
- }
-
- throw new NoSuchFieldException(name);
- }
-
- /**
* Makes a deep copy of the given array of fields. This is useful
* when handing out arrays from the public API.
*
diff --git a/luni/src/main/java/java/lang/String.java b/luni/src/main/java/java/lang/String.java
index 605ea67..541d987 100644
--- a/luni/src/main/java/java/lang/String.java
+++ b/luni/src/main/java/java/lang/String.java
@@ -28,6 +28,7 @@ import java.util.Comparator;
import java.util.Formatter;
import java.util.Locale;
import java.util.regex.Pattern;
+import libcore.base.EmptyArray;
/**
* An immutable sequence of characters/code units ({@code char}s). A
@@ -94,8 +95,6 @@ public final class String implements Serializable, Comparable<String>, CharSeque
*/
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
- private static final char[] EMPTY_CHAR_ARRAY = new char[0];
-
private static final char[] ASCII;
static {
ASCII = new char[128];
@@ -116,7 +115,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque
* Creates an empty string.
*/
public String() {
- value = EMPTY_CHAR_ARRAY;
+ value = EmptyArray.CHAR;
offset = 0;
count = 0;
}
@@ -184,10 +183,11 @@ public final class String implements Serializable, Comparable<String>, CharSeque
if (count > 0) {
value = cb.array();
} else {
- value = EMPTY_CHAR_ARRAY;
+ value = EmptyArray.CHAR;
}
} else {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("data.length=" + data.length
+ + " start=" + start + " length=" + length);
}
}
@@ -224,7 +224,8 @@ public final class String implements Serializable, Comparable<String>, CharSeque
value[i] = (char) (high + (data[start++] & 0xff));
}
} else {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException(
+ "data.length=" + data.length + " start=" + start + " length=" + length);
}
} else {
throw new NullPointerException();
@@ -314,7 +315,8 @@ public final class String implements Serializable, Comparable<String>, CharSeque
*/
public String(byte[] data, int start, int length, Charset charset) {
if (start < 0 || length < 0 || length > data.length - start) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException(
+ "data.length=" + data.length + " start=" + start + " length=" + length);
}
// We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed and because 'count' and
@@ -446,7 +448,7 @@ outer:
this.value = new char[count];
System.arraycopy(cb.array(), 0, value, 0, count);
} else {
- value = EMPTY_CHAR_ARRAY;
+ value = EmptyArray.CHAR;
}
}
}
@@ -514,7 +516,8 @@ outer:
count = length;
System.arraycopy(data, start, value, 0, count);
} else {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException(
+ "data.length=" + data.length + " start=" + start + " length=" + length);
}
}
@@ -623,7 +626,8 @@ outer:
throw new NullPointerException();
}
if (offset < 0 || count < 0 || (long) offset + (long) count > codePoints.length) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("codePoints.length=" + codePoints.length
+ + " offset=" + offset + " count=" + count);
}
this.offset = 0;
this.value = new char[count * 2];
@@ -685,7 +689,7 @@ outer:
if (0 <= index && index < count) {
return value[offset + index];
}
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
}
// Optimized for ASCII
@@ -940,12 +944,11 @@ outer:
for (int i = offset + start; i < end; i++) {
data[index++] = (byte) value[i];
}
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new StringIndexOutOfBoundsException();
+ } catch (ArrayIndexOutOfBoundsException ignored) {
}
- } else {
- throw new StringIndexOutOfBoundsException();
}
+ throw new StringIndexOutOfBoundsException("start=" + start + " end=" + end
+ + " data.length=" + data.length + " index=" + index + " length=" + count);
}
/**
@@ -1027,7 +1030,8 @@ outer:
if (0 <= start && start <= end && end <= count) {
System.arraycopy(value, start + offset, buffer, index, end - start);
} else {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("start=" + start + " end=" + end +
+ " buffer.length=" + buffer.length + " index=" + index + " length=" + count);
}
}
@@ -1618,7 +1622,7 @@ outer:
if (0 <= start && start <= count) {
return new String(offset + start, count - start, value);
}
- throw new StringIndexOutOfBoundsException(start);
+ throw new StringIndexOutOfBoundsException("start=" + start + " length=" + count);
}
/**
@@ -1644,7 +1648,8 @@ outer:
if (0 <= start && start <= end && end <= count) {
return new String(offset + start, end - start, value);
}
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException(
+ "start=" + start + " end=" + end + " length=" + count);
}
/**
@@ -1894,6 +1899,23 @@ outer:
}
/**
+ * Equivalent to {@code equals(new String(chars, offset, length))}.
+ *
+ * @hide
+ */
+ public boolean contentEquals(char[] chars, int offset, int length) {
+ if (count != length) {
+ return false;
+ }
+ for (int i = 0; i < length; i++) {
+ if (chars[offset + i] != value[this.offset + i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Compares a {@code CharSequence} to this {@code String} to determine if
* their contents are equal.
*
@@ -2045,7 +2067,7 @@ outer:
*/
public int codePointAt(int index) {
if (index < 0 || index >= count) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
}
return Character.codePointAt(value, offset + index, offset + count);
}
@@ -2059,7 +2081,7 @@ outer:
*/
public int codePointBefore(int index) {
if (index < 1 || index > count) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
}
return Character.codePointBefore(value, offset + index, offset);
}
@@ -2080,7 +2102,8 @@ outer:
*/
public int codePointCount(int beginIndex, int endIndex) {
if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) {
- throw new StringIndexOutOfBoundsException();
+ throw new StringIndexOutOfBoundsException("beginIndex=" + beginIndex
+ + " endIndex=" + endIndex + " length=" + count);
}
return Character.codePointCount(value, offset + beginIndex, endIndex - beginIndex);
}
diff --git a/luni/src/main/java/java/lang/Thread.java b/luni/src/main/java/java/lang/Thread.java
index 980c841..e26ecf0 100644
--- a/luni/src/main/java/java/lang/Thread.java
+++ b/luni/src/main/java/java/lang/Thread.java
@@ -69,6 +69,7 @@ import org.apache.harmony.security.fortress.SecurityUtils;
*
*/
public class Thread implements Runnable {
+ private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
private static final int NANOS_PER_MILLI = 1000000;
@@ -735,7 +736,7 @@ public class Thread implements Runnable {
}
StackTraceElement ste[] = VMStack.getThreadStackTrace(this);
- return ste != null ? ste : new StackTraceElement[0];
+ return ste != null ? ste : EMPTY_STACK_TRACE;
}
/**
diff --git a/luni/src/main/java/java/lang/Void.java b/luni/src/main/java/java/lang/Void.java
index 56d7c74..75e616a 100644
--- a/luni/src/main/java/java/lang/Void.java
+++ b/luni/src/main/java/java/lang/Void.java
@@ -18,6 +18,7 @@
package java.lang;
import java.lang.reflect.Method;
+import libcore.base.EmptyArray;
/**
* Placeholder class for the Java keyword {@code void}.
@@ -38,7 +39,7 @@ public final class Void extends Object {
private static Class<Void> lookupType() {
Class<?> voidType = null;
try {
- Method method = Runnable.class.getMethod("run", new Class[0]);
+ Method method = Runnable.class.getMethod("run", EmptyArray.CLASS);
voidType = method.getReturnType();
} catch (Exception e) {
throw new RuntimeException(e);
diff --git a/luni/src/main/java/java/lang/reflect/Array.java b/luni/src/main/java/java/lang/reflect/Array.java
index 1e672d1..db0d426 100644
--- a/luni/src/main/java/java/lang/reflect/Array.java
+++ b/luni/src/main/java/java/lang/reflect/Array.java
@@ -47,14 +47,14 @@ public final class Array {
/**
* Returns the element of the array at the specified index. This reproduces
* the effect of {@code array[index]}. If the array component is a primitive
- * type, the result is automatically wrapped.
+ * type, the result is automatically boxed.
*
* @param array
* the array
* @param index
* the index
*
- * @return the requested element, possibly wrapped
+ * @return the requested element, possibly boxed
*
* @throws NullPointerException
* if the array is null
@@ -486,7 +486,7 @@ public final class Array {
/**
* Sets the element of the array at the specified index to the value. This
* reproduces the effect of {@code array[index] = value}. If the array
- * component is a primitive type, the value is automatically unwrapped.
+ * component is a primitive type, the value is automatically unboxed.
*
* @param array
* the array
diff --git a/luni/src/main/java/java/lang/reflect/Constructor.java b/luni/src/main/java/java/lang/reflect/Constructor.java
index fba433e..f511687 100644
--- a/luni/src/main/java/java/lang/reflect/Constructor.java
+++ b/luni/src/main/java/java/lang/reflect/Constructor.java
@@ -34,6 +34,7 @@ package java.lang.reflect;
import dalvik.system.VMStack;
import java.lang.annotation.Annotation;
+import libcore.base.EmptyArray;
import org.apache.harmony.kernel.vm.StringUtils;
import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
import org.apache.harmony.luni.lang.reflect.ListOfTypes;
@@ -291,8 +292,9 @@ public final class Constructor<T> extends AccessibleObject implements GenericDec
* @return the declared exception classes
*/
public Class<?>[] getExceptionTypes() {
- if (exceptionTypes == null)
- return new Class[0];
+ if (exceptionTypes == null) {
+ return EmptyArray.CLASS;
+ }
return exceptionTypes.clone();
}
@@ -381,7 +383,7 @@ public final class Constructor<T> extends AccessibleObject implements GenericDec
* <li>For each argument passed:
* <ul>
* <li>If the corresponding parameter type is a primitive type, the argument
- * is unwrapped. If the unwrapping fails, an IllegalArgumentException is
+ * is unboxed. If the unboxing fails, an IllegalArgumentException is
* thrown.</li>
* <li>If the resulting argument cannot be converted to the parameter type
* via a widening conversion, an IllegalArgumentException is thrown.</li>
diff --git a/luni/src/main/java/java/lang/reflect/Field.java b/luni/src/main/java/java/lang/reflect/Field.java
index ecdb93a..d7898b9 100644
--- a/luni/src/main/java/java/lang/reflect/Field.java
+++ b/luni/src/main/java/java/lang/reflect/Field.java
@@ -209,23 +209,22 @@ public final class Field extends AccessibleObject implements Member {
/**
* Returns the value of the field in the specified object. This reproduces
* the effect of {@code object.fieldName}
- * <p>
- * If the type of this field is a primitive type, the field value is
- * automatically wrapped.
- * <p>
- * If this field is static, the object argument is ignored.
+ *
+ * <p>If the type of this field is a primitive type, the field value is
+ * automatically boxed.
+ *
+ * <p>If this field is static, the object argument is ignored.
* Otherwise, if the object is null, a NullPointerException is thrown. If
* the object is not an instance of the declaring class of the method, an
* IllegalArgumentException is thrown.
- * <p>
- * If this Field object is enforcing access control (see AccessibleObject)
+ *
+ * <p>If this Field object is enforcing access control (see AccessibleObject)
* and this field is not accessible from the current context, an
* IllegalAccessException is thrown.
- * <p>
*
* @param object
* the object to access
- * @return the field value, possibly wrapped
+ * @return the field value, possibly boxed
* @throws NullPointerException
* if the object is {@code null} and the field is non-static
* @throws IllegalArgumentException
@@ -527,18 +526,18 @@ public final class Field extends AccessibleObject implements Member {
/**
* Sets the value of the field in the specified object to the value. This
* reproduces the effect of {@code object.fieldName = value}
- * <p>
- * If this field is static, the object argument is ignored.
+ *
+ * <p>If this field is static, the object argument is ignored.
* Otherwise, if the object is {@code null}, a NullPointerException is
* thrown. If the object is not an instance of the declaring class of the
* method, an IllegalArgumentException is thrown.
- * <p>
- * If this Field object is enforcing access control (see AccessibleObject)
+ *
+ * <p>If this Field object is enforcing access control (see AccessibleObject)
* and this field is not accessible from the current context, an
* IllegalAccessException is thrown.
- * <p>
- * If the field type is a primitive type, the value is automatically
- * unwrapped. If the unwrap fails, an IllegalArgumentException is thrown. If
+ *
+ * <p>If the field type is a primitive type, the value is automatically
+ * unboxed. If the unboxing fails, an IllegalArgumentException is thrown. If
* the value cannot be converted to the field type via a widening
* conversion, an IllegalArgumentException is thrown.
*
diff --git a/luni/src/main/java/java/lang/reflect/InvocationHandler.java b/luni/src/main/java/java/lang/reflect/InvocationHandler.java
index c5bd7d2..330904e 100644
--- a/luni/src/main/java/java/lang/reflect/InvocationHandler.java
+++ b/luni/src/main/java/java/lang/reflect/InvocationHandler.java
@@ -46,10 +46,9 @@ public interface InvocationHandler {
* @param args
* an array of objects containing the parameters passed to the
* method, or {@code null} if no arguments are expected.
- * Primitive types are wrapped in the appropriate wrapper type
+ * Primitive types are boxed.
*
- * @return the result of executing the method. Primitive types need to be
- * wrapped in the appropriate wrapper type
+ * @return the result of executing the method. Primitive types are boxed.
*
* @throws Throwable
* the exception to throw from the invoked method on the proxy.
@@ -57,6 +56,5 @@ public interface InvocationHandler {
* of the invoked method or any unchecked exception type. If not
* then an {@code UndeclaredThrowableException} is thrown
*/
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable;
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
diff --git a/luni/src/main/java/java/lang/reflect/Method.java b/luni/src/main/java/java/lang/reflect/Method.java
index f46d76f..68fe219 100644
--- a/luni/src/main/java/java/lang/reflect/Method.java
+++ b/luni/src/main/java/java/lang/reflect/Method.java
@@ -34,6 +34,7 @@ package java.lang.reflect;
import java.lang.annotation.Annotation;
import java.util.Comparator;
+import libcore.base.EmptyArray;
import org.apache.harmony.kernel.vm.StringUtils;
import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
import org.apache.harmony.luni.lang.reflect.ListOfTypes;
@@ -378,9 +379,8 @@ public final class Method extends AccessibleObject implements GenericDeclaration
*/
public Class<?>[] getExceptionTypes() {
if (exceptionTypes == null) {
- return new Class[0];
+ return EmptyArray.CLASS;
}
-
return exceptionTypes.clone();
}
@@ -444,73 +444,57 @@ public final class Method extends AccessibleObject implements GenericDeclaration
}
/**
- * Returns the result of dynamically invoking this method. This reproduces
- * the effect of {@code receiver.methodName(arg1, arg2, ... , argN)} This
- * method performs the following:
- * <ul>
- * <li>If this method is static, the receiver argument is ignored.</li>
- * <li>Otherwise, if the receiver is null, a NullPointerException is thrown.
- * </li>
- * <li>If the receiver is not an instance of the declaring class of the
- * method, an IllegalArgumentException is thrown.</li>
- * <li>If this Method object is enforcing access control (see
- * AccessibleObject) and this method is not accessible from the current
- * context, an IllegalAccessException is thrown.</li>
- * <li>If the number of arguments passed and the number of parameters do not
- * match, an IllegalArgumentException is thrown.</li>
- * <li>For each argument passed:
- * <ul>
- * <li>If the corresponding parameter type is a primitive type, the argument
- * is unwrapped. If the unwrapping fails, an IllegalArgumentException is
- * thrown.</li>
- * <li>If the resulting argument cannot be converted to the parameter type
- * via a widening conversion, an IllegalArgumentException is thrown.</li>
- * </ul>
- * <li>If this method is static, it is invoked directly. If it is
- * non-static, this method and the receiver are then used to perform a
- * standard dynamic method lookup. The resulting method is then invoked.</li>
- * <li>If an exception is thrown during the invocation it is caught and
- * wrapped in an InvocationTargetException. This exception is then thrown.</li>
- * <li>If the invocation completes normally, the return value itself is
+ * Returns the result of dynamically invoking this method. Equivalent to
+ * {@code receiver.methodName(arg1, arg2, ... , argN)}.
+ *
+ * <p>If the method is static, the receiver argument is ignored (and may be null).
+ *
+ * <p>If the method takes no arguments, you can pass {@code (Object[]) null} instead of
+ * allocating an empty array.
+ *
+ * <p>If you're calling a varargs method, you need to pass an {@code Object[]} for the
+ * varargs parameter: that conversion is usually done in {@code javac}, not the VM, and
+ * the reflection machinery does not do this for you. (It couldn't, because it would be
+ * ambiguous.)
+ *
+ * <p>Reflective method invocation follows the usual process for method lookup.
+ *
+ * <p>If an exception is thrown during the invocation it is caught and
+ * wrapped in an InvocationTargetException. This exception is then thrown.
+ *
+ * <p>If the invocation completes normally, the return value itself is
* returned. If the method is declared to return a primitive type, the
- * return value is first wrapped. If the return type is void, null is
- * returned.</li>
- * </ul>
+ * return value is boxed. If the return type is void, null is returned.
*
* @param receiver
- * the object on which to call this method
+ * the object on which to call this method (or null for static methods)
* @param args
* the arguments to the method
- *
- * @return the new, initialized, object
+ * @return the result
*
* @throws NullPointerException
- * if the receiver is null for a non-static method
+ * if {@code receiver == null} for a non-static method
* @throws IllegalAccessException
- * if this method is not accessible
+ * if this method is not accessible (see {@link AccessibleObject})
* @throws IllegalArgumentException
- * if an incorrect number of arguments are passed, the receiver
- * is incompatible with the declaring class, or an argument
- * could not be converted by a widening conversion
+ * if the number of arguments doesn't match the number of parameters, the receiver
+ * is incompatible with the declaring class, or an argument could not be unboxed
+ * or converted by a widening conversion to the corresponding parameter type
* @throws InvocationTargetException
* if an exception was thrown by the invoked method
- *
- * @see AccessibleObject
*/
public Object invoke(Object receiver, Object... args)
- throws IllegalAccessException, IllegalArgumentException,
- InvocationTargetException {
+ throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (args == null) {
- args = new Object[0];
+ args = EmptyArray.OBJECT;
}
-
- return invokeNative (receiver, args, declaringClass, parameterTypes, returnType, slot, flag);
+ return invokeNative(receiver, args, declaringClass, parameterTypes, returnType, slot, flag);
}
- private native Object invokeNative(Object obj, Object[] args, Class<?> declaringClass, Class<?>[] parameterTypes, Class<?> returnType, int slot, boolean noAccessCheck)
- throws IllegalAccessException,
- IllegalArgumentException,
- InvocationTargetException;
+ private native Object invokeNative(Object obj, Object[] args, Class<?> declaringClass,
+ Class<?>[] parameterTypes, Class<?> returnType, int slot, boolean noAccessCheck)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException;
/**
* Returns a string containing a concise, human-readable description of this
diff --git a/luni/src/main/java/java/net/DatagramPacket.java b/luni/src/main/java/java/net/DatagramPacket.java
index 36ec922..cde5d7b 100644
--- a/luni/src/main/java/java/net/DatagramPacket.java
+++ b/luni/src/main/java/java/net/DatagramPacket.java
@@ -277,8 +277,7 @@ public final class DatagramPacket {
* @throws SocketException
* if an error in the underlying protocol occurs.
*/
- public DatagramPacket(byte[] data, int length, SocketAddress sockAddr)
- throws SocketException {
+ public DatagramPacket(byte[] data, int length, SocketAddress sockAddr) throws SocketException {
this(data, 0, length);
setSocketAddress(sockAddr);
}
@@ -328,6 +327,9 @@ public final class DatagramPacket {
(sockAddr == null ? null : sockAddr.getClass()));
}
InetSocketAddress inetAddr = (InetSocketAddress) sockAddr;
+ if (inetAddr.isUnresolved()) {
+ throw new IllegalArgumentException("Socket address unresolved: " + sockAddr);
+ }
port = inetAddr.getPort();
address = inetAddr.getAddress();
}
diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java
index d1cc593..1bfe40e 100644
--- a/luni/src/main/java/java/net/DatagramSocket.java
+++ b/luni/src/main/java/java/net/DatagramSocket.java
@@ -429,18 +429,14 @@ public class DatagramSocket {
} else {
// not connected so the target address is not allowed to be null
if (packAddr == null) {
- if (pack.getPort() == -1) {
- throw new NullPointerException("Destination address is null");
- }
- return;
+ throw new NullPointerException("Destination address is null");
}
SecurityManager security = System.getSecurityManager();
if (security != null) {
if (packAddr.isMulticastAddress()) {
security.checkMulticast(packAddr);
} else {
- security.checkConnect(packAddr.getHostName(), pack
- .getPort());
+ security.checkConnect(packAddr.getHostName(), pack.getPort());
}
}
}
diff --git a/luni/src/main/java/java/net/Inet6Address.java b/luni/src/main/java/java/net/Inet6Address.java
index 24a069f..f8920fc 100644
--- a/luni/src/main/java/java/net/Inet6Address.java
+++ b/luni/src/main/java/java/net/Inet6Address.java
@@ -23,6 +23,7 @@ import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.util.Arrays;
import java.util.Enumeration;
+import libcore.base.EmptyArray;
/**
* An IPv6 address. See {@link InetAddress}.
@@ -375,7 +376,7 @@ public final class Inet6Address extends InetAddress {
}
private static final ObjectStreamField[] serialPersistentFields = {
- new ObjectStreamField("ipaddress", new byte[0].getClass()),
+ new ObjectStreamField("ipaddress", EmptyArray.BYTE.getClass()),
new ObjectStreamField("scope_id", Integer.TYPE),
new ObjectStreamField("scope_id_set", Boolean.TYPE),
new ObjectStreamField("scope_ifname_set", Boolean.TYPE),
diff --git a/luni/src/main/java/java/net/InetSocketAddress.java b/luni/src/main/java/java/net/InetSocketAddress.java
index 8a33db8..f9f973b 100644
--- a/luni/src/main/java/java/net/InetSocketAddress.java
+++ b/luni/src/main/java/java/net/InetSocketAddress.java
@@ -28,10 +28,9 @@ public class InetSocketAddress extends SocketAddress {
private static final long serialVersionUID = 5076001401234631237L;
- private final String hostname;
-
+ // Exactly one of hostname or addr should be set.
private final InetAddress addr;
-
+ private final String hostname;
private final int port;
/**
@@ -59,14 +58,10 @@ public class InetSocketAddress extends SocketAddress {
*/
public InetSocketAddress(InetAddress address, int port) {
if (port < 0 || port > 65535) {
- throw new IllegalArgumentException();
- }
- if (address == null) {
- addr = Inet4Address.ANY;
- } else {
- addr = address;
+ throw new IllegalArgumentException("port=" + port);
}
- hostname = addr.getHostName();
+ this.addr = (address == null) ? Inet4Address.ANY : address;
+ this.hostname = null;
this.port = port;
}
@@ -97,7 +92,6 @@ public class InetSocketAddress extends SocketAddress {
if (hostname == null || port < 0 || port > 65535) {
throw new IllegalArgumentException("host=" + hostname + ", port=" + port);
}
- this.port = port;
InetAddress addr = null;
if (needResolved) {
@@ -111,8 +105,9 @@ public class InetSocketAddress extends SocketAddress {
} catch (UnknownHostException ignored) {
}
}
- this.hostname = hostname;
this.addr = addr;
+ this.hostname = hostname;
+ this.port = port;
}
/**
@@ -157,7 +152,7 @@ public class InetSocketAddress extends SocketAddress {
* @return the socket endpoint hostname.
*/
public final String getHostName() {
- return (null != addr) ? addr.getHostName() : hostname;
+ return (addr != null) ? addr.getHostName() : hostname;
}
/**
@@ -178,13 +173,7 @@ public class InetSocketAddress extends SocketAddress {
*/
@Override
public String toString() {
- String host;
- if (addr != null) {
- host = addr.toString();
- } else {
- host = hostname;
- }
- return host + ":" + port;
+ return ((addr != null) ? addr.toString() : hostname) + ":" + port;
}
/**
@@ -235,8 +224,7 @@ public class InetSocketAddress extends SocketAddress {
return addr.hashCode() + port;
}
- private void readObject(ObjectInputStream stream) throws IOException,
- ClassNotFoundException {
+ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
}
}
diff --git a/luni/src/main/java/java/net/NetworkInterface.java b/luni/src/main/java/java/net/NetworkInterface.java
index 457b0e6..c5675b3 100644
--- a/luni/src/main/java/java/net/NetworkInterface.java
+++ b/luni/src/main/java/java/net/NetworkInterface.java
@@ -24,6 +24,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import libcore.base.EmptyArray;
/**
* This class is used to represent a network interface of the local device. An
@@ -471,7 +472,7 @@ public final class NetworkInterface extends Object {
*/
public byte[] getHardwareAddress() throws SocketException {
if (addresses.isEmpty()) {
- return new byte[0];
+ return EmptyArray.BYTE;
}
return getHardwareAddressImpl(name);
}
diff --git a/luni/src/main/java/java/net/Proxy.java b/luni/src/main/java/java/net/Proxy.java
index 752ea2b..b9d38ae 100644
--- a/luni/src/main/java/java/net/Proxy.java
+++ b/luni/src/main/java/java/net/Proxy.java
@@ -102,11 +102,15 @@ public class Proxy {
*/
@Override
public String toString() {
- String proxyString = String.valueOf(type);
- if (null != address) {
- proxyString += "/" + address.toString();
+ StringBuilder builder = new StringBuilder();
+ if (type != null) {
+ builder.append(type.toString());
+ }
+ builder.append("@");
+ if (type != Proxy.Type.DIRECT && address != null) {
+ builder.append(address.toString());
}
- return proxyString;
+ return builder.toString();
}
/**
diff --git a/luni/src/main/java/java/net/SocketOptions.java b/luni/src/main/java/java/net/SocketOptions.java
index 71441d3..a1b95bf 100644
--- a/luni/src/main/java/java/net/SocketOptions.java
+++ b/luni/src/main/java/java/net/SocketOptions.java
@@ -101,10 +101,13 @@ public interface SocketOptions {
public static final int SO_KEEPALIVE = 8;
/**
- * This option specifies the value for the Type-of-Service (TOS) field of
- * the IP header. This may be ignored by the underlying OS.
- * Values must be between 0 and 255 inclusive.
- * See <a href="http://www.ietf.org/rfc/rfc1349.txt">RFC 1349</a> for more information.
+ * This option specifies the value for the type-of-service field of the IPv4 header, or the
+ * traffic class field of the IPv6 header. These correspond to the IP_TOS and IPV6_TCLASS
+ * socket options. These may be ignored by the underlying OS. Values must be between 0 and 255
+ * inclusive.
+ *
+ * <p>See <a href="http://www.ietf.org/rfc/rfc1349.txt">RFC 1349</a> for more about IPv4
+ * and <a href="http://www.ietf.org/rfc/rfc2460.txt">RFC 2460</a> for more about IPv6.
*/
public static final int IP_TOS = 3;
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index 7750830..6cc1217 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -34,6 +34,7 @@ import java.nio.channels.DatagramChannel;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.spi.SelectorProvider;
+import libcore.base.EmptyArray;
import org.apache.harmony.luni.net.PlainDatagramSocketImpl;
import org.apache.harmony.luni.platform.FileDescriptorHandler;
import org.apache.harmony.luni.platform.INetworkSystem;
@@ -43,8 +44,6 @@ import org.apache.harmony.luni.platform.Platform;
* The default implementation class of java.nio.channels.DatagramChannel.
*/
class DatagramChannelImpl extends DatagramChannel implements FileDescriptorHandler {
- private static final byte[] stubArray = new byte[0];
-
// The fd to interact with native code
private final FileDescriptor fd;
@@ -255,7 +254,7 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorHandl
private SocketAddress receiveDirectImpl(ByteBuffer target, boolean loop) throws IOException {
SocketAddress retAddr = null;
- DatagramPacket receivePacket = new DatagramPacket(stubArray, 0);
+ DatagramPacket receivePacket = new DatagramPacket(EmptyArray.BYTE, 0);
int oldposition = target.position();
int received = 0;
do {
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index e01181a..bf673b8 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -35,6 +35,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import libcore.base.EmptyArray;
import org.apache.harmony.luni.platform.FileDescriptorHandler;
import org.apache.harmony.luni.platform.Platform;
@@ -43,9 +44,8 @@ import org.apache.harmony.luni.platform.Platform;
*/
final class SelectorImpl extends AbstractSelector {
- private static final int[] EMPTY_INT_ARRAY = new int[0];
+ static final FileDescriptor[] EMPTY_FILE_DESCRIPTORS_ARRAY = new FileDescriptor[0];
- private static final FileDescriptor[] EMPTY_FILE_DESCRIPTORS_ARRAY = new FileDescriptor[0];
private static final SelectionKeyImpl[] EMPTY_SELECTION_KEY_IMPLS_ARRAY
= new SelectionKeyImpl[0];
@@ -121,7 +121,7 @@ final class SelectorImpl extends AbstractSelector {
* actively selecting, all elements are 0. Corresponds to the ready keys
* set.
*/
- private int[] flags = EMPTY_INT_ARRAY;
+ private int[] flags = EmptyArray.INT;
public SelectorImpl(SelectorProvider selectorProvider) throws IOException {
super(selectorProvider);
diff --git a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
index abea8cf..240a0ba 100644
--- a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
@@ -82,7 +82,7 @@ final class ServerSocketChannelImpl extends ServerSocketChannel implements FileD
if (!isBlocking) {
int[] tryResult = new int[1];
boolean success = Platform.NETWORK.select(new FileDescriptor[] { fd },
- new FileDescriptor[0], 1, 0, 0, tryResult);
+ SelectorImpl.EMPTY_FILE_DESCRIPTORS_ARRAY, 1, 0, 0, tryResult);
if (!success || 0 == tryResult[0]) {
// no pending connections, returns immediately.
return null;
diff --git a/luni/src/main/java/java/security/cert/X509CertSelector.java b/luni/src/main/java/java/security/cert/X509CertSelector.java
index 8745bf2..33e68c7 100644
--- a/luni/src/main/java/java/security/cert/X509CertSelector.java
+++ b/luni/src/main/java/java/security/cert/X509CertSelector.java
@@ -30,6 +30,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
+import libcore.base.EmptyArray;
import org.apache.harmony.security.asn1.ASN1OctetString;
import org.apache.harmony.security.x509.AlgorithmIdentifier;
import org.apache.harmony.security.x509.CertificatePolicies;
@@ -1281,8 +1282,7 @@ public class X509CertSelector implements CertSelector {
// initialize the check map
for (int i=0; i<9; i++) {
map[i] = (subjectAltNames[i] == null)
- ? new boolean[0]
- : new boolean[subjectAltNames[i].size()];
+ ? EmptyArray.BOOLEAN : new boolean[subjectAltNames[i].size()];
}
Iterator it = sans.iterator();
while (it.hasNext()) {
diff --git a/luni/src/main/java/java/text/ChoiceFormat.java b/luni/src/main/java/java/text/ChoiceFormat.java
index 4d689df..b594efc 100644
--- a/luni/src/main/java/java/text/ChoiceFormat.java
+++ b/luni/src/main/java/java/text/ChoiceFormat.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
+import libcore.base.EmptyArray;
/**
* Returns a fixed string based on a numeric value. The class can be used in
@@ -147,8 +148,8 @@ public class ChoiceFormat extends NumberFormat {
index = skipWhitespace(template, position.getIndex());
if (position.getErrorIndex() != -1 || index >= length) {
// Fix Harmony 540
- choiceLimits = new double[0];
- choiceFormats = new String[0];
+ choiceLimits = EmptyArray.DOUBLE;
+ choiceFormats = EmptyArray.STRING;
return;
}
char ch = template.charAt(index++);
diff --git a/luni/src/main/java/java/text/MessageFormat.java b/luni/src/main/java/java/text/MessageFormat.java
index d740437..5abc9c8 100644
--- a/luni/src/main/java/java/text/MessageFormat.java
+++ b/luni/src/main/java/java/text/MessageFormat.java
@@ -27,6 +27,7 @@ import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
import java.util.Vector;
+import libcore.base.EmptyArray;
import libcore.base.Objects;
/**
@@ -866,7 +867,7 @@ public class MessageFormat extends Format {
*/
public Object[] parse(String string, ParsePosition position) {
if (string == null) {
- return new Object[0];
+ return EmptyArray.OBJECT;
}
ParsePosition internalPos = new ParsePosition(0);
int offset = position.getIndex();
diff --git a/luni/src/main/java/java/util/AbstractQueue.java b/luni/src/main/java/java/util/AbstractQueue.java
index 8ec69f9..d368ac9 100644
--- a/luni/src/main/java/java/util/AbstractQueue.java
+++ b/luni/src/main/java/java/util/AbstractQueue.java
@@ -1,127 +1,163 @@
-/* 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.
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
*/
package java.util;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
- * AbstractQueue is an abstract class which implements some of the methods in
- * {@link Queue}. The provided implementations of {@code add, remove} and
- * {@code element} are based on {@code offer, poll}, and {@code peek} except
- * that they throw exceptions to indicate some error instead of returning true
- * or false.
+ * This class provides skeletal implementations of some {@link Queue}
+ * operations. The implementations in this class are appropriate when
+ * the base implementation does <em>not</em> allow <tt>null</tt>
+ * elements. Methods {@link #add add}, {@link #remove remove}, and
+ * {@link #element element} are based on {@link #offer offer}, {@link
+ * #poll poll}, and {@link #peek peek}, respectively, but throw
+ * exceptions instead of indicating failure via <tt>false</tt> or
+ * <tt>null</tt> returns.
+ *
+ * <p>A <tt>Queue</tt> implementation that extends this class must
+ * minimally define a method {@link Queue#offer} which does not permit
+ * insertion of <tt>null</tt> elements, along with methods {@link
+ * Queue#peek}, {@link Queue#poll}, {@link Collection#size}, and
+ * {@link Collection#iterator}. Typically, additional methods will be
+ * overridden as well. If these requirements cannot be met, consider
+ * instead subclassing {@link AbstractCollection}.
*
- * @param <E>
- * the type of the element in the collection.
+ * @since 1.5
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
*/
-public abstract class AbstractQueue<E> extends AbstractCollection<E> implements
- Queue<E> {
+public abstract class AbstractQueue<E>
+ extends AbstractCollection<E>
+ implements Queue<E> {
/**
- * Constructor to be used by subclasses.
+ * Constructor for use by subclasses.
*/
protected AbstractQueue() {
- super();
}
/**
- * Adds an element to the queue.
+ * Inserts the specified element into this queue if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * <tt>true</tt> upon success and throwing an <tt>IllegalStateException</tt>
+ * if no space is currently available.
*
- * @param o
- * the element to be added to the queue.
- * @return {@code true} if the operation succeeds, otherwise {@code false}.
- * @throws IllegalStateException
- * if the element is not allowed to be added to the queue.
+ * <p>This implementation returns <tt>true</tt> if <tt>offer</tt> succeeds,
+ * else throws an <tt>IllegalStateException</tt>.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Collection#add})
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null and
+ * this queue does not permit null elements
+ * @throws IllegalArgumentException if some property of this element
+ * prevents it from being added to this queue
*/
- @Override
- public boolean add(E o) {
- if (null == o) {
- throw new NullPointerException();
- }
- if (offer(o)) {
+ public boolean add(E e) {
+ if (offer(e))
return true;
- }
- throw new IllegalStateException();
+ else
+ throw new IllegalStateException("Queue full");
}
/**
- * Adds all the elements of a collection to the queue. If the collection is
- * the queue itself, then an IllegalArgumentException will be thrown. If
- * during the process, some runtime exception is thrown, then those elements
- * in the collection which have already successfully been added will remain
- * in the queue. The result of the method is undefined if the collection is
- * modified during the process of the method.
+ * Retrieves and removes the head of this queue. This method differs
+ * from {@link #poll poll} only in that it throws an exception if this
+ * queue is empty.
*
- * @param c
- * the collection to be added to the queue.
- * @return {@code true} if the operation succeeds, otherwise {@code false}.
- * @throws NullPointerException
- * if the collection or any element of it is null.
- * @throws IllegalArgumentException
- * If the collection to be added to the queue is the queue
- * itself.
- */
- @Override
- public boolean addAll(Collection<? extends E> c) {
- if (null == c) {
- throw new NullPointerException();
- }
- if (this == c) {
- throw new IllegalArgumentException();
- }
- return super.addAll(c);
- }
-
- /**
- * Removes the element at the head of the queue and returns it.
+ * <p>This implementation returns the result of <tt>poll</tt>
+ * unless the queue is empty.
*
- * @return the element at the head of the queue.
- * @throws NoSuchElementException
- * if the queue is empty.
+ * @return the head of this queue
+ * @throws NoSuchElementException if this queue is empty
*/
public E remove() {
- E o = poll();
- if (null == o) {
+ E x = poll();
+ if (x != null)
+ return x;
+ else
throw new NoSuchElementException();
- }
- return o;
}
/**
- * Returns but does not remove the element at the head of the queue.
+ * Retrieves, but does not remove, the head of this queue. This method
+ * differs from {@link #peek peek} only in that it throws an exception if
+ * this queue is empty.
*
- * @return the element at the head of the queue.
- * @throws NoSuchElementException
- * if the queue is empty.
+ * <p>This implementation returns the result of <tt>peek</tt>
+ * unless the queue is empty.
+ *
+ * @return the head of this queue
+ * @throws NoSuchElementException if this queue is empty
*/
public E element() {
- E o = peek();
- if (null == o) {
+ E x = peek();
+ if (x != null)
+ return x;
+ else
throw new NoSuchElementException();
- }
- return o;
}
/**
- * Removes all elements of the queue, leaving it empty.
+ * Removes all of the elements from this queue.
+ * The queue will be empty after this call returns.
+ *
+ * <p>This implementation repeatedly invokes {@link #poll poll} until it
+ * returns <tt>null</tt>.
*/
- @Override
public void clear() {
- E o;
- do {
- o = poll();
- } while (null != o);
+ while (poll() != null)
+ ;
+ }
+
+ /**
+ * Adds all of the elements in the specified collection to this
+ * queue. Attempts to addAll of a queue to itself result in
+ * <tt>IllegalArgumentException</tt>. Further, the behavior of
+ * this operation is undefined if the specified collection is
+ * modified while the operation is in progress.
+ *
+ * <p>This implementation iterates over the specified collection,
+ * and adds each element returned by the iterator to this
+ * queue, in turn. A runtime exception encountered while
+ * trying to add an element (including, in particular, a
+ * <tt>null</tt> element) may result in only some of the elements
+ * having been successfully added when the associated exception is
+ * thrown.
+ *
+ * @param c collection containing elements to be added to this queue
+ * @return <tt>true</tt> if this queue changed as a result of the call
+ * @throws ClassCastException if the class of an element of the specified
+ * collection prevents it from being added to this queue
+ * @throws NullPointerException if the specified collection contains a
+ * null element and this queue does not permit null elements,
+ * or if the specified collection is null
+ * @throws IllegalArgumentException if some property of an element of the
+ * specified collection prevents it from being added to this
+ * queue, or if the specified collection is this queue
+ * @throws IllegalStateException if not all the elements can be added at
+ * this time due to insertion restrictions
+ * @see #add(Object)
+ */
+ public boolean addAll(Collection<? extends E> c) {
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ boolean modified = false;
+ for (E e : c)
+ if (add(e))
+ modified = true;
+ return modified;
}
+
}
diff --git a/luni/src/main/java/java/util/ArrayDeque.java b/luni/src/main/java/java/util/ArrayDeque.java
index feaac4d..fafcdb4 100644
--- a/luni/src/main/java/java/util/ArrayDeque.java
+++ b/luni/src/main/java/java/util/ArrayDeque.java
@@ -1,883 +1,838 @@
/*
- * 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.
+ * Written by Josh Bloch of Google Inc. and released to the public domain,
+ * as explained at http://creativecommons.org/licenses/publicdomain.
*/
package java.util;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.lang.reflect.Array;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
+import java.io.*;
/**
- * An implementation of Deque, backed by an array.
+ * Resizable-array implementation of the {@link Deque} interface. Array
+ * deques have no capacity restrictions; they grow as necessary to support
+ * usage. They are not thread-safe; in the absence of external
+ * synchronization, they do not support concurrent access by multiple threads.
+ * Null elements are prohibited. This class is likely to be faster than
+ * {@link Stack} when used as a stack, and faster than {@link LinkedList}
+ * when used as a queue.
+ *
+ * <p>Most <tt>ArrayDeque</tt> operations run in amortized constant time.
+ * Exceptions include {@link #remove(Object) remove}, {@link
+ * #removeFirstOccurrence removeFirstOccurrence}, {@link #removeLastOccurrence
+ * removeLastOccurrence}, {@link #contains contains}, {@link #iterator
+ * iterator.remove()}, and the bulk operations, all of which run in linear
+ * time.
*
- * ArrayDeques have no size limit, can not contain null element, and they are
- * not thread-safe.
+ * <p>The iterators returned by this class's <tt>iterator</tt> method are
+ * <i>fail-fast</i>: If the deque is modified at any time after the iterator
+ * is created, in any way except through the iterator's own <tt>remove</tt>
+ * method, the iterator will generally throw a {@link
+ * ConcurrentModificationException}. Thus, in the face of concurrent
+ * modification, the iterator fails quickly and cleanly, rather than risking
+ * arbitrary, non-deterministic behavior at an undetermined time in the
+ * future.
*
- * All optional operations are supported, and the elements can be any objects.
+ * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
+ * as it is, generally speaking, impossible to make any hard guarantees in the
+ * presence of unsynchronized concurrent modification. Fail-fast iterators
+ * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
+ * Therefore, it would be wrong to write a program that depended on this
+ * exception for its correctness: <i>the fail-fast behavior of iterators
+ * should be used only to detect bugs.</i>
*
- * @param <E>
- * the type of elements in this collection
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
*
- * @since 1.6
+ * @author Josh Bloch and Doug Lea
+ * @since 1.6
+ * @param <E> the type of elements held in this collection
*/
-public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>,
- Cloneable, Serializable {
-
- private static final long serialVersionUID = 2340985798034038923L;
-
- private static final int DEFAULT_SIZE = 16;
-
- private enum DequeStatus {
- Empty, Normal, Full;
- }
-
- private transient DequeStatus status;
-
- private transient int modCount;
-
- // the pointer of the head element
- private transient int front;
-
- // the pointer of the "next" position of the tail element
- private transient int rear;
-
+public class ArrayDeque<E> extends AbstractCollection<E>
+ implements Deque<E>, Cloneable, Serializable
+{
+ /**
+ * The array in which the elements of the deque are stored.
+ * The capacity of the deque is the length of this array, which is
+ * always a power of two. The array is never allowed to become
+ * full, except transiently within an addX method where it is
+ * resized (see doubleCapacity) immediately upon becoming full,
+ * thus avoiding head and tail wrapping around to equal each
+ * other. We also guarantee that all array cells not holding
+ * deque elements are always null.
+ */
private transient E[] elements;
- @SuppressWarnings("hiding")
- private class ArrayDequeIterator<E> implements Iterator<E> {
- private int pos;
-
- private final int expectedModCount;
-
- private boolean canRemove;
+ /**
+ * The index of the element at the head of the deque (which is the
+ * element that would be removed by remove() or pop()); or an
+ * arbitrary number equal to tail if the deque is empty.
+ */
+ private transient int head;
- @SuppressWarnings("synthetic-access")
- ArrayDequeIterator() {
- super();
- pos = front;
- expectedModCount = modCount;
- canRemove = false;
- }
+ /**
+ * The index at which the next element would be added to the tail
+ * of the deque (via addLast(E), add(E), or push(E)).
+ */
+ private transient int tail;
- @SuppressWarnings("synthetic-access")
- public boolean hasNext() {
- if (expectedModCount != modCount) {
- return false;
- }
- return hasNextInternal();
- }
+ /**
+ * The minimum capacity that we'll use for a newly created deque.
+ * Must be a power of 2.
+ */
+ private static final int MIN_INITIAL_CAPACITY = 8;
- private boolean hasNextInternal() {
- // canRemove means "next" method is called, and the Full
- // status can ensure that this method is not called just
- // after "remove" method is call.(so, canRemove can keep
- // true after "next" method called)
- return (pos != rear)
- || ((status == DequeStatus.Full) && !canRemove);
- }
+ // ****** Array allocation and resizing utilities ******
- @SuppressWarnings( { "synthetic-access", "unchecked" })
- public E next() {
- if (hasNextInternal()) {
- E result = (E) elements[pos];
- if (expectedModCount == modCount && null != result) {
- canRemove = true;
- pos = circularBiggerPos(pos);
- return result;
- }
- throw new ConcurrentModificationException();
- }
- throw new NoSuchElementException();
- }
+ /**
+ * Allocate empty array to hold the given number of elements.
+ *
+ * @param numElements the number of elements to hold
+ */
+ private void allocateElements(int numElements) {
+ int initialCapacity = MIN_INITIAL_CAPACITY;
+ // Find the best power of two to hold elements.
+ // Tests "<=" because arrays aren't kept full.
+ if (numElements >= initialCapacity) {
+ initialCapacity = numElements;
+ initialCapacity |= (initialCapacity >>> 1);
+ initialCapacity |= (initialCapacity >>> 2);
+ initialCapacity |= (initialCapacity >>> 4);
+ initialCapacity |= (initialCapacity >>> 8);
+ initialCapacity |= (initialCapacity >>> 16);
+ initialCapacity++;
- @SuppressWarnings("synthetic-access")
- public void remove() {
- if (canRemove) {
- int removedPos = circularSmallerPos(pos);
- if (expectedModCount == modCount
- && null != elements[removedPos]) {
- removeInternal(removedPos, true);
- canRemove = false;
- return;
- }
- throw new ConcurrentModificationException();
- }
- throw new IllegalStateException();
+ if (initialCapacity < 0) // Too many elements, must back off
+ initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
+ elements = (E[]) new Object[initialCapacity];
}
- /*
- * NOTES:descendingIterator is not fail-fast, according to the documentation
- * and test case.
+ /**
+ * Double the capacity of this deque. Call only when full, i.e.,
+ * when head and tail have wrapped around to become equal.
*/
- @SuppressWarnings("hiding")
- private class ReverseArrayDequeIterator<E> implements Iterator<E> {
- private int pos;
-
- private final int expectedModCount;
-
- private boolean canRemove;
-
- @SuppressWarnings("synthetic-access")
- ReverseArrayDequeIterator() {
- super();
- expectedModCount = modCount;
- pos = circularSmallerPos(rear);
- canRemove = false;
- }
-
- @SuppressWarnings("synthetic-access")
- public boolean hasNext() {
- if (expectedModCount != modCount) {
- return false;
- }
- return hasNextInternal();
- }
-
- private boolean hasNextInternal() {
- // canRemove means "next" method is called, and the Full
- // status can ensure that this method is not called just
- // after "remove" method is call.(so, canRemove can keep
- // true after "next" method called)
- return (circularBiggerPos(pos) != front)
- || ((status == DequeStatus.Full) && !canRemove);
- }
-
- @SuppressWarnings( { "synthetic-access", "unchecked" })
- public E next() {
- if (hasNextInternal()) {
- E result = (E) elements[pos];
- canRemove = true;
- pos = circularSmallerPos(pos);
- return result;
- }
- throw new NoSuchElementException();
- }
+ private void doubleCapacity() {
+ assert head == tail;
+ int p = head;
+ int n = elements.length;
+ int r = n - p; // number of elements to the right of p
+ int newCapacity = n << 1;
+ if (newCapacity < 0)
+ throw new IllegalStateException("Sorry, deque too big");
+ Object[] a = new Object[newCapacity];
+ System.arraycopy(elements, p, a, 0, r);
+ System.arraycopy(elements, 0, a, r, p);
+ elements = (E[])a;
+ head = 0;
+ tail = n;
+ }
- @SuppressWarnings("synthetic-access")
- public void remove() {
- if (canRemove) {
- removeInternal(circularBiggerPos(pos), false);
- canRemove = false;
- return;
- }
- throw new IllegalStateException();
+ /**
+ * Copies the elements from our element array into the specified array,
+ * in order (from first to last element in the deque). It is assumed
+ * that the array is large enough to hold all elements in the deque.
+ *
+ * @return its argument
+ */
+ private <T> T[] copyElements(T[] a) {
+ if (head < tail) {
+ System.arraycopy(elements, head, a, 0, size());
+ } else if (head > tail) {
+ int headPortionLen = elements.length - head;
+ System.arraycopy(elements, head, a, 0, headPortionLen);
+ System.arraycopy(elements, 0, a, headPortionLen, tail);
}
+ return a;
}
/**
- * Constructs a new empty instance of ArrayDeque big enough for 16 elements.
+ * Constructs an empty array deque with an initial capacity
+ * sufficient to hold 16 elements.
*/
public ArrayDeque() {
- this(DEFAULT_SIZE);
+ elements = (E[]) new Object[16];
}
/**
- * Constructs a new empty instance of ArrayDeque big enough for specified
- * number of elements.
+ * Constructs an empty array deque with an initial capacity
+ * sufficient to hold the specified number of elements.
*
- * @param minSize
- * the smallest size of the ArrayDeque
- */
- @SuppressWarnings("unchecked")
- public ArrayDeque(final int minSize) {
- int size = countInitSize(minSize);
- elements = (E[]) new Object[size];
- front = rear = 0;
- status = DequeStatus.Empty;
- modCount = 0;
- }
-
- /*
- * count out the size for a new deque, and ensure that size >= minSize
+ * @param numElements lower bound on initial capacity of the deque
*/
- private int countInitSize(final int minSize) {
- int size = Math.max(minSize, DEFAULT_SIZE);
- // get the smallest number that not smaller than size,
- // and is a power of 2.
- size = Integer.highestOneBit(size - 1) << 1;
- if (0 >= size) {
- size = minSize;
- }
- return size;
+ public ArrayDeque(int numElements) {
+ allocateElements(numElements);
}
/**
- * Constructs a new instance of ArrayDeque containing the elements of the
- * specified collection, with the order returned by the collection's
- * iterator.
+ * Constructs a deque containing the elements of the specified
+ * collection, in the order they are returned by the collection's
+ * iterator. (The first element returned by the collection's
+ * iterator becomes the first element, or <i>front</i> of the
+ * deque.)
*
- * @param c
- * the source of the elements
- * @throws NullPointerException
- * if the collection is null
+ * @param c the collection whose elements are to be placed into the deque
+ * @throws NullPointerException if the specified collection is null
*/
- @SuppressWarnings("unchecked")
public ArrayDeque(Collection<? extends E> c) {
- elements = (E[]) new Object[countInitSize(c.size())];
- front = rear = 0;
- status = DequeStatus.Empty;
- modCount = 0;
- Iterator<? extends E> it = c.iterator();
- while (it.hasNext()) {
- addLastImpl(it.next());
- }
+ allocateElements(c.size());
+ addAll(c);
}
+ // The main insertion and extraction methods are addFirst,
+ // addLast, pollFirst, pollLast. The other methods are defined in
+ // terms of these.
+
/**
- * {@inheritDoc}
+ * Inserts the specified element at the front of this deque.
*
- * @param e
- * the element
- * @throws NullPointerException
- * if the element is null
- * @see java.util.Deque#addFirst(java.lang.Object)
+ * @param e the element to add
+ * @throws NullPointerException if the specified element is null
*/
public void addFirst(E e) {
- offerFirst(e);
+ if (e == null)
+ throw new NullPointerException();
+ elements[head = (head - 1) & (elements.length - 1)] = e;
+ if (head == tail)
+ doubleCapacity();
}
/**
- * {@inheritDoc}
+ * Inserts the specified element at the end of this deque.
+ *
+ * <p>This method is equivalent to {@link #add}.
*
- * @param e
- * the element
- * @throws NullPointerException
- * if the element is null
- * @see java.util.Deque#addLast(java.lang.Object)
+ * @param e the element to add
+ * @throws NullPointerException if the specified element is null
*/
public void addLast(E e) {
- addLastImpl(e);
+ if (e == null)
+ throw new NullPointerException();
+ elements[tail] = e;
+ if ( (tail = (tail + 1) & (elements.length - 1)) == head)
+ doubleCapacity();
}
/**
- * {@inheritDoc}
+ * Inserts the specified element at the front of this deque.
*
- * @param e
- * the element
- * @return true
- * @throws NullPointerException
- * if the element is null
- * @see java.util.Deque#offerFirst(java.lang.Object)
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Deque#offerFirst})
+ * @throws NullPointerException if the specified element is null
*/
- public boolean offerFirst(E e) {
- checkNull(e);
- checkAndExpand();
- front = circularSmallerPos(front);
- elements[front] = e;
- resetStatus(true);
- modCount++;
+ public boolean offerFirst(E e) {
+ addFirst(e);
return true;
}
/**
- * {@inheritDoc}
+ * Inserts the specified element at the end of this deque.
*
- * @param e
- * the element
- * @return true if the operation succeeds or false if it fails
- * @throws NullPointerException
- * if the element is null
- * @see java.util.Deque#offerLast(java.lang.Object)
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Deque#offerLast})
+ * @throws NullPointerException if the specified element is null
*/
public boolean offerLast(E e) {
- return addLastImpl(e);
+ addLast(e);
+ return true;
}
/**
- * Inserts the element at the tail of the deque.
- *
- * @param e
- * the element
- * @return true if the operation succeeds or false if it fails.
- * @throws NullPointerException
- * if the element is null
- * @see java.util.Queue#offer(java.lang.Object)
+ * @throws NoSuchElementException {@inheritDoc}
*/
- public boolean offer(E e) {
- return addLastImpl(e);
+ public E removeFirst() {
+ E x = pollFirst();
+ if (x == null)
+ throw new NoSuchElementException();
+ return x;
}
/**
- * Inserts the element to the tail of the deque.
- *
- * @param e
- * the element
- * @return true
- * @see java.util.AbstractCollection#add(java.lang.Object)
+ * @throws NoSuchElementException {@inheritDoc}
*/
- @Override
- public boolean add(E e) {
- return addLastImpl(e);
+ public E removeLast() {
+ E x = pollLast();
+ if (x == null)
+ throw new NoSuchElementException();
+ return x;
}
- /**
- * {@inheritDoc}
- *
- * @param e
- * the element to push
- * @throws NullPointerException
- * if the element is null
- * @see java.util.Deque#push(java.lang.Object)
- */
- public void push(E e) {
- offerFirst(e);
+ public E pollFirst() {
+ int h = head;
+ E result = elements[h]; // Element is null if deque empty
+ if (result == null)
+ return null;
+ elements[h] = null; // Must null out slot
+ head = (h + 1) & (elements.length - 1);
+ return result;
+ }
+
+ public E pollLast() {
+ int t = (tail - 1) & (elements.length - 1);
+ E result = elements[t];
+ if (result == null)
+ return null;
+ elements[t] = null;
+ tail = t;
+ return result;
}
/**
- * {@inheritDoc}
- *
- * @return the head element
- * @throws NoSuchElementException
- * if the deque is empty
- * @see java.util.Deque#removeFirst()
+ * @throws NoSuchElementException {@inheritDoc}
*/
- public E removeFirst() {
- checkEmpty();
- return removePollFirstImpl();
+ public E getFirst() {
+ E x = elements[head];
+ if (x == null)
+ throw new NoSuchElementException();
+ return x;
}
/**
- * Gets and removes the head element of this deque. This method throws an
- * exception if the deque is empty.
- *
- * @return the head element
- * @throws NoSuchElementException
- * if the deque is empty
- * @see java.util.Queue#remove()
+ * @throws NoSuchElementException {@inheritDoc}
*/
- public E remove() {
- return removeFirst();
+ public E getLast() {
+ E x = elements[(tail - 1) & (elements.length - 1)];
+ if (x == null)
+ throw new NoSuchElementException();
+ return x;
+ }
+
+ public E peekFirst() {
+ return elements[head]; // elements[head] is null if deque empty
+ }
+
+ public E peekLast() {
+ return elements[(tail - 1) & (elements.length - 1)];
}
/**
- * {@inheritDoc}
+ * Removes the first occurrence of the specified element in this
+ * deque (when traversing the deque from head to tail).
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element <tt>e</tt> such that
+ * <tt>o.equals(e)</tt> (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
*
- * @return the head element
- * @throws NoSuchElementException
- * if the deque is empty
- * @see java.util.Deque#pop()
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if the deque contained the specified element
*/
- public E pop() {
- return removeFirst();
+ public boolean removeFirstOccurrence(Object o) {
+ if (o == null)
+ return false;
+ int mask = elements.length - 1;
+ int i = head;
+ E x;
+ while ( (x = elements[i]) != null) {
+ if (o.equals(x)) {
+ delete(i);
+ return true;
+ }
+ i = (i + 1) & mask;
+ }
+ return false;
}
/**
- * {@inheritDoc}
- *
- * @return the tail element
- * @throws NoSuchElementException
- * if the deque is empty
- * @see java.util.Deque#removeLast()
- */
- public E removeLast() {
- checkEmpty();
- return removeLastImpl();
+ * Removes the last occurrence of the specified element in this
+ * deque (when traversing the deque from head to tail).
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the last element <tt>e</tt> such that
+ * <tt>o.equals(e)</tt> (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if the deque contained the specified element
+ */
+ public boolean removeLastOccurrence(Object o) {
+ if (o == null)
+ return false;
+ int mask = elements.length - 1;
+ int i = (tail - 1) & mask;
+ E x;
+ while ( (x = elements[i]) != null) {
+ if (o.equals(x)) {
+ delete(i);
+ return true;
+ }
+ i = (i - 1) & mask;
+ }
+ return false;
}
+ // *** Queue methods ***
+
/**
- * {@inheritDoc}
+ * Inserts the specified element at the end of this deque.
*
- * @return the head element or null if the deque is empty
- * @see java.util.Deque#pollFirst()
+ * <p>This method is equivalent to {@link #addLast}.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Collection#add})
+ * @throws NullPointerException if the specified element is null
*/
- public E pollFirst() {
- return (status == DequeStatus.Empty) ? null : removePollFirstImpl();
+ public boolean add(E e) {
+ addLast(e);
+ return true;
}
/**
- * Gets and removes the head element of this deque. This method returns null
- * if the deque is empty.
+ * Inserts the specified element at the end of this deque.
+ *
+ * <p>This method is equivalent to {@link #offerLast}.
*
- * @return the head element or null if the deque is empty
- * @see java.util.Queue#poll()
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Queue#offer})
+ * @throws NullPointerException if the specified element is null
*/
- public E poll() {
- return pollFirst();
+ public boolean offer(E e) {
+ return offerLast(e);
}
/**
- * {@inheritDoc}
+ * Retrieves and removes the head of the queue represented by this deque.
+ *
+ * This method differs from {@link #poll poll} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #removeFirst}.
*
- * @return the tail element or null if the deque is empty
- * @see java.util.Deque#pollLast()
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException {@inheritDoc}
*/
- public E pollLast() {
- return (status == DequeStatus.Empty) ? null : removeLastImpl();
+ public E remove() {
+ return removeFirst();
}
/**
- * {@inheritDoc}
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque), or returns
+ * <tt>null</tt> if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #pollFirst}.
*
- * @return the head element
- * @throws NoSuchElementException
- * if the deque is empty
- * @see java.util.Deque#getFirst()
+ * @return the head of the queue represented by this deque, or
+ * <tt>null</tt> if this deque is empty
*/
- public E getFirst() {
- checkEmpty();
- return elements[front];
+ public E poll() {
+ return pollFirst();
}
/**
- * Gets but does not remove the head element of this deque. It throws an
- * exception if the deque is empty.
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque. This method differs from {@link #peek peek} only in
+ * that it throws an exception if this deque is empty.
*
- * @return the head element
- * @throws NoSuchElementException
- * if the deque is empty
- * @see java.util.Queue#element()
+ * <p>This method is equivalent to {@link #getFirst}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException {@inheritDoc}
*/
public E element() {
return getFirst();
}
/**
- * {@inheritDoc}
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque, or returns <tt>null</tt> if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #peekFirst}.
*
- * @return the tail element
- * @throws NoSuchElementException
- * if the deque is empty
- * @see java.util.Deque#getLast()
+ * @return the head of the queue represented by this deque, or
+ * <tt>null</tt> if this deque is empty
*/
- public E getLast() {
- checkEmpty();
- return elements[circularSmallerPos(rear)];
+ public E peek() {
+ return peekFirst();
}
+ // *** Stack methods ***
+
/**
- * {@inheritDoc}
+ * Pushes an element onto the stack represented by this deque. In other
+ * words, inserts the element at the front of this deque.
+ *
+ * <p>This method is equivalent to {@link #addFirst}.
*
- * @return the head element or null if the deque is empty
- * @see java.util.Deque#peekFirst()
+ * @param e the element to push
+ * @throws NullPointerException if the specified element is null
*/
- public E peekFirst() {
- return (status == DequeStatus.Empty) ? null : elements[front];
+ public void push(E e) {
+ addFirst(e);
}
/**
- * Gets but not removes the head element of this deque. This method returns
- * null if the deque is empty.
+ * Pops an element from the stack represented by this deque. In other
+ * words, removes and returns the first element of this deque.
+ *
+ * <p>This method is equivalent to {@link #removeFirst()}.
*
- * @return the head element or null if the deque is empty
- * @see java.util.Queue#peek()
+ * @return the element at the front of this deque (which is the top
+ * of the stack represented by this deque)
+ * @throws NoSuchElementException {@inheritDoc}
*/
- public E peek() {
- return (status == DequeStatus.Empty) ? null : elements[front];
+ public E pop() {
+ return removeFirst();
+ }
+
+ private void checkInvariants() {
+ assert elements[tail] == null;
+ assert head == tail ? elements[head] == null :
+ (elements[head] != null &&
+ elements[(tail - 1) & (elements.length - 1)] != null);
+ assert elements[(head - 1) & (elements.length - 1)] == null;
}
/**
- * {@inheritDoc}
+ * Removes the element at the specified position in the elements array,
+ * adjusting head and tail as necessary. This can result in motion of
+ * elements backwards or forwards in the array.
*
- * @return the tail element or null if the deque is empty
- * @see java.util.Deque#peekLast()
+ * <p>This method is called delete rather than remove to emphasize
+ * that its semantics differ from those of {@link List#remove(int)}.
+ *
+ * @return true if elements moved backwards
*/
- public E peekLast() {
- return (status == DequeStatus.Empty) ? null
- : elements[circularSmallerPos(rear)];
- }
+ private boolean delete(int i) {
+ checkInvariants();
+ final E[] elements = this.elements;
+ final int mask = elements.length - 1;
+ final int h = head;
+ final int t = tail;
+ final int front = (i - h) & mask;
+ final int back = (t - i) & mask;
- private void checkNull(E e) {
- if (null == e) {
- throw new NullPointerException();
- }
- }
+ // Invariant: head <= i < tail mod circularity
+ if (front >= ((t - h) & mask))
+ throw new ConcurrentModificationException();
- private void checkEmpty() {
- if (status == DequeStatus.Empty) {
- throw new NoSuchElementException();
+ // Optimize for least element motion
+ if (front < back) {
+ if (h <= i) {
+ System.arraycopy(elements, h, elements, h + 1, front);
+ } else { // Wrap around
+ System.arraycopy(elements, 0, elements, 1, i);
+ elements[0] = elements[mask];
+ System.arraycopy(elements, h, elements, h + 1, mask - h);
+ }
+ elements[h] = null;
+ head = (h + 1) & mask;
+ return false;
+ } else {
+ if (i < t) { // Copy the null tail as well
+ System.arraycopy(elements, i + 1, elements, i, back);
+ tail = t - 1;
+ } else { // Wrap around
+ System.arraycopy(elements, i + 1, elements, i, mask - i);
+ elements[mask] = elements[0];
+ System.arraycopy(elements, 1, elements, 0, t);
+ tail = (t - 1) & mask;
+ }
+ return true;
}
}
- private int circularSmallerPos(int current) {
- return (current - 1 < 0) ? (elements.length - 1) : current - 1;
- }
+ // *** Collection Methods ***
- private int circularBiggerPos(int current) {
- return (current + 1 >= elements.length) ? 0 : current + 1;
+ /**
+ * Returns the number of elements in this deque.
+ *
+ * @return the number of elements in this deque
+ */
+ public int size() {
+ return (tail - head) & (elements.length - 1);
}
- @SuppressWarnings("unchecked")
- /*
- * If array of elements is full, there will be a new bigger array to store
- * the elements.
+ /**
+ * Returns <tt>true</tt> if this deque contains no elements.
+ *
+ * @return <tt>true</tt> if this deque contains no elements
*/
- private void checkAndExpand() {
- if (status != DequeStatus.Full) {
- return;
- }
- if (Integer.MAX_VALUE == elements.length) {
- throw new IllegalStateException();
- }
- int length = elements.length;
- int newLength = length * 2;
- // bigger than Integer.MAX_VALUE
- if (newLength < 0) {
- newLength = Integer.MAX_VALUE;
- }
- E[] newElements = (E[]) new Object[newLength];
- System.arraycopy(elements, front, newElements, 0, length - front);
- System.arraycopy(elements, 0, newElements, length - front, front);
- front = 0;
- rear = length;
- status = DequeStatus.Normal;
- elements = newElements;
+ public boolean isEmpty() {
+ return head == tail;
}
/**
- * Resets the status after adding or removing operation.
+ * Returns an iterator over the elements in this deque. The elements
+ * will be ordered from first (head) to last (tail). This is the same
+ * order that elements would be dequeued (via successive calls to
+ * {@link #remove} or popped (via successive calls to {@link #pop}).
*
- * @param adding
- * if the method is called after an "adding" operation
+ * @return an iterator over the elements in this deque
*/
- private void resetStatus(boolean adding) {
- if (front == rear) {
- status = adding ? DequeStatus.Full : DequeStatus.Empty;
- } else {
- status = DequeStatus.Normal;
- }
+ public Iterator<E> iterator() {
+ return new DeqIterator();
}
- private boolean addLastImpl(E e) {
- checkNull(e);
- checkAndExpand();
- elements[rear] = e;
- rear = circularBiggerPos(rear);
- resetStatus(true);
- modCount++;
- return true;
+ public Iterator<E> descendingIterator() {
+ return new DescendingIterator();
}
- private E removePollFirstImpl() {
- E element = elements[front];
- elements[front] = null;
- front = circularBiggerPos(front);
- resetStatus(false);
- modCount++;
- return element;
- }
+ private class DeqIterator implements Iterator<E> {
+ /**
+ * Index of element to be returned by subsequent call to next.
+ */
+ private int cursor = head;
- private E removeLastImpl() {
- int last = circularSmallerPos(rear);
- E element = elements[last];
- elements[last] = null;
- rear = last;
- resetStatus(false);
- modCount++;
- return element;
- }
+ /**
+ * Tail recorded at construction (also in remove), to stop
+ * iterator and also to check for comodification.
+ */
+ private int fence = tail;
- /**
- * {@inheritDoc}
- *
- * @param obj
- * the element to be removed
- * @return true if the operation succeeds or false if the deque does not
- * contain the element
- * @see java.util.Deque#removeFirstOccurrence(java.lang.Object)
- */
- public boolean removeFirstOccurrence(Object obj) {
- return removeFirstOccurrenceImpl(obj);
- }
+ /**
+ * Index of element returned by most recent call to next.
+ * Reset to -1 if element is deleted by a call to remove.
+ */
+ private int lastRet = -1;
- /**
- * Removes the first equivalent element of the specified object. If the
- * deque does not contain the element, it is unchanged and returns false.
- *
- * @param obj
- * the element to be removed
- * @return true if the operation succeeds or false if the deque does not
- * contain the element
- * @see java.util.AbstractCollection#remove(java.lang.Object)
- */
- @Override
- public boolean remove(Object obj) {
- return removeFirstOccurrenceImpl(obj);
- }
+ public boolean hasNext() {
+ return cursor != fence;
+ }
- /**
- * {@inheritDoc}
- *
- * @param obj
- * the element to be removed
- * @return true if the operation succeeds or false if the deque does not
- * contain the element.
- * @see java.util.Deque#removeLastOccurrence(java.lang.Object)
- */
- public boolean removeLastOccurrence(final Object obj) {
- if (null != obj) {
- Iterator<E> iter = descendingIterator();
- while (iter.hasNext()) {
- if (iter.next().equals(obj)) {
- iter.remove();
- return true;
- }
- }
+ public E next() {
+ if (cursor == fence)
+ throw new NoSuchElementException();
+ E result = elements[cursor];
+ // This check doesn't catch all possible comodifications,
+ // but does catch the ones that corrupt traversal
+ if (tail != fence || result == null)
+ throw new ConcurrentModificationException();
+ lastRet = cursor;
+ cursor = (cursor + 1) & (elements.length - 1);
+ return result;
}
- return false;
- }
- private boolean removeFirstOccurrenceImpl(final Object obj) {
- if (null != obj) {
- Iterator<E> iter = iterator();
- while (iter.hasNext()) {
- if (iter.next().equals(obj)) {
- iter.remove();
- return true;
- }
+ public void remove() {
+ if (lastRet < 0)
+ throw new IllegalStateException();
+ if (delete(lastRet)) { // if left-shifted, undo increment in next()
+ cursor = (cursor - 1) & (elements.length - 1);
+ fence = tail;
}
+ lastRet = -1;
}
- return false;
}
- /*
- * Removes the element in the cursor position and shifts front elements to
- * fill the gap if frontShift is true, shifts rear elements otherwise.
- *
- */
- private void removeInternal(final int current, final boolean frontShift) {
- int cursor = current;
- if (frontShift) {
- while (cursor != front) {
- int next = circularSmallerPos(cursor);
- elements[cursor] = elements[next];
- cursor = next;
- }
- front = circularBiggerPos(front);
- } else {
- while (cursor != rear) {
- int next = circularBiggerPos(cursor);
- elements[cursor] = elements[next];
- cursor = next;
- }
- rear = circularSmallerPos(rear);
+ private class DescendingIterator implements Iterator<E> {
+ /*
+ * This class is nearly a mirror-image of DeqIterator, using
+ * tail instead of head for initial cursor, and head instead of
+ * tail for fence.
+ */
+ private int cursor = tail;
+ private int fence = head;
+ private int lastRet = -1;
+
+ public boolean hasNext() {
+ return cursor != fence;
}
- elements[cursor] = null;
- resetStatus(false);
- }
- /**
- * Returns the size of the deque.
- *
- * @return the size of the deque
- * @see java.util.AbstractCollection#size()
- */
- @Override
- public int size() {
- if (status == DequeStatus.Full) {
- return elements.length;
+ public E next() {
+ if (cursor == fence)
+ throw new NoSuchElementException();
+ cursor = (cursor - 1) & (elements.length - 1);
+ E result = elements[cursor];
+ if (head != fence || result == null)
+ throw new ConcurrentModificationException();
+ lastRet = cursor;
+ return result;
}
- return (front <= rear) ? (rear - front)
- : (rear + elements.length - front);
- }
- /**
- * Returns true if the deque has no elements.
- *
- * @return true if the deque has no elements, false otherwise
- * @see java.util.AbstractCollection#isEmpty()
- */
- @Override
- public boolean isEmpty() {
- return 0 == size();
+ public void remove() {
+ if (lastRet < 0)
+ throw new IllegalStateException();
+ if (!delete(lastRet)) {
+ cursor = (cursor + 1) & (elements.length - 1);
+ fence = head;
+ }
+ lastRet = -1;
+ }
}
/**
- * Returns true if the specified element is in the deque.
+ * Returns <tt>true</tt> if this deque contains the specified element.
+ * More formally, returns <tt>true</tt> if and only if this deque contains
+ * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.
*
- * @param obj
- * the element
- * @return true if the element is in the deque, false otherwise
- * @see java.util.AbstractCollection#contains(java.lang.Object)
+ * @param o object to be checked for containment in this deque
+ * @return <tt>true</tt> if this deque contains the specified element
*/
- @SuppressWarnings("cast")
- @Override
- public boolean contains(final Object obj) {
- if (null != obj) {
- Iterator<E> it = new ArrayDequeIterator<E>();
- while (it.hasNext()) {
- if (obj.equals((E) it.next())) {
- return true;
- }
- }
+ public boolean contains(Object o) {
+ if (o == null)
+ return false;
+ int mask = elements.length - 1;
+ int i = head;
+ E x;
+ while ( (x = elements[i]) != null) {
+ if (o.equals(x))
+ return true;
+ i = (i + 1) & mask;
}
return false;
}
/**
- * Empty the deque.
+ * Removes a single instance of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element <tt>e</tt> such that
+ * <tt>o.equals(e)</tt> (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
*
- * @see java.util.AbstractCollection#clear()
+ * <p>This method is equivalent to {@link #removeFirstOccurrence}.
+ *
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if this deque contained the specified element
*/
- @SuppressWarnings("cast")
- @Override
- public void clear() {
- if (status != DequeStatus.Empty) {
- int cursor = front;
- do {
- elements[cursor] = null;
- cursor = circularBiggerPos(cursor);
- } while (cursor != rear);
- status = DequeStatus.Empty;
- }
- front = rear = 0;
- modCount = 0;
+ public boolean remove(Object o) {
+ return removeFirstOccurrence(o);
}
/**
- * Returns a clone of the deque.
- *
- * @return the clone of the deque
- * @see java.lang.Object#clone()
- * @see java.lang.Cloneable
+ * Removes all of the elements from this deque.
+ * The deque will be empty after this call returns.
*/
- @SuppressWarnings("unchecked")
- @Override
- public ArrayDeque<E> clone() {
- try {
- ArrayDeque<E> newDeque = (ArrayDeque<E>) super.clone();
- newDeque.elements = elements.clone();
- return newDeque;
- } catch (CloneNotSupportedException e) {
- return null;
+ public void clear() {
+ int h = head;
+ int t = tail;
+ if (h != t) { // clear all cells
+ head = tail = 0;
+ int i = h;
+ int mask = elements.length - 1;
+ do {
+ elements[i] = null;
+ i = (i + 1) & mask;
+ } while (i != t);
}
}
/**
- * Returns all the elements in an array from head to tail. The result is a
- * copy of all the elements.
+ * Returns an array containing all of the elements in this deque
+ * in proper sequence (from first to last element).
+ *
+ * <p>The returned array will be "safe" in that no references to it are
+ * maintained by this deque. (In other words, this method must allocate
+ * a new array). The caller is thus free to modify the returned array.
+ *
+ * <p>This method acts as bridge between array-based and collection-based
+ * APIs.
*
- * @return the Array of all the elements
- * @see java.util.AbstractCollection#toArray()
+ * @return an array containing all of the elements in this deque
*/
- @Override
public Object[] toArray() {
- return newArray(new Object[size()]);
+ return copyElements(new Object[size()]);
}
/**
- * Returns all the elements in an array from head to tail, and the type of
- * the result array is the type of the argument array. If the argument array
- * is big enough, the elements from the deque will be stored in it(elements
- * following the tail of the deque is set to null, if any); otherwise, it
- * will return a new array with the size of the argument array and size of
- * the deque.
+ * Returns an array containing all of the elements in this deque in
+ * proper sequence (from first to last element); the runtime type of the
+ * returned array is that of the specified array. If the deque fits in
+ * the specified array, it is returned therein. Otherwise, a new array
+ * is allocated with the runtime type of the specified array and the
+ * size of this deque.
*
- * @param array
- * the array stores all the elements from the deque, if it has
- * enough space; otherwise, a new array of the same type and the
- * size of the deque will be used
- * @return the Array of all the elements
- * @throws ArrayStoreException
- * if the type of the argument array is not compatible with
- * every element in the deque
- * @throws NullPointerException
- * if the argument array is null
- * @see java.util.AbstractCollection#toArray
+ * <p>If this deque fits in the specified array with room to spare
+ * (i.e., the array has more elements than this deque), the element in
+ * the array immediately following the end of the deque is set to
+ * <tt>null</tt>.
+ *
+ * <p>Like the {@link #toArray()} method, this method acts as bridge between
+ * array-based and collection-based APIs. Further, this method allows
+ * precise control over the runtime type of the output array, and may,
+ * under certain circumstances, be used to save allocation costs.
+ *
+ * <p>Suppose <tt>x</tt> is a deque known to contain only strings.
+ * The following code can be used to dump the deque into a newly
+ * allocated array of <tt>String</tt>:
+ *
+ * <pre>
+ * String[] y = x.toArray(new String[0]);</pre>
+ *
+ * Note that <tt>toArray(new Object[0])</tt> is identical in function to
+ * <tt>toArray()</tt>.
+ *
+ * @param a the array into which the elements of the deque are to
+ * be stored, if it is big enough; otherwise, a new array of the
+ * same runtime type is allocated for this purpose
+ * @return an array containing all of the elements in this deque
+ * @throws ArrayStoreException if the runtime type of the specified array
+ * is not a supertype of the runtime type of every element in
+ * this deque
+ * @throws NullPointerException if the specified array is null
*/
- @Override
- public <T> T[] toArray(T[] array) {
- return newArray(array);
- }
-
- @SuppressWarnings("unchecked")
- private <T> T[] newArray(T[] array) {
+ public <T> T[] toArray(T[] a) {
int size = size();
- if (size > array.length) {
- Class<?> clazz = array.getClass().getComponentType();
- array = (T[]) Array.newInstance(clazz, size);
- }
- if (front < rear) {
- System.arraycopy(elements, front, array, 0, size);
- } else if (size != 0) {
- int length = elements.length;
- System.arraycopy(elements, front, array, 0, length - front);
- System.arraycopy(elements, 0, array, length - front, rear);
- }
- if (size < array.length) {
- array[size] = null;
- }
- return array;
+ if (a.length < size)
+ a = (T[])java.lang.reflect.Array.newInstance(
+ a.getClass().getComponentType(), size);
+ copyElements(a);
+ if (a.length > size)
+ a[size] = null;
+ return a;
}
+ // *** Object methods ***
+
/**
- * Returns the iterator of the deque. The elements will be ordered from head
- * to tail.
+ * Returns a copy of this deque.
*
- * @return the iterator
- * @see java.util.AbstractCollection#iterator()
+ * @return a copy of this deque
*/
- @SuppressWarnings("synthetic-access")
- @Override
- public Iterator<E> iterator() {
- return new ArrayDequeIterator<E>();
+ public ArrayDeque<E> clone() {
+ try {
+ ArrayDeque<E> result = (ArrayDeque<E>) super.clone();
+ result.elements = Arrays.copyOf(elements, elements.length);
+ return result;
+
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError();
+ }
}
/**
- * {@inheritDoc}
- *
- * @return the reverse order Iterator
- * @see java.util.Deque#descendingIterator()
+ * Appease the serialization gods.
*/
- public Iterator<E> descendingIterator() {
- return new ReverseArrayDequeIterator<E>();
- }
+ private static final long serialVersionUID = 2340985798034038923L;
/**
- * Deserialization method.
+ * Serialize this deque.
*
- * @param stream
- * the ObjectInputStream
- * @throws IOException
- * @throws ClassNotFoundException
+ * @serialData The current size (<tt>int</tt>) of the deque,
+ * followed by all of its elements (each an object reference) in
+ * first-to-last order.
*/
- @SuppressWarnings("unchecked")
- private void readObject(ObjectInputStream stream) throws IOException,
- ClassNotFoundException {
- stream.defaultReadObject();
- int size = stream.readInt();
- elements = (E[]) new Object[countInitSize(size)];
- front = rear = 0;
- status = DequeStatus.Empty;
- modCount = 0;
- for (int i = 0; i < size; i++) {
- addLastImpl((E) stream.readObject());
- }
+ private void writeObject(ObjectOutputStream s) throws IOException {
+ s.defaultWriteObject();
+
+ // Write out size
+ s.writeInt(size());
+
+ // Write out elements in order.
+ int mask = elements.length - 1;
+ for (int i = head; i != tail; i = (i + 1) & mask)
+ s.writeObject(elements[i]);
}
/**
- * Serialization method.
- *
- * @param stream
- * the ObjectOutputStream
- * @serialData The current size of the deque, followed by all the elements
- * from head to tail.
- * @throws IOException
- *
+ * Deserialize this deque.
*/
- private void writeObject(ObjectOutputStream stream) throws IOException {
- stream.defaultWriteObject();
- stream.writeInt(size());
- Iterator<?> it = new ArrayDequeIterator<E>();
- while (it.hasNext()) {
- stream.writeObject(it.next());
- }
- }
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ s.defaultReadObject();
+
+ // Read in size and allocate array
+ int size = s.readInt();
+ allocateElements(size);
+ head = 0;
+ tail = size;
+ // Read in all elements in the proper order.
+ for (int i = 0; i < size; i++)
+ elements[i] = (E)s.readObject();
+ }
}
diff --git a/luni/src/main/java/java/util/ArrayList.java b/luni/src/main/java/java/util/ArrayList.java
index 19d2449..d07ca60 100644
--- a/luni/src/main/java/java/util/ArrayList.java
+++ b/luni/src/main/java/java/util/ArrayList.java
@@ -27,6 +27,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
+import libcore.base.EmptyArray;
/**
* ArrayList is an implementation of {@link List}, backed by an array.
@@ -44,13 +45,7 @@ import java.lang.reflect.Array;
* @param <E> The element type of this list.
* @since 1.2
*/
-public class ArrayList<E> extends AbstractList<E>
- implements Cloneable, Serializable, RandomAccess {
- /**
- * An empty array of objects (to be shared among all empty lists).
- */
- private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
-
+public class ArrayList<E> extends AbstractList<E> implements Cloneable, Serializable, RandomAccess {
/**
* The minimum amount by which the capacity of an ArrayList will increase.
* This tuning parameter controls a time-space tradeoff. This value (12)
@@ -81,14 +76,14 @@ public class ArrayList<E> extends AbstractList<E>
if (capacity < 0) {
throw new IllegalArgumentException();
}
- array = (capacity == 0 ? EMPTY_OBJECT_ARRAY : new Object[capacity]);
+ array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
}
/**
* Constructs a new {@code ArrayList} instance with zero initial capacity.
*/
public ArrayList() {
- array = EMPTY_OBJECT_ARRAY;
+ array = EmptyArray.OBJECT;
}
/**
@@ -542,7 +537,7 @@ public class ArrayList<E> extends AbstractList<E>
return;
}
if (s == 0) {
- array = EMPTY_OBJECT_ARRAY;
+ array = EmptyArray.OBJECT;
} else {
Object[] newArray = new Object[s];
System.arraycopy(array, 0, newArray, 0, s);
@@ -652,15 +647,14 @@ public class ArrayList<E> extends AbstractList<E>
}
}
- private void readObject(ObjectInputStream stream) throws IOException,
- ClassNotFoundException {
+ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
int cap = stream.readInt();
if (cap < size) {
throw new InvalidObjectException(
"Capacity: " + cap + " < size: " + size);
}
- array = (cap == 0 ? EMPTY_OBJECT_ARRAY : new Object[cap]);
+ array = (cap == 0 ? EmptyArray.OBJECT : new Object[cap]);
for (int i = 0; i < size; i++) {
array[i] = stream.readObject();
}
diff --git a/luni/src/main/java/java/util/Arrays.java b/luni/src/main/java/java/util/Arrays.java
index d5bfbc8..5f38db3 100644
--- a/luni/src/main/java/java/util/Arrays.java
+++ b/luni/src/main/java/java/util/Arrays.java
@@ -114,15 +114,9 @@ public class Arrays {
@Override
public E set(int location, E object) {
- try {
- E result = a[location];
- a[location] = object;
- return result;
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new IndexOutOfBoundsException();
- } catch (ArrayStoreException e) {
- throw new ClassCastException();
- }
+ E result = a[location];
+ a[location] = object;
+ return result;
}
@Override
diff --git a/luni/src/main/java/java/util/Deque.java b/luni/src/main/java/java/util/Deque.java
index 7ccc6f4..cb6bd90 100644
--- a/luni/src/main/java/java/util/Deque.java
+++ b/luni/src/main/java/java/util/Deque.java
@@ -1,255 +1,548 @@
/*
- * 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.
+ * Written by Doug Lea and Josh Bloch with assistance from members of
+ * JCP JSR-166 Expert Group and released to the public domain, as explained
+ * at http://creativecommons.org/licenses/publicdomain
*/
package java.util;
+// BEGIN android-note
+// removed link to collections framework docs
+// changed {@link #offer(Object)} to {@link #offer} to satisfy DroidDoc
+// END android-note
+
/**
- * A kind of collection that can insert or remove element at both ends("double
- * ended queue"). Mostly a deque has no limit of its size.
+ * A linear collection that supports element insertion and removal at
+ * both ends. The name <i>deque</i> is short for "double ended queue"
+ * and is usually pronounced "deck". Most <tt>Deque</tt>
+ * implementations place no fixed limits on the number of elements
+ * they may contain, but this interface supports capacity-restricted
+ * deques as well as those with no fixed size limit.
+ *
+ * <p>This interface defines methods to access the elements at both
+ * ends of the deque. Methods are provided to insert, remove, and
+ * examine the element. Each of these methods exists in two forms:
+ * one throws an exception if the operation fails, the other returns a
+ * special value (either <tt>null</tt> or <tt>false</tt>, depending on
+ * the operation). The latter form of the insert operation is
+ * designed specifically for use with capacity-restricted
+ * <tt>Deque</tt> implementations; in most implementations, insert
+ * operations cannot fail.
+ *
+ * <p>The twelve methods described above are summarized in the
+ * following table:
+ *
+ * <p>
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <tr>
+ * <td></td>
+ * <td ALIGN=CENTER COLSPAN = 2> <b>First Element (Head)</b></td>
+ * <td ALIGN=CENTER COLSPAN = 2> <b>Last Element (Tail)</b></td>
+ * </tr>
+ * <tr>
+ * <td></td>
+ * <td ALIGN=CENTER><em>Throws exception</em></td>
+ * <td ALIGN=CENTER><em>Special value</em></td>
+ * <td ALIGN=CENTER><em>Throws exception</em></td>
+ * <td ALIGN=CENTER><em>Special value</em></td>
+ * </tr>
+ * <tr>
+ * <td><b>Insert</b></td>
+ * <td>{@link #addFirst addFirst(e)}</td>
+ * <td>{@link #offerFirst offerFirst(e)}</td>
+ * <td>{@link #addLast addLast(e)}</td>
+ * <td>{@link #offerLast offerLast(e)}</td>
+ * </tr>
+ * <tr>
+ * <td><b>Remove</b></td>
+ * <td>{@link #removeFirst removeFirst()}</td>
+ * <td>{@link #pollFirst pollFirst()}</td>
+ * <td>{@link #removeLast removeLast()}</td>
+ * <td>{@link #pollLast pollLast()}</td>
+ * </tr>
+ * <tr>
+ * <td><b>Examine</b></td>
+ * <td>{@link #getFirst getFirst()}</td>
+ * <td>{@link #peekFirst peekFirst()}</td>
+ * <td>{@link #getLast getLast()}</td>
+ * <td>{@link #peekLast peekLast()}</td>
+ * </tr>
+ * </table>
+ *
+ * <p>This interface extends the {@link Queue} interface. When a deque is
+ * used as a queue, FIFO (First-In-First-Out) behavior results. Elements are
+ * added at the end of the deque and removed from the beginning. The methods
+ * inherited from the <tt>Queue</tt> interface are precisely equivalent to
+ * <tt>Deque</tt> methods as indicated in the following table:
+ *
+ * <p>
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <tr>
+ * <td ALIGN=CENTER> <b><tt>Queue</tt> Method</b></td>
+ * <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.util.Queue#add add(e)}</td>
+ * <td>{@link #addLast addLast(e)}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.util.Queue#offer offer(e)}</td>
+ * <td>{@link #offerLast offerLast(e)}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.util.Queue#remove remove()}</td>
+ * <td>{@link #removeFirst removeFirst()}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.util.Queue#poll poll()}</td>
+ * <td>{@link #pollFirst pollFirst()}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.util.Queue#element element()}</td>
+ * <td>{@link #getFirst getFirst()}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link java.util.Queue#peek peek()}</td>
+ * <td>{@link #peek peekFirst()}</td>
+ * </tr>
+ * </table>
+ *
+ * <p>Deques can also be used as LIFO (Last-In-First-Out) stacks. This
+ * interface should be used in preference to the legacy {@link Stack} class.
+ * When a deque is used as a stack, elements are pushed and popped from the
+ * beginning of the deque. Stack methods are precisely equivalent to
+ * <tt>Deque</tt> methods as indicated in the table below:
*
- * <p>Extending from Queue, a deque can be used as a Queue which behavior is
- * first-in-first-out. Furthermore, a deque can also be used as a Stack(legacy
- * class) which behavior is last-in-first-out.
+ * <p>
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <tr>
+ * <td ALIGN=CENTER> <b>Stack Method</b></td>
+ * <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #push push(e)}</td>
+ * <td>{@link #addFirst addFirst(e)}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #pop pop()}</td>
+ * <td>{@link #removeFirst removeFirst()}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #peek peek()}</td>
+ * <td>{@link #peekFirst peekFirst()}</td>
+ * </tr>
+ * </table>
*
- * <p>A typical deque does not allow null to be inserted as its element, while some
- * implementations allow it. But null should not be inserted even in these
- * implementations, since method poll return null to indicate that there is no
- * element left in the deque.
+ * <p>Note that the {@link #peek peek} method works equally well when
+ * a deque is used as a queue or a stack; in either case, elements are
+ * drawn from the beginning of the deque.
*
- * <p>A deque can also remove interior elements by removeFirstOccurrence and
- * removeLastOccurrence methods. A deque can not access elements by index.
+ * <p>This interface provides two methods to remove interior
+ * elements, {@link #removeFirstOccurrence removeFirstOccurrence} and
+ * {@link #removeLastOccurrence removeLastOccurrence}.
*
- * @param <E>
- * the type of elements in this collection
- * @since 1.6
+ * <p>Unlike the {@link List} interface, this interface does not
+ * provide support for indexed access to elements.
+ *
+ * <p>While <tt>Deque</tt> implementations are not strictly required
+ * to prohibit the insertion of null elements, they are strongly
+ * encouraged to do so. Users of any <tt>Deque</tt> implementations
+ * that do allow null elements are strongly encouraged <i>not</i> to
+ * take advantage of the ability to insert nulls. This is so because
+ * <tt>null</tt> is used as a special return value by various methods
+ * to indicated that the deque is empty.
+ *
+ * <p><tt>Deque</tt> implementations generally do not define
+ * element-based versions of the <tt>equals</tt> and <tt>hashCode</tt>
+ * methods, but instead inherit the identity-based versions from class
+ * <tt>Object</tt>.
+ *
+ * @author Doug Lea
+ * @author Josh Bloch
+ * @since 1.6
+ * @param <E> the type of elements held in this collection
*/
-public interface Deque<E> extends Queue<E> {
+public interface Deque<E> extends Queue<E> {
/**
- * Inserts an element at the head of this deque if it dose not violate size
- * limit immediately. It is better to use offerFirst(E) if a deque is
- * size-limited.
+ * Inserts the specified element at the front of this deque if it is
+ * possible to do so immediately without violating capacity restrictions.
+ * When using a capacity-restricted deque, it is generally preferable to
+ * use method {@link #offerFirst}.
*
- * @param e
- * the element
- * @throws IllegalStateException
- * if it can not add now due to size limit
- * @throws ClassCastException
- * if the class of element can not be added into this deque
- * @throws NullPointerException
- * if the element is null and the deque can not contain null
- * element
- * @throws IllegalArgumentException
- * if the element can not be added due to some property.
+ * @param e the element to add
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
*/
void addFirst(E e);
/**
- * Inserts an element at the tail of this deque if it dose not violate size
- * limit immediately. It is better to use offerLast(E) if a deque is
- * size-limited.
+ * Inserts the specified element at the end of this deque if it is
+ * possible to do so immediately without violating capacity restrictions.
+ * When using a capacity-restricted deque, it is generally preferable to
+ * use method {@link #offerLast}.
*
- * @param e
- * the element
- * @throws IllegalStateException
- * if it can not add now due to size limit
- * @throws ClassCastException
- * if the class of element can not be added into this deque
- * @throws NullPointerException
- * if the element is null and the deque can not contain null
- * element
- * @throws IllegalArgumentException
- * if the element can not be added due to some property.
+ * <p>This method is equivalent to {@link #add}.
+ *
+ * @param e the element to add
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
*/
void addLast(E e);
/**
- * Inserts an element at the head of this deque unless it would violate size
- * limit. It is better than the addFirst(E) method in a size-limited deque,
- * because the latter one may fail to add the element only by throwing an
- * exception.
+ * Inserts the specified element at the front of this deque unless it would
+ * violate capacity restrictions. When using a capacity-restricted deque,
+ * this method is generally preferable to the {@link #addFirst} method,
+ * which can fail to insert an element only by throwing an exception.
*
- * @param e
- * the element
- * @return true if the operation succeeds or false if it fails.
- * @throws ClassCastException
- * if the class of element can not be added into this deque
- * @throws NullPointerException
- * if the element is null and the deque can not contain null
- * element
- * @throws IllegalArgumentException
- * if the element can not be added due to some property.
+ * @param e the element to add
+ * @return <tt>true</tt> if the element was added to this deque, else
+ * <tt>false</tt>
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
*/
boolean offerFirst(E e);
/**
- * Inserts an element at the tail of this deque unless it would violate size
- * limit. It is better than the addLast(E) method in a size-limited deque,
- * because the latter one may fail to add the element only by throwing an
- * exception.
+ * Inserts the specified element at the end of this deque unless it would
+ * violate capacity restrictions. When using a capacity-restricted deque,
+ * this method is generally preferable to the {@link #addLast} method,
+ * which can fail to insert an element only by throwing an exception.
*
- * @param e
- * the element
- * @return true if the operation succeeds or false if it fails
- * @throws ClassCastException
- * if the class of element can not be added into this deque
- * @throws NullPointerException
- * if the element is null and the deque can not contain null
- * element
- * @throws IllegalArgumentException
- * if the element can not be added due to some property
+ * @param e the element to add
+ * @return <tt>true</tt> if the element was added to this deque, else
+ * <tt>false</tt>
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
*/
boolean offerLast(E e);
/**
- * Gets and removes the head element of this deque. This method throws an
- * exception if the deque is empty.
+ * Retrieves and removes the first element of this deque. This method
+ * differs from {@link #pollFirst pollFirst} only in that it throws an
+ * exception if this deque is empty.
*
- * @return the head element
- * @throws NoSuchElementException
- * if the deque is empty
+ * @return the head of this deque
+ * @throws NoSuchElementException if this deque is empty
*/
E removeFirst();
/**
- * Gets and removes the tail element of this deque. This method throws an
- * exception if the deque is empty.
+ * Retrieves and removes the last element of this deque. This method
+ * differs from {@link #pollLast pollLast} only in that it throws an
+ * exception if this deque is empty.
*
- * @return the tail element
- * @throws NoSuchElementException
- * if the deque is empty
+ * @return the tail of this deque
+ * @throws NoSuchElementException if this deque is empty
*/
E removeLast();
/**
- * Gets and removes the head element of this deque. This method returns null
- * if the deque is empty.
+ * Retrieves and removes the first element of this deque,
+ * or returns <tt>null</tt> if this deque is empty.
*
- * @return the head element or null if the deque is empty
+ * @return the head of this deque, or <tt>null</tt> if this deque is empty
*/
E pollFirst();
/**
- * Gets and removes the tail element of this deque. This method returns null
- * if the deque is empty.
+ * Retrieves and removes the last element of this deque,
+ * or returns <tt>null</tt> if this deque is empty.
*
- * @return the tail element or null if the deque is empty
+ * @return the tail of this deque, or <tt>null</tt> if this deque is empty
*/
E pollLast();
/**
- * Gets but not removes the head element of this deque. This method throws
- * an exception if the deque is empty.
+ * Retrieves, but does not remove, the first element of this deque.
+ *
+ * This method differs from {@link #peekFirst peekFirst} only in that it
+ * throws an exception if this deque is empty.
*
- * @return the head element
- * @throws NoSuchElementException
- * if the deque is empty
+ * @return the head of this deque
+ * @throws NoSuchElementException if this deque is empty
*/
E getFirst();
/**
- * Gets but not removes the tail element of this deque. This method throws
- * an exception if the deque is empty.
+ * Retrieves, but does not remove, the last element of this deque.
+ * This method differs from {@link #peekLast peekLast} only in that it
+ * throws an exception if this deque is empty.
*
- * @return the tail element
- * @throws NoSuchElementException
- * if the deque is empty
+ * @return the tail of this deque
+ * @throws NoSuchElementException if this deque is empty
*/
E getLast();
/**
- * Gets but not removes the head element of this deque. This method returns
- * null if the deque is empty.
+ * Retrieves, but does not remove, the first element of this deque,
+ * or returns <tt>null</tt> if this deque is empty.
*
- * @return the head element or null if the deque is empty
+ * @return the head of this deque, or <tt>null</tt> if this deque is empty
*/
E peekFirst();
/**
- * Gets but not removes the tail element of this deque. This method returns
- * null if the deque is empty.
+ * Retrieves, but does not remove, the last element of this deque,
+ * or returns <tt>null</tt> if this deque is empty.
*
- * @return the tail element or null if the deque is empty
+ * @return the tail of this deque, or <tt>null</tt> if this deque is empty
*/
E peekLast();
/**
- * Removes the first equivalent element of the specified object. If the
- * deque does not contain the element, it is unchanged and returns false.
+ * Removes the first occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
+ * (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
*
- * @param o
- * the element to be removed
- * @return true if the operation succeeds or false if the deque does not
- * contain the element.
- * @throws ClassCastException
- * if the class of the element is incompatible with the deque
- * @throws NullPointerException
- * if the element is null and the deque can not contain null
- * element
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if an element was removed as a result of this call
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
*/
boolean removeFirstOccurrence(Object o);
/**
- * Removes the last equivalent element of the specified object. If the deque
- * does not contain the element, it is unchanged and returns false.
+ * Removes the last occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the last element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
+ * (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
*
- * @param o
- * the element to be removed
- * @return true if the operation succeeds or false if the deque does not
- * contain the element.
- * @throws ClassCastException
- * if the class of the element is incompatible with the deque
- * @throws NullPointerException
- * if the element is null and the deque can not contain null
- * element
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if an element was removed as a result of this call
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
*/
boolean removeLastOccurrence(Object o);
+ // *** Queue methods ***
+
+ /**
+ * Inserts the specified element into the queue represented by this deque
+ * (in other words, at the tail of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * <tt>true</tt> upon success and throwing an
+ * <tt>IllegalStateException</tt> if no space is currently available.
+ * When using a capacity-restricted deque, it is generally preferable to
+ * use {@link #offer offer}.
+ *
+ * <p>This method is equivalent to {@link #addLast}.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Collection#add})
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean add(E e);
+
+ /**
+ * Inserts the specified element into the queue represented by this deque
+ * (in other words, at the tail of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * <tt>true</tt> upon success and <tt>false</tt> if no space is currently
+ * available. When using a capacity-restricted deque, this method is
+ * generally preferable to the {@link #add} method, which can fail to
+ * insert an element only by throwing an exception.
+ *
+ * <p>This method is equivalent to {@link #offerLast}.
+ *
+ * @param e the element to add
+ * @return <tt>true</tt> if the element was added to this deque, else
+ * <tt>false</tt>
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
+ */
+ boolean offer(E e);
+
/**
- * Pushes the element to the deque(at the head of the deque), just same as
- * addFirst(E).
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque).
+ * This method differs from {@link #poll poll} only in that it throws an
+ * exception if this deque is empty.
*
- * @param e
- * the element
- * @throws IllegalStateException
- * if it can not add now due to size limit
- * @throws ClassCastException
- * if the class of element can not be added into this deque
- * @throws NullPointerException
- * if the element is null and the deque can not contain null
- * element
- * @throws IllegalArgumentException
- * if the element can not be added due to some property.
+ * <p>This method is equivalent to {@link #removeFirst()}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E remove();
+
+ /**
+ * Retrieves and removes the head of the queue represented by this deque
+ * (in other words, the first element of this deque), or returns
+ * <tt>null</tt> if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #pollFirst()}.
+ *
+ * @return the first element of this deque, or <tt>null</tt> if
+ * this deque is empty
+ */
+ E poll();
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque (in other words, the first element of this deque).
+ * This method differs from {@link #peek peek} only in that it throws an
+ * exception if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #getFirst()}.
+ *
+ * @return the head of the queue represented by this deque
+ * @throws NoSuchElementException if this deque is empty
+ */
+ E element();
+
+ /**
+ * Retrieves, but does not remove, the head of the queue represented by
+ * this deque (in other words, the first element of this deque), or
+ * returns <tt>null</tt> if this deque is empty.
+ *
+ * <p>This method is equivalent to {@link #peekFirst()}.
+ *
+ * @return the head of the queue represented by this deque, or
+ * <tt>null</tt> if this deque is empty
+ */
+ E peek();
+
+
+ // *** Stack methods ***
+
+ /**
+ * Pushes an element onto the stack represented by this deque (in other
+ * words, at the head of this deque) if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * <tt>true</tt> upon success and throwing an
+ * <tt>IllegalStateException</tt> if no space is currently available.
+ *
+ * <p>This method is equivalent to {@link #addFirst}.
+ *
+ * @param e the element to push
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this deque
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements
+ * @throws IllegalArgumentException if some property of the specified
+ * element prevents it from being added to this deque
*/
void push(E e);
/**
- * Pops the head element of the deque, just same as removeFirst().
+ * Pops an element from the stack represented by this deque. In other
+ * words, removes and returns the first element of this deque.
*
- * @return the head element
- * @throws NoSuchElementException
- * if the deque is empty
+ * <p>This method is equivalent to {@link #removeFirst()}.
+ *
+ * @return the element at the front of this deque (which is the top
+ * of the stack represented by this deque)
+ * @throws NoSuchElementException if this deque is empty
*/
E pop();
+
+ // *** Collection methods ***
+
/**
- * Returns the iterator in reverse order, from tail to head.
+ * Removes the first occurrence of the specified element from this deque.
+ * If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
+ * (if such an element exists).
+ * Returns <tt>true</tt> if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * <p>This method is equivalent to {@link #removeFirstOccurrence}.
*
- * @return the iterator in reverse order
+ * @param o element to be removed from this deque, if present
+ * @return <tt>true</tt> if an element was removed as a result of this call
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
+ */
+ boolean remove(Object o);
+
+ /**
+ * Returns <tt>true</tt> if this deque contains the specified element.
+ * More formally, returns <tt>true</tt> if and only if this deque contains
+ * at least one element <tt>e</tt> such that
+ * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
+ *
+ * @param o element whose presence in this deque is to be tested
+ * @return <tt>true</tt> if this deque contains the specified element
+ * @throws ClassCastException if the type of the specified element
+ * is incompatible with this deque (optional)
+ * @throws NullPointerException if the specified element is null and this
+ * deque does not permit null elements (optional)
+ */
+ boolean contains(Object o);
+
+ /**
+ * Returns the number of elements in this deque.
+ *
+ * @return the number of elements in this deque
+ */
+ public int size();
+
+ /**
+ * Returns an iterator over the elements in this deque in proper sequence.
+ * The elements will be returned in order from first (head) to last (tail).
+ *
+ * @return an iterator over the elements in this deque in proper sequence
+ */
+ Iterator<E> iterator();
+
+ /**
+ * Returns an iterator over the elements in this deque in reverse
+ * sequential order. The elements will be returned in order from
+ * last (tail) to first (head).
+ *
+ * @return an iterator over the elements in this deque in reverse
+ * sequence
*/
Iterator<E> descendingIterator();
-} \ No newline at end of file
+
+}
diff --git a/luni/src/main/java/java/util/EnumMap.java b/luni/src/main/java/java/util/EnumMap.java
index 2ccaff1..36ff03a 100644
--- a/luni/src/main/java/java/util/EnumMap.java
+++ b/luni/src/main/java/java/util/EnumMap.java
@@ -769,9 +769,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
if (null == key) {
throw new NullPointerException();
}
- if (!isValidKeyType(key)) {
- throw new ClassCastException();
- }
+ keyType.cast(key); // Called to throw ClassCastException.
int keyOrdinal = key.ordinal();
if (!hasMapping[keyOrdinal]) {
hasMapping[keyOrdinal] = true;
diff --git a/luni/src/main/java/java/util/EnumSet.java b/luni/src/main/java/java/util/EnumSet.java
index 48bfd3a..1b4723e 100644
--- a/luni/src/main/java/java/util/EnumSet.java
+++ b/luni/src/main/java/java/util/EnumSet.java
@@ -55,7 +55,7 @@ public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
*/
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
if (!elementType.isEnum()) {
- throw new ClassCastException();
+ throw new ClassCastException(elementType.getClass().getName() + " is not an Enum");
}
// BEGIN android-changed
E[] enums = SpecialAccess.LANG.getEnumValuesInOrder(elementType);
diff --git a/luni/src/main/java/java/util/HugeEnumSet.java b/luni/src/main/java/java/util/HugeEnumSet.java
index ef90d8e..2c6395f 100644
--- a/luni/src/main/java/java/util/HugeEnumSet.java
+++ b/luni/src/main/java/java/util/HugeEnumSet.java
@@ -121,10 +121,7 @@ final class HugeEnumSet<E extends Enum<E>> extends EnumSet<E> {
@Override
public boolean add(E element) {
- if (!isValidType(element.getDeclaringClass())) {
- throw new ClassCastException();
- }
-
+ elementClass.cast(element); // Called to throw ClassCastException.
int ordinal = element.ordinal();
int index = ordinal / BIT_IN_LONG;
int inBits = ordinal % BIT_IN_LONG;
@@ -146,9 +143,7 @@ final class HugeEnumSet<E extends Enum<E>> extends EnumSet<E> {
if (collection instanceof EnumSet) {
EnumSet<?> set = (EnumSet) collection; // raw type due to javac bug 6548436
- if (!isValidType(set.elementClass)) {
- throw new ClassCastException();
- }
+ set.elementClass.asSubclass(elementClass); // Called to throw ClassCastException.
HugeEnumSet<E> hugeSet = (HugeEnumSet<E>) set;
boolean changed = false;
diff --git a/luni/src/main/java/java/util/IdentityHashMap.java b/luni/src/main/java/java/util/IdentityHashMap.java
index 53d3f09..827ffcf 100644
--- a/luni/src/main/java/java/util/IdentityHashMap.java
+++ b/luni/src/main/java/java/util/IdentityHashMap.java
@@ -83,8 +83,11 @@ public class IdentityHashMap<K, V> extends AbstractMap<K, V> implements
private static final Object NULL_OBJECT = new Object(); //$NON-LOCK-1$
static class IdentityHashMapEntry<K, V> extends MapEntry<K, V> {
- IdentityHashMapEntry(K theKey, V theValue) {
+ private final IdentityHashMap<K,V> map;
+
+ IdentityHashMapEntry(IdentityHashMap<K,V> map, K theKey, V theValue) {
super(theKey, theValue);
+ this.map = map;
}
@Override
@@ -114,6 +117,13 @@ public class IdentityHashMap<K, V> extends AbstractMap<K, V> implements
public String toString() {
return key + "=" + value;
}
+
+ @Override
+ public V setValue(V object) {
+ V result = super.setValue(object);
+ map.put(key, object);
+ return result;
+ }
}
static class IdentityHashMapIterator<E, KT, VT> implements Iterator<E> {
@@ -407,7 +417,7 @@ public class IdentityHashMap<K, V> extends AbstractMap<K, V> implements
value = null;
}
- return new IdentityHashMapEntry<K, V>((K) key, (V) value);
+ return new IdentityHashMapEntry<K, V>(this, (K) key, (V) value);
}
/**
diff --git a/luni/src/main/java/java/util/MiniEnumSet.java b/luni/src/main/java/java/util/MiniEnumSet.java
index e19612a..c4c7364 100644
--- a/luni/src/main/java/java/util/MiniEnumSet.java
+++ b/luni/src/main/java/java/util/MiniEnumSet.java
@@ -108,10 +108,7 @@ final class MiniEnumSet<E extends Enum<E>> extends EnumSet<E> {
@Override
public boolean add(E element) {
- if (!isValidType(element.getDeclaringClass())) {
- throw new ClassCastException();
- }
-
+ elementClass.cast(element); // Called to throw ClassCastException.
long oldBits = bits;
long newBits = oldBits | (1L << element.ordinal());
if (oldBits != newBits) {
@@ -129,9 +126,7 @@ final class MiniEnumSet<E extends Enum<E>> extends EnumSet<E> {
}
if (collection instanceof EnumSet) {
EnumSet<?> set = (EnumSet) collection; // raw type due to javac bug 6548436
- if (!isValidType(set.elementClass)) {
- throw new ClassCastException();
- }
+ set.elementClass.asSubclass(elementClass); // Called to throw ClassCastException.
MiniEnumSet<?> miniSet = (MiniEnumSet<?>) set;
long oldBits = bits;
diff --git a/luni/src/main/java/java/util/NavigableMap.java b/luni/src/main/java/java/util/NavigableMap.java
index a87a8c0..29961c8 100644
--- a/luni/src/main/java/java/util/NavigableMap.java
+++ b/luni/src/main/java/java/util/NavigableMap.java
@@ -1,262 +1,396 @@
-/* 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.
+/*
+ * Written by Doug Lea and Josh Bloch with assistance from members of JCP
+ * JSR-166 Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
*/
package java.util;
+// BEGIN android-note
+// removed link to collections framework docs
+// changed {@link #subMap(Object)} to {@link #subMap} to satisfy DroidDoc
+// END android-note
+
/**
- * NavigableMap is a SortedMap with navigation methods answering the closest
- * matches for specified item.
+ * A {@link SortedMap} extended with navigation methods returning the
+ * closest matches for given search targets. Methods
+ * {@code lowerEntry}, {@code floorEntry}, {@code ceilingEntry},
+ * and {@code higherEntry} return {@code Map.Entry} objects
+ * associated with keys respectively less than, less than or equal,
+ * greater than or equal, and greater than a given key, returning
+ * {@code null} if there is no such key. Similarly, methods
+ * {@code lowerKey}, {@code floorKey}, {@code ceilingKey}, and
+ * {@code higherKey} return only the associated keys. All of these
+ * methods are designed for locating, not traversing entries.
+ *
+ * <p>A {@code NavigableMap} may be accessed and traversed in either
+ * ascending or descending key order. The {@code descendingMap}
+ * method returns a view of the map with the senses of all relational
+ * and directional methods inverted. The performance of ascending
+ * operations and views is likely to be faster than that of descending
+ * ones. Methods {@code subMap}, {@code headMap},
+ * and {@code tailMap} differ from the like-named {@code
+ * SortedMap} methods in accepting additional arguments describing
+ * whether lower and upper bounds are inclusive versus exclusive.
+ * Submaps of any {@code NavigableMap} must implement the {@code
+ * NavigableMap} interface.
*
- * @param <K>
- * the type of key
- * @param <V>
- * the type of value
+ * <p>This interface additionally defines methods {@code firstEntry},
+ * {@code pollFirstEntry}, {@code lastEntry}, and
+ * {@code pollLastEntry} that return and/or remove the least and
+ * greatest mappings, if any exist, else returning {@code null}.
+ *
+ * <p>Implementations of entry-returning methods are expected to
+ * return {@code Map.Entry} pairs representing snapshots of mappings
+ * at the time they were produced, and thus generally do <em>not</em>
+ * support the optional {@code Entry.setValue} method. Note however
+ * that it is possible to change mappings in the associated map using
+ * method {@code put}.
+ *
+ * <p>Methods
+ * {@link #subMap subMap(K, K)},
+ * {@link #headMap headMap(K)}, and
+ * {@link #tailMap tailMap(K)}
+ * are specified to return {@code SortedMap} to allow existing
+ * implementations of {@code SortedMap} to be compatibly retrofitted to
+ * implement {@code NavigableMap}, but extensions and implementations
+ * of this interface are encouraged to override these methods to return
+ * {@code NavigableMap}. Similarly,
+ * {@link #keySet()} can be overriden to return {@code NavigableSet}.
+ *
+ * @author Doug Lea
+ * @author Josh Bloch
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
* @since 1.6
*/
-public interface NavigableMap<K, V> extends SortedMap<K, V> {
+public interface NavigableMap<K,V> extends SortedMap<K,V> {
/**
- * Returns the entry with the smallest key, or null if the map is empty.
+ * Returns a key-value mapping associated with the greatest key
+ * strictly less than the given key, or {@code null} if there is
+ * no such key.
*
- * @return the entry with the smallest key, or null if the map is empty
+ * @param key the key
+ * @return an entry with the greatest key less than {@code key},
+ * or {@code null} if there is no such key
+ * @throws ClassCastException if the specified key cannot be compared
+ * with the keys currently in the map
+ * @throws NullPointerException if the specified key is null
+ * and this map does not permit null keys
*/
- Map.Entry<K, V> firstEntry();
+ Map.Entry<K,V> lowerEntry(K key);
/**
- * Returns the entry with the biggest key, or null if the map is empty.
+ * Returns the greatest key strictly less than the given key, or
+ * {@code null} if there is no such key.
*
- * @return the entry with the biggest key, or null if the map is empty
+ * @param key the key
+ * @return the greatest key less than {@code key},
+ * or {@code null} if there is no such key
+ * @throws ClassCastException if the specified key cannot be compared
+ * with the keys currently in the map
+ * @throws NullPointerException if the specified key is null
+ * and this map does not permit null keys
*/
- Map.Entry<K, V> lastEntry();
+ K lowerKey(K key);
/**
- * Deletes and returns the entry with the smallest key, or null if the map
- * is empty.
+ * Returns a key-value mapping associated with the greatest key
+ * less than or equal to the given key, or {@code null} if there
+ * is no such key.
*
- * @return the entry with the smallest key, or null if the map is empty
+ * @param key the key
+ * @return an entry with the greatest key less than or equal to
+ * {@code key}, or {@code null} if there is no such key
+ * @throws ClassCastException if the specified key cannot be compared
+ * with the keys currently in the map
+ * @throws NullPointerException if the specified key is null
+ * and this map does not permit null keys
*/
- Map.Entry<K, V> pollFirstEntry();
+ Map.Entry<K,V> floorEntry(K key);
/**
- * Deletes and returns the entry with the biggest key, or null if the map is
- * empty.
+ * Returns the greatest key less than or equal to the given key,
+ * or {@code null} if there is no such key.
*
- * @return the entry with the biggest key, or null if the map is empty
+ * @param key the key
+ * @return the greatest key less than or equal to {@code key},
+ * or {@code null} if there is no such key
+ * @throws ClassCastException if the specified key cannot be compared
+ * with the keys currently in the map
+ * @throws NullPointerException if the specified key is null
+ * and this map does not permit null keys
*/
- Map.Entry<K, V> pollLastEntry();
+ K floorKey(K key);
/**
- * Returns an entry related with the smallest key greater than or equal to
- * the specified key, or null if no such key.
+ * Returns a key-value mapping associated with the least key
+ * greater than or equal to the given key, or {@code null} if
+ * there is no such key.
*
- * @param key
- * the key
- * @return the entry, or null if no such key
- * @throws ClassCastException
- * if the key cannot be compared with the keys in the map
- * @throws NullPointerException
- * if the key is null and the map can not contain null key
+ * @param key the key
+ * @return an entry with the least key greater than or equal to
+ * {@code key}, or {@code null} if there is no such key
+ * @throws ClassCastException if the specified key cannot be compared
+ * with the keys currently in the map
+ * @throws NullPointerException if the specified key is null
+ * and this map does not permit null keys
*/
- Map.Entry<K, V> ceilingEntry(K key);
+ Map.Entry<K,V> ceilingEntry(K key);
/**
- * Returns the smallest key greater than or equal to the specified key, or
- * null if no such key.
+ * Returns the least key greater than or equal to the given key,
+ * or {@code null} if there is no such key.
*
- * @param key
- * the key
- * @return the smallest key greater than or equal to key, or null if no such
- * key
- * @throws ClassCastException
- * if the key cannot be compared with the keys in the map
- * @throws NullPointerException
- * if the key is null and the map can not contain null key
+ * @param key the key
+ * @return the least key greater than or equal to {@code key},
+ * or {@code null} if there is no such key
+ * @throws ClassCastException if the specified key cannot be compared
+ * with the keys currently in the map
+ * @throws NullPointerException if the specified key is null
+ * and this map does not permit null keys
*/
K ceilingKey(K key);
/**
- * Returns an entry related with the smallest key greater than the specified
- * key, or null if no such key.
+ * Returns a key-value mapping associated with the least key
+ * strictly greater than the given key, or {@code null} if there
+ * is no such key.
*
- * @param key
- * the key
- * @return the entry, or null if no such key
- * @throws ClassCastException
- * if the key cannot be compared with the keys in the map
- * @throws NullPointerException
- * if the key is null and the map can not contain null key
+ * @param key the key
+ * @return an entry with the least key greater than {@code key},
+ * or {@code null} if there is no such key
+ * @throws ClassCastException if the specified key cannot be compared
+ * with the keys currently in the map
+ * @throws NullPointerException if the specified key is null
+ * and this map does not permit null keys
*/
- Map.Entry<K, V> higherEntry(K key);
+ Map.Entry<K,V> higherEntry(K key);
/**
- * Returns the smallest key greater than the specified key, or null if no
- * such key.
+ * Returns the least key strictly greater than the given key, or
+ * {@code null} if there is no such key.
*
- * @param key
- * the key
- * @return the smallest key greater than key, or null if no such key
- * @throws ClassCastException
- * if the key cannot be compared with the keys in the map
- * @throws NullPointerException
- * if the key is null and the map can not contain null key
+ * @param key the key
+ * @return the least key greater than {@code key},
+ * or {@code null} if there is no such key
+ * @throws ClassCastException if the specified key cannot be compared
+ * with the keys currently in the map
+ * @throws NullPointerException if the specified key is null
+ * and this map does not permit null keys
*/
K higherKey(K key);
/**
- * Returns an entry related with the biggest key less than or equal to the
- * specified key, or null if no such key.
+ * Returns a key-value mapping associated with the least
+ * key in this map, or {@code null} if the map is empty.
*
- * @param key
- * the key
- * @return the entry, or null if no such key
- * @throws ClassCastException
- * if the key cannot be compared with the keys in the map
- * @throws NullPointerException
- * if the key is null and the map can not contain null key
+ * @return an entry with the least key,
+ * or {@code null} if this map is empty
*/
- Map.Entry<K, V> floorEntry(K key);
+ Map.Entry<K,V> firstEntry();
/**
- * Returns the biggest key less than or equal to the specified key, or null
- * if no such key.
+ * Returns a key-value mapping associated with the greatest
+ * key in this map, or {@code null} if the map is empty.
*
- * @param key
- * the key
- * @return the biggest key less than or equal to key, or null if no such key
- * @throws ClassCastException
- * if the key cannot be compared with the keys in the map
- * @throws NullPointerException
- * if the key is null and the map can not contain null key
+ * @return an entry with the greatest key,
+ * or {@code null} if this map is empty
*/
- K floorKey(K key);
+ Map.Entry<K,V> lastEntry();
/**
- * Returns an entry related with the biggest key less than the specified
- * key, or null if no such key.
+ * Removes and returns a key-value mapping associated with
+ * the least key in this map, or {@code null} if the map is empty.
*
- * @param key
- * the key
- * @return the entry, or null if no such key
- * @throws ClassCastException
- * if the key cannot be compared with the keys in the map
- * @throws NullPointerException
- * if the key is null and the map can not contain null key
+ * @return the removed first entry of this map,
+ * or {@code null} if this map is empty
*/
- Map.Entry<K, V> lowerEntry(K key);
+ Map.Entry<K,V> pollFirstEntry();
/**
- * Returns the biggest key less than the specified key, or null if no such
- * key.
+ * Removes and returns a key-value mapping associated with
+ * the greatest key in this map, or {@code null} if the map is empty.
*
- * @param key
- * the key
- * @return the biggest key less than key, or null if no such key
- * @throws ClassCastException
- * if the key cannot be compared with the keys in the map
- * @throws NullPointerException
- * if the key is null and the map can not contain null key
+ * @return the removed last entry of this map,
+ * or {@code null} if this map is empty
*/
- K lowerKey(K key);
+ Map.Entry<K,V> pollLastEntry();
/**
- * Returns a NavigableSet view of the keys in ascending order.
+ * Returns a reverse order view of the mappings contained in this map.
+ * The descending map is backed by this map, so changes to the map are
+ * reflected in the descending map, and vice-versa. If either map is
+ * modified while an iteration over a collection view of either map
+ * is in progress (except through the iterator's own {@code remove}
+ * operation), the results of the iteration are undefined.
+ *
+ * <p>The returned map has an ordering equivalent to
+ * <tt>{@link Collections#reverseOrder(Comparator) Collections.reverseOrder}(comparator())</tt>.
+ * The expression {@code m.descendingMap().descendingMap()} returns a
+ * view of {@code m} essentially equivalent to {@code m}.
*
- * @return the navigable set view
+ * @return a reverse order view of this map
*/
- NavigableSet<K> navigableKeySet();
+ NavigableMap<K,V> descendingMap();
/**
- * Returns a reverse order view of the map.
+ * Returns a {@link NavigableSet} view of the keys contained in this map.
+ * The set's iterator returns the keys in ascending order.
+ * The set is backed by the map, so changes to the map are reflected in
+ * the set, and vice-versa. If the map is modified while an iteration
+ * over the set is in progress (except through the iterator's own {@code
+ * remove} operation), the results of the iteration are undefined. The
+ * set supports element removal, which removes the corresponding mapping
+ * from the map, via the {@code Iterator.remove}, {@code Set.remove},
+ * {@code removeAll}, {@code retainAll}, and {@code clear} operations.
+ * It does not support the {@code add} or {@code addAll} operations.
*
- * @return the reverse order view of the map
+ * @return a navigable set view of the keys in this map
*/
- NavigableMap<K, V> descendingMap();
+ NavigableSet<K> navigableKeySet();
/**
- * Returns a NavigableSet view of the keys in descending order.
+ * Returns a reverse order {@link NavigableSet} view of the keys contained in this map.
+ * The set's iterator returns the keys in descending order.
+ * The set is backed by the map, so changes to the map are reflected in
+ * the set, and vice-versa. If the map is modified while an iteration
+ * over the set is in progress (except through the iterator's own {@code
+ * remove} operation), the results of the iteration are undefined. The
+ * set supports element removal, which removes the corresponding mapping
+ * from the map, via the {@code Iterator.remove}, {@code Set.remove},
+ * {@code removeAll}, {@code retainAll}, and {@code clear} operations.
+ * It does not support the {@code add} or {@code addAll} operations.
*
- * @return the navigable set view
+ * @return a reverse order navigable set view of the keys in this map
*/
NavigableSet<K> descendingKeySet();
/**
- * Returns a view of part of the map whose keys is from startKey to endKey.
+ * Returns a view of the portion of this map whose keys range from
+ * {@code fromKey} to {@code toKey}. If {@code fromKey} and
+ * {@code toKey} are equal, the returned map is empty unless
+ * {@code fromExclusive} and {@code toExclusive} are both true. The
+ * returned map is backed by this map, so changes in the returned map are
+ * reflected in this map, and vice-versa. The returned map supports all
+ * optional map operations that this map supports.
+ *
+ * <p>The returned map will throw an {@code IllegalArgumentException}
+ * on an attempt to insert a key outside of its range, or to construct a
+ * submap either of whose endpoints lie outside its range.
+ *
+ * @param fromKey low endpoint of the keys in the returned map
+ * @param fromInclusive {@code true} if the low endpoint
+ * is to be included in the returned view
+ * @param toKey high endpoint of the keys in the returned map
+ * @param toInclusive {@code true} if the high endpoint
+ * is to be included in the returned view
+ * @return a view of the portion of this map whose keys range from
+ * {@code fromKey} to {@code toKey}
+ * @throws ClassCastException if {@code fromKey} and {@code toKey}
+ * cannot be compared to one another using this map's comparator
+ * (or, if the map has no comparator, using natural ordering).
+ * Implementations may, but are not required to, throw this
+ * exception if {@code fromKey} or {@code toKey}
+ * cannot be compared to keys currently in the map.
+ * @throws NullPointerException if {@code fromKey} or {@code toKey}
+ * is null and this map does not permit null keys
+ * @throws IllegalArgumentException if {@code fromKey} is greater than
+ * {@code toKey}; or if this map itself has a restricted
+ * range, and {@code fromKey} or {@code toKey} lies
+ * outside the bounds of the range
+ */
+ NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
+ K toKey, boolean toInclusive);
+
+ /**
+ * Returns a view of the portion of this map whose keys are less than (or
+ * equal to, if {@code inclusive} is true) {@code toKey}. The returned
+ * map is backed by this map, so changes in the returned map are reflected
+ * in this map, and vice-versa. The returned map supports all optional
+ * map operations that this map supports.
+ *
+ * <p>The returned map will throw an {@code IllegalArgumentException}
+ * on an attempt to insert a key outside its range.
+ *
+ * @param toKey high endpoint of the keys in the returned map
+ * @param inclusive {@code true} if the high endpoint
+ * is to be included in the returned view
+ * @return a view of the portion of this map whose keys are less than
+ * (or equal to, if {@code inclusive} is true) {@code toKey}
+ * @throws ClassCastException if {@code toKey} is not compatible
+ * with this map's comparator (or, if the map has no comparator,
+ * if {@code toKey} does not implement {@link Comparable}).
+ * Implementations may, but are not required to, throw this
+ * exception if {@code toKey} cannot be compared to keys
+ * currently in the map.
+ * @throws NullPointerException if {@code toKey} is null
+ * and this map does not permit null keys
+ * @throws IllegalArgumentException if this map itself has a
+ * restricted range, and {@code toKey} lies outside the
+ * bounds of the range
+ */
+ NavigableMap<K,V> headMap(K toKey, boolean inclusive);
+
+ /**
+ * Returns a view of the portion of this map whose keys are greater than (or
+ * equal to, if {@code inclusive} is true) {@code fromKey}. The returned
+ * map is backed by this map, so changes in the returned map are reflected
+ * in this map, and vice-versa. The returned map supports all optional
+ * map operations that this map supports.
+ *
+ * <p>The returned map will throw an {@code IllegalArgumentException}
+ * on an attempt to insert a key outside its range.
+ *
+ * @param fromKey low endpoint of the keys in the returned map
+ * @param inclusive {@code true} if the low endpoint
+ * is to be included in the returned view
+ * @return a view of the portion of this map whose keys are greater than
+ * (or equal to, if {@code inclusive} is true) {@code fromKey}
+ * @throws ClassCastException if {@code fromKey} is not compatible
+ * with this map's comparator (or, if the map has no comparator,
+ * if {@code fromKey} does not implement {@link Comparable}).
+ * Implementations may, but are not required to, throw this
+ * exception if {@code fromKey} cannot be compared to keys
+ * currently in the map.
+ * @throws NullPointerException if {@code fromKey} is null
+ * and this map does not permit null keys
+ * @throws IllegalArgumentException if this map itself has a
+ * restricted range, and {@code fromKey} lies outside the
+ * bounds of the range
+ */
+ NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
+
+ /**
+ * {@inheritDoc}
*
- * @param startKey
- * the start key
- * @param startInclusive
- * true if the start key is in the returned map
- * @param endKey
- * the end key
- * @param endInclusive
- * true if the end key is in the returned map
- * @return the sub-map view
+ * <p>Equivalent to {@code subMap(fromKey, true, toKey, false)}.
*
- * @exception ClassCastException
- * when the class of the start or end key is inappropriate
- * for this SubMap
- * @exception NullPointerException
- * when the start or end key is null and this SortedMap does
- * not support null keys
- * @exception IllegalArgumentException
- * when the start key is greater than the end key
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
*/
- NavigableMap<K, V> subMap(K startKey, boolean startInclusive, K endKey,
- boolean endInclusive);
+ SortedMap<K,V> subMap(K fromKey, K toKey);
/**
- * Returns a view of the head of the map whose keys are smaller than (or
- * equal to, depends on inclusive argument) endKey.
+ * {@inheritDoc}
*
- * @param endKey
- * the end key
- * @param inclusive
- * true if the end key is in the returned map
- * @return the head-map view
+ * <p>Equivalent to {@code headMap(toKey, false)}.
*
- * @exception ClassCastException
- * when the class of the end key is inappropriate for this
- * SubMap
- * @exception NullPointerException
- * when the end key is null and this SortedMap does not
- * support null keys
- * @exception IllegalArgumentException
- * when the map is range-limited and end key is out of the
- * range of the map
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
*/
- NavigableMap<K, V> headMap(K endKey, boolean inclusive);
+ SortedMap<K,V> headMap(K toKey);
/**
- * Returns a view of the tail of the map whose keys are bigger than (or
- * equal to, depends on inclusive argument) startKey.
+ * {@inheritDoc}
*
- * @param startKey
- * the start key
- * @param inclusive
- * true if the start key is in the returned map
- * @return the tail-map view
+ * <p>Equivalent to {@code tailMap(fromKey, true)}.
*
- * @exception ClassCastException
- * when the class of the start key is inappropriate for this
- * SubMap
- * @exception NullPointerException
- * when the start key is null and this SortedMap does not
- * support null keys
- * @exception IllegalArgumentException
- * when the map is range-limited and start key is out of the
- * range of the map
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
*/
- NavigableMap<K, V> tailMap(K startKey, boolean inclusive);
-} \ No newline at end of file
+ SortedMap<K,V> tailMap(K fromKey);
+}
diff --git a/luni/src/main/java/java/util/NavigableSet.java b/luni/src/main/java/java/util/NavigableSet.java
index ae94b77..cff0800 100644
--- a/luni/src/main/java/java/util/NavigableSet.java
+++ b/luni/src/main/java/java/util/NavigableSet.java
@@ -1,192 +1,291 @@
/*
- * 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.
+ * Written by Doug Lea and Josh Bloch with assistance from members of JCP
+ * JSR-166 Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
*/
package java.util;
+// BEGIN android-note
+// removed link to collections framework docs
+// changed {@link #subSet(Object)} to {@link #subSet} to satisfy DroidDoc
+// END android-note
+
/**
- * NavigableSet is a SortedSet with navigation methods answering the closest
- * matches for specified item.
+ * A {@link SortedSet} extended with navigation methods reporting
+ * closest matches for given search targets. Methods {@code lower},
+ * {@code floor}, {@code ceiling}, and {@code higher} return elements
+ * respectively less than, less than or equal, greater than or equal,
+ * and greater than a given element, returning {@code null} if there
+ * is no such element. A {@code NavigableSet} may be accessed and
+ * traversed in either ascending or descending order. The {@code
+ * descendingSet} method returns a view of the set with the senses of
+ * all relational and directional methods inverted. The performance of
+ * ascending operations and views is likely to be faster than that of
+ * descending ones. This interface additionally defines methods
+ * {@code pollFirst} and {@code pollLast} that return and remove the
+ * lowest and highest element, if one exists, else returning {@code
+ * null}. Methods {@code subSet}, {@code headSet},
+ * and {@code tailSet} differ from the like-named {@code
+ * SortedSet} methods in accepting additional arguments describing
+ * whether lower and upper bounds are inclusive versus exclusive.
+ * Subsets of any {@code NavigableSet} must implement the {@code
+ * NavigableSet} interface.
*
- * @param <E>
- * the type of element
+ * <p> The return values of navigation methods may be ambiguous in
+ * implementations that permit {@code null} elements. However, even
+ * in this case the result can be disambiguated by checking
+ * {@code contains(null)}. To avoid such issues, implementations of
+ * this interface are encouraged to <em>not</em> permit insertion of
+ * {@code null} elements. (Note that sorted sets of {@link
+ * Comparable} elements intrinsically do not permit {@code null}.)
+ *
+ * <p>Methods
+ * {@link #subSet subSet(E, E)},
+ * {@link #headSet headSet(E)}, and
+ * {@link #tailSet tailSet(E)}
+ * are specified to return {@code SortedSet} to allow existing
+ * implementations of {@code SortedSet} to be compatibly retrofitted to
+ * implement {@code NavigableSet}, but extensions and implementations
+ * of this interface are encouraged to override these methods to return
+ * {@code NavigableSet}.
+ *
+ * @author Doug Lea
+ * @author Josh Bloch
+ * @param <E> the type of elements maintained by this set
* @since 1.6
*/
public interface NavigableSet<E> extends SortedSet<E> {
+ /**
+ * Returns the greatest element in this set strictly less than the
+ * given element, or {@code null} if there is no such element.
+ *
+ * @param e the value to match
+ * @return the greatest element less than {@code e},
+ * or {@code null} if there is no such element
+ * @throws ClassCastException if the specified element cannot be
+ * compared with the elements currently in the set
+ * @throws NullPointerException if the specified element is null
+ * and this set does not permit null elements
+ */
+ E lower(E e);
/**
- * Deletes and returns the smallest element, or null if the set is empty.
+ * Returns the greatest element in this set less than or equal to
+ * the given element, or {@code null} if there is no such element.
*
- * @return the smallest element, or null if the set is empty
+ * @param e the value to match
+ * @return the greatest element less than or equal to {@code e},
+ * or {@code null} if there is no such element
+ * @throws ClassCastException if the specified element cannot be
+ * compared with the elements currently in the set
+ * @throws NullPointerException if the specified element is null
+ * and this set does not permit null elements
*/
- E pollFirst();
+ E floor(E e);
/**
- * Deletes and returns the biggest element, or null if the set is empty.
+ * Returns the least element in this set greater than or equal to
+ * the given element, or {@code null} if there is no such element.
*
- * @return the biggest element, or null if the set is empty
+ * @param e the value to match
+ * @return the least element greater than or equal to {@code e},
+ * or {@code null} if there is no such element
+ * @throws ClassCastException if the specified element cannot be
+ * compared with the elements currently in the set
+ * @throws NullPointerException if the specified element is null
+ * and this set does not permit null elements
*/
- E pollLast();
+ E ceiling(E e);
/**
- * Returns the smallest element bigger than the specified one, or null if no
- * such element.
- *
- * @param e
- * the specified element
- * @return the smallest element bigger than the specified one, or null if no
- * such element
- * @throws ClassCastException
- * if the element cannot be compared with the ones in the set
- * @throws NullPointerException
- * if the element is null and the set can not contain null
+ * Returns the least element in this set strictly greater than the
+ * given element, or {@code null} if there is no such element.
+ *
+ * @param e the value to match
+ * @return the least element greater than {@code e},
+ * or {@code null} if there is no such element
+ * @throws ClassCastException if the specified element cannot be
+ * compared with the elements currently in the set
+ * @throws NullPointerException if the specified element is null
+ * and this set does not permit null elements
*/
E higher(E e);
/**
- * Returns the smallest element bigger than or equal to the specified one,
- * or null if no such element.
- *
- * @param e
- * the specified element
- * @return the smallest element bigger than or equal to the specified one,
- * or null if no such element
- * @throws ClassCastException
- * if the element cannot be compared with the ones in the set
- * @throws NullPointerException
- * if the element is null and the set can not contain null
+ * Retrieves and removes the first (lowest) element,
+ * or returns {@code null} if this set is empty.
+ *
+ * @return the first element, or {@code null} if this set is empty
*/
- E ceiling(E e);
+ E pollFirst();
/**
- * Returns the biggest element less than the specified one, or null if no
- * such element.
- *
- * @param e
- * the specified element
- * @return the biggest element less than the specified one, or null if no
- * such element
- * @throws ClassCastException
- * if the element cannot be compared with the ones in the set
- * @throws NullPointerException
- * if the element is null and the set can not contain null
+ * Retrieves and removes the last (highest) element,
+ * or returns {@code null} if this set is empty.
+ *
+ * @return the last element, or {@code null} if this set is empty
*/
- E lower(E e);
+ E pollLast();
/**
- * Returns the biggest element less than or equal to the specified one, or
- * null if no such element.
- *
- * @param e
- * the specified element
- * @return the biggest element less than or equal to the specified one, or
- * null if no such element
- * @throws ClassCastException
- * if the element cannot be compared with the ones in the set
- * @throws NullPointerException
- * if the element is null and the set can not contain null
+ * Returns an iterator over the elements in this set, in ascending order.
+ *
+ * @return an iterator over the elements in this set, in ascending order
*/
- E floor(E e);
+ Iterator<E> iterator();
/**
- * Returns a descending iterator of this set.
+ * Returns a reverse order view of the elements contained in this set.
+ * The descending set is backed by this set, so changes to the set are
+ * reflected in the descending set, and vice-versa. If either set is
+ * modified while an iteration over either set is in progress (except
+ * through the iterator's own {@code remove} operation), the results of
+ * the iteration are undefined.
+ *
+ * <p>The returned set has an ordering equivalent to
+ * <tt>{@link Collections#reverseOrder(Comparator) Collections.reverseOrder}(comparator())</tt>.
+ * The expression {@code s.descendingSet().descendingSet()} returns a
+ * view of {@code s} essentially equivalent to {@code s}.
*
- * @return the descending iterator
+ * @return a reverse order view of this set
+ */
+ NavigableSet<E> descendingSet();
+
+ /**
+ * Returns an iterator over the elements in this set, in descending order.
+ * Equivalent in effect to {@code descendingSet().iterator()}.
+ *
+ * @return an iterator over the elements in this set, in descending order
*/
Iterator<E> descendingIterator();
/**
- * Returns a reverse order view of this set.
+ * Returns a view of the portion of this set whose elements range from
+ * {@code fromElement} to {@code toElement}. If {@code fromElement} and
+ * {@code toElement} are equal, the returned set is empty unless {@code
+ * fromExclusive} and {@code toExclusive} are both true. The returned set
+ * is backed by this set, so changes in the returned set are reflected in
+ * this set, and vice-versa. The returned set supports all optional set
+ * operations that this set supports.
+ *
+ * <p>The returned set will throw an {@code IllegalArgumentException}
+ * on an attempt to insert an element outside its range.
*
- * @return the reverse order view
+ * @param fromElement low endpoint of the returned set
+ * @param fromInclusive {@code true} if the low endpoint
+ * is to be included in the returned view
+ * @param toElement high endpoint of the returned set
+ * @param toInclusive {@code true} if the high endpoint
+ * is to be included in the returned view
+ * @return a view of the portion of this set whose elements range from
+ * {@code fromElement}, inclusive, to {@code toElement}, exclusive
+ * @throws ClassCastException if {@code fromElement} and
+ * {@code toElement} cannot be compared to one another using this
+ * set's comparator (or, if the set has no comparator, using
+ * natural ordering). Implementations may, but are not required
+ * to, throw this exception if {@code fromElement} or
+ * {@code toElement} cannot be compared to elements currently in
+ * the set.
+ * @throws NullPointerException if {@code fromElement} or
+ * {@code toElement} is null and this set does
+ * not permit null elements
+ * @throws IllegalArgumentException if {@code fromElement} is
+ * greater than {@code toElement}; or if this set itself
+ * has a restricted range, and {@code fromElement} or
+ * {@code toElement} lies outside the bounds of the range.
*/
- NavigableSet<E> descendingSet();
+ NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
+ E toElement, boolean toInclusive);
+
+ /**
+ * Returns a view of the portion of this set whose elements are less than
+ * (or equal to, if {@code inclusive} is true) {@code toElement}. The
+ * returned set is backed by this set, so changes in the returned set are
+ * reflected in this set, and vice-versa. The returned set supports all
+ * optional set operations that this set supports.
+ *
+ * <p>The returned set will throw an {@code IllegalArgumentException}
+ * on an attempt to insert an element outside its range.
+ *
+ * @param toElement high endpoint of the returned set
+ * @param inclusive {@code true} if the high endpoint
+ * is to be included in the returned view
+ * @return a view of the portion of this set whose elements are less than
+ * (or equal to, if {@code inclusive} is true) {@code toElement}
+ * @throws ClassCastException if {@code toElement} is not compatible
+ * with this set's comparator (or, if the set has no comparator,
+ * if {@code toElement} does not implement {@link Comparable}).
+ * Implementations may, but are not required to, throw this
+ * exception if {@code toElement} cannot be compared to elements
+ * currently in the set.
+ * @throws NullPointerException if {@code toElement} is null and
+ * this set does not permit null elements
+ * @throws IllegalArgumentException if this set itself has a
+ * restricted range, and {@code toElement} lies outside the
+ * bounds of the range
+ */
+ NavigableSet<E> headSet(E toElement, boolean inclusive);
/**
- * Returns a NavigableSet of the specified portion of this set which
- * contains elements greater (or equal to, depends on startInclusive) the
- * start element but less than (or equal to, depends on endInclusive) the
- * end element. The returned NavigableSet is backed by this set so changes
- * to one are reflected by the other.
- *
- * @param start
- * the start element
- * @param startInclusive
- * true if the start element is in the returned set
- * @param end
- * the end element
- * @param endInclusive
- * true if the end element is in the returned set
- * @return the subset
- *
- * @throws ClassCastException
- * when the start or end object cannot be compared with the
- * elements in this set
- * @throws NullPointerException
- * when the start or end object is null and the set cannot
- * contain null
- * @throws IllegalArgumentException
- * when the start is bigger than end; or start or end is out of
- * range and the set has a range
+ * Returns a view of the portion of this set whose elements are greater
+ * than (or equal to, if {@code inclusive} is true) {@code fromElement}.
+ * The returned set is backed by this set, so changes in the returned set
+ * are reflected in this set, and vice-versa. The returned set supports
+ * all optional set operations that this set supports.
+ *
+ * <p>The returned set will throw an {@code IllegalArgumentException}
+ * on an attempt to insert an element outside its range.
+ *
+ * @param fromElement low endpoint of the returned set
+ * @param inclusive {@code true} if the low endpoint
+ * is to be included in the returned view
+ * @return a view of the portion of this set whose elements are greater
+ * than or equal to {@code fromElement}
+ * @throws ClassCastException if {@code fromElement} is not compatible
+ * with this set's comparator (or, if the set has no comparator,
+ * if {@code fromElement} does not implement {@link Comparable}).
+ * Implementations may, but are not required to, throw this
+ * exception if {@code fromElement} cannot be compared to elements
+ * currently in the set.
+ * @throws NullPointerException if {@code fromElement} is null
+ * and this set does not permit null elements
+ * @throws IllegalArgumentException if this set itself has a
+ * restricted range, and {@code fromElement} lies outside the
+ * bounds of the range
*/
- NavigableSet<E> subSet(E start, boolean startInclusive, E end,
- boolean endInclusive);
+ NavigableSet<E> tailSet(E fromElement, boolean inclusive);
/**
- * Returns a NavigableSet of the specified portion of this set which
- * contains elements less than (or equal to, depends on endInclusive) the
- * end element. The returned NavigableSet is backed by this set so changes
- * to one are reflected by the other.
- *
- * @param end
- * the end element
- * @param endInclusive
- * true if the end element is in the returned set
- * @return the subset
- *
- * @throws ClassCastException
- * when the end object cannot be compared with the elements in
- * this set
- * @throws NullPointerException
- * when the end object is null and the set cannot contain handle
- * null
- * @throws IllegalArgumentException
- * when end is out of range and the set has a range
+ * {@inheritDoc}
+ *
+ * <p>Equivalent to {@code subSet(fromElement, true, toElement, false)}.
+ *
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
*/
- NavigableSet<E> headSet(E end, boolean endInclusive);
+ SortedSet<E> subSet(E fromElement, E toElement);
/**
- * Returns a NavigableSet of the specified portion of this set which
- * contains elements greater (or equal to, depends on startInclusive) the
- * start element. The returned NavigableSet is backed by this set so changes
- * to one are reflected by the other.
- *
- * @param start
- * the start element
- * @param startInclusive
- * true if the start element is in the returned set
- * @return the subset
- *
- * @throws ClassCastException
- * when the start object cannot be compared with the elements in
- * this set
- * @throws NullPointerException
- * when the start object is null and the set cannot contain null
- * @throws IllegalArgumentException
- * when start is out of range and the set has a range
+ * {@inheritDoc}
+ *
+ * <p>Equivalent to {@code headSet(toElement, false)}.
+ *
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+na */
+ SortedSet<E> headSet(E toElement);
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Equivalent to {@code tailSet(fromElement, true)}.
+ *
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
*/
- NavigableSet<E> tailSet(E start, boolean startInclusive);
-} \ No newline at end of file
+ SortedSet<E> tailSet(E fromElement);
+}
diff --git a/luni/src/main/java/java/util/Queue.java b/luni/src/main/java/java/util/Queue.java
index 51899e3..5aef944 100644
--- a/luni/src/main/java/java/util/Queue.java
+++ b/luni/src/main/java/java/util/Queue.java
@@ -1,91 +1,188 @@
/*
- * 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.
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
*/
+
package java.util;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
/**
- * This kind of collection provides advanced operations compared to basic
- * collections, such as insertion, extraction, and inspection.
- * <p>
- * Generally, a queue orders its elements by means of first-in-first-out.
- * However, a priority queue orders its elements according to a comparator
- * specified or the elements' natural order. Furthermore, a stack orders its
- * elements last-in-first out.
- * <p>
- * A typical queue does not allow {@code null} to be inserted as its element,
- * while some implementations such as {@code LinkedList} allow it. But {@code
- * null} should not be inserted even in these implementations, since the method
- * {@code poll} returns {@code null} to indicate that there is no element left
- * in the queue.
+ * A collection designed for holding elements prior to processing.
+ * Besides basic {@link java.util.Collection Collection} operations,
+ * queues provide additional insertion, extraction, and inspection
+ * operations. Each of these methods exists in two forms: one throws
+ * an exception if the operation fails, the other returns a special
+ * value (either <tt>null</tt> or <tt>false</tt>, depending on the
+ * operation). The latter form of the insert operation is designed
+ * specifically for use with capacity-restricted <tt>Queue</tt>
+ * implementations; in most implementations, insert operations cannot
+ * fail.
+ *
* <p>
- * {@code Queue} does not provide blocking queue methods, which would block
- * until the operation of the method is allowed. See the
- * {@link java.util.concurrent.BlockingQueue} interface for information about
- * blocking queue methods.
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <tr>
+ * <td></td>
+ * <td ALIGN=CENTER><em>Throws exception</em></td>
+ * <td ALIGN=CENTER><em>Returns special value</em></td>
+ * </tr>
+ * <tr>
+ * <td><b>Insert</b></td>
+ * <td>{@link #add add(e)}</td>
+ * <td>{@link #offer offer(e)}</td>
+ * </tr>
+ * <tr>
+ * <td><b>Remove</b></td>
+ * <td>{@link #remove remove()}</td>
+ * <td>{@link #poll poll()}</td>
+ * </tr>
+ * <tr>
+ * <td><b>Examine</b></td>
+ * <td>{@link #element element()}</td>
+ * <td>{@link #peek peek()}</td>
+ * </tr>
+ * </table>
+ *
+ * <p>Queues typically, but do not necessarily, order elements in a
+ * FIFO (first-in-first-out) manner. Among the exceptions are
+ * priority queues, which order elements according to a supplied
+ * comparator, or the elements' natural ordering, and LIFO queues (or
+ * stacks) which order the elements LIFO (last-in-first-out).
+ * Whatever the ordering used, the <em>head</em> of the queue is that
+ * element which would be removed by a call to {@link #remove() } or
+ * {@link #poll()}. In a FIFO queue, all new elements are inserted at
+ * the <em> tail</em> of the queue. Other kinds of queues may use
+ * different placement rules. Every <tt>Queue</tt> implementation
+ * must specify its ordering properties.
+ *
+ * <p>The {@link #offer offer} method inserts an element if possible,
+ * otherwise returning <tt>false</tt>. This differs from the {@link
+ * java.util.Collection#add Collection.add} method, which can fail to
+ * add an element only by throwing an unchecked exception. The
+ * <tt>offer</tt> method is designed for use when failure is a normal,
+ * rather than exceptional occurrence, for example, in fixed-capacity
+ * (or &quot;bounded&quot;) queues.
+ *
+ * <p>The {@link #remove()} and {@link #poll()} methods remove and
+ * return the head of the queue.
+ * Exactly which element is removed from the queue is a
+ * function of the queue's ordering policy, which differs from
+ * implementation to implementation. The <tt>remove()</tt> and
+ * <tt>poll()</tt> methods differ only in their behavior when the
+ * queue is empty: the <tt>remove()</tt> method throws an exception,
+ * while the <tt>poll()</tt> method returns <tt>null</tt>.
+ *
+ * <p>The {@link #element()} and {@link #peek()} methods return, but do
+ * not remove, the head of the queue.
+ *
+ * <p>The <tt>Queue</tt> interface does not define the <i>blocking queue
+ * methods</i>, which are common in concurrent programming. These methods,
+ * which wait for elements to appear or for space to become available, are
+ * defined in the {@link java.util.concurrent.BlockingQueue} interface, which
+ * extends this interface.
+ *
+ * <p><tt>Queue</tt> implementations generally do not allow insertion
+ * of <tt>null</tt> elements, although some implementations, such as
+ * {@link LinkedList}, do not prohibit insertion of <tt>null</tt>.
+ * Even in the implementations that permit it, <tt>null</tt> should
+ * not be inserted into a <tt>Queue</tt>, as <tt>null</tt> is also
+ * used as a special return value by the <tt>poll</tt> method to
+ * indicate that the queue contains no elements.
+ *
+ * <p><tt>Queue</tt> implementations generally do not define
+ * element-based versions of methods <tt>equals</tt> and
+ * <tt>hashCode</tt> but instead inherit the identity based versions
+ * from class <tt>Object</tt>, because element-based equality is not
+ * always well-defined for queues with the same elements but different
+ * ordering properties.
+ *
+ * @see java.util.Collection
+ * @see LinkedList
+ * @see PriorityQueue
+ * @see java.util.concurrent.LinkedBlockingQueue
+ * @see java.util.concurrent.BlockingQueue
+ * @see java.util.concurrent.ArrayBlockingQueue
+ * @see java.util.concurrent.LinkedBlockingQueue
+ * @see java.util.concurrent.PriorityBlockingQueue
+ * @since 1.5
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
*/
public interface Queue<E> extends Collection<E> {
-
/**
- * Inserts the specified element into the queue provided that the condition
- * allows such an operation. The method is generally preferable to
- * {@link Collection#add}, since the latter might throw an exception if the
- * operation fails.
+ * Inserts the specified element into this queue if it is possible to do so
+ * immediately without violating capacity restrictions, returning
+ * <tt>true</tt> upon success and throwing an <tt>IllegalStateException</tt>
+ * if no space is currently available.
*
- * @param o
- * the specified element to insert into the queue.
- * @return {@code true} if the operation succeeds and {@code false} if it
- * fails.
+ * @param e the element to add
+ * @return <tt>true</tt> (as specified by {@link Collection#add})
+ * @throws IllegalStateException if the element cannot be added at this
+ * time due to capacity restrictions
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null and
+ * this queue does not permit null elements
+ * @throws IllegalArgumentException if some property of this element
+ * prevents it from being added to this queue
*/
- public boolean offer(E o);
+ boolean add(E e);
/**
- * Gets and removes the element at the head of the queue, or returns {@code
- * null} if there is no element in the queue.
+ * Inserts the specified element into this queue if it is possible to do
+ * so immediately without violating capacity restrictions.
+ * When using a capacity-restricted queue, this method is generally
+ * preferable to {@link #add}, which can fail to insert an element only
+ * by throwing an exception.
*
- * @return the element at the head of the queue or {@code null} if there is
- * no element in the queue.
+ * @param e the element to add
+ * @return <tt>true</tt> if the element was added to this queue, else
+ * <tt>false</tt>
+ * @throws ClassCastException if the class of the specified element
+ * prevents it from being added to this queue
+ * @throws NullPointerException if the specified element is null and
+ * this queue does not permit null elements
+ * @throws IllegalArgumentException if some property of this element
+ * prevents it from being added to this queue
*/
- public E poll();
+ boolean offer(E e);
/**
- * Gets and removes the element at the head of the queue. Throws a
- * NoSuchElementException if there is no element in the queue.
+ * Retrieves and removes the head of this queue. This method differs
+ * from {@link #poll poll} only in that it throws an exception if this
+ * queue is empty.
*
- * @return the element at the head of the queue.
- * @throws NoSuchElementException
- * if there is no element in the queue.
+ * @return the head of this queue
+ * @throws NoSuchElementException if this queue is empty
*/
- public E remove();
+ E remove();
/**
- * Gets but does not remove the element at the head of the queue.
+ * Retrieves and removes the head of this queue,
+ * or returns <tt>null</tt> if this queue is empty.
*
- * @return the element at the head of the queue or {@code null} if there is
- * no element in the queue.
+ * @return the head of this queue, or <tt>null</tt> if this queue is empty
*/
- public E peek();
+ E poll();
/**
- * Gets but does not remove the element at the head of the queue. Throws a
- * {@code NoSuchElementException} if there is no element in the queue.
+ * Retrieves, but does not remove, the head of this queue. This method
+ * differs from {@link #peek peek} only in that it throws an exception
+ * if this queue is empty.
*
- * @return the element at the head of the queue.
- * @throws NoSuchElementException
- * if there is no element in the queue.
+ * @return the head of this queue
+ * @throws NoSuchElementException if this queue is empty
*/
- public E element();
+ E element();
+ /**
+ * Retrieves, but does not remove, the head of this queue,
+ * or returns <tt>null</tt> if this queue is empty.
+ *
+ * @return the head of this queue, or <tt>null</tt> if this queue is empty
+ */
+ E peek();
}
diff --git a/luni/src/main/java/java/util/TreeMap.java b/luni/src/main/java/java/util/TreeMap.java
index ad0f182..59f240c 100644
--- a/luni/src/main/java/java/util/TreeMap.java
+++ b/luni/src/main/java/java/util/TreeMap.java
@@ -251,7 +251,7 @@ public class TreeMap<K, V> extends AbstractMap<K, V>
Node<K, V> find(K key, Relation relation) {
if (root == null) {
if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) {
- throw new ClassCastException(key.getClass().getName()); // NullPointerException ok
+ throw new ClassCastException(key.getClass().getName() + " is not Comparable"); // NullPointerException ok
}
if (relation == Relation.CREATE) {
root = new Node<K, V>(null, key);
diff --git a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
index 153d449..85fd53e 100644
--- a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
+++ b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
@@ -14,6 +14,7 @@ import java.util.Iterator;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
+import libcore.base.EmptyArray;
// BEGIN android-note
// removed link to collections framework docs
@@ -1011,7 +1012,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
* @return a zero-length array
*/
public Object[] toArray() {
- return new Object[0];
+ return EmptyArray.OBJECT;
}
/**
diff --git a/luni/src/main/java/java/util/jar/Attributes.java b/luni/src/main/java/java/util/jar/Attributes.java
index a0f6bf4..9041c26 100644
--- a/luni/src/main/java/java/util/jar/Attributes.java
+++ b/luni/src/main/java/java/util/jar/Attributes.java
@@ -387,7 +387,7 @@ public class Attributes implements Cloneable, Map<Object, Object> {
*/
public void putAll(Map<?, ?> attrib) {
if (attrib == null || !(attrib instanceof Attributes)) {
- throw new ClassCastException();
+ throw new ClassCastException(attrib.getClass().getName() + " not an Attributes");
}
this.map.putAll(attrib);
}
diff --git a/luni/src/main/java/java/util/jar/JarFile.java b/luni/src/main/java/java/util/jar/JarFile.java
index 0f42d78..ed39f67 100644
--- a/luni/src/main/java/java/util/jar/JarFile.java
+++ b/luni/src/main/java/java/util/jar/JarFile.java
@@ -28,7 +28,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import libcore.base.Streams;
import org.apache.harmony.archive.util.Util;
-import org.apache.harmony.luni.util.InputStreamHelper;
/**
* {@code JarFile} is used to read jar entries and their associated data from
@@ -291,8 +290,11 @@ public class JarFile extends ZipFile {
try {
InputStream is = super.getInputStream(manifestEntry);
if (verifier != null) {
- verifier.addMetaEntry(manifestEntry.getName(),
- InputStreamHelper.readFullyAndClose(is));
+ try {
+ verifier.addMetaEntry(manifestEntry.getName(), Streams.readFully(is));
+ } finally {
+ is.close();
+ }
is = super.getInputStream(manifestEntry);
}
try {
@@ -329,8 +331,7 @@ public class JarFile extends ZipFile {
for (ZipEntry entry : metaEntries) {
String entryName = entry.getName();
// Is this the entry for META-INF/MANIFEST.MF ?
- if (manifestEntry == null
- && Util.asciiEqualsIgnoreCase(MANIFEST_NAME, entryName)) {
+ if (manifestEntry == null && Util.asciiEqualsIgnoreCase(MANIFEST_NAME, entryName)) {
manifestEntry = entry;
// If there is no verifier then we don't need to look any further.
if (verifier == null) {
@@ -344,8 +345,11 @@ public class JarFile extends ZipFile {
|| Util.asciiEndsWithIgnoreCase(entryName, ".RSA"))) {
signed = true;
InputStream is = super.getInputStream(entry);
- byte[] buf = InputStreamHelper.readFullyAndClose(is);
- verifier.addMetaEntry(entryName, buf);
+ try {
+ verifier.addMetaEntry(entryName, Streams.readFully(is));
+ } finally {
+ is.close();
+ }
}
}
}
diff --git a/luni/src/main/java/java/util/jar/Manifest.java b/luni/src/main/java/java/util/jar/Manifest.java
index e7476cf..6216261 100644
--- a/luni/src/main/java/java/util/jar/Manifest.java
+++ b/luni/src/main/java/java/util/jar/Manifest.java
@@ -17,10 +17,12 @@
package java.util.jar;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;
@@ -30,7 +32,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-import org.apache.harmony.luni.util.InputStreamHelper;
+import libcore.base.Streams;
/**
* The {@code Manifest} class is used to obtain attribute information for a
@@ -45,6 +47,19 @@ public class Manifest implements Cloneable {
private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name("Name");
+ private static final Field BAIS_BUF = getByteArrayInputStreamField("buf");
+ private static final Field BAIS_POS = getByteArrayInputStreamField("pos");
+
+ private static Field getByteArrayInputStreamField(String name) {
+ try {
+ Field f = ByteArrayInputStream.class.getDeclaredField(name);
+ f.setAccessible(true);
+ return f;
+ } catch (Exception ex) {
+ throw new AssertionError(ex);
+ }
+ }
+
private Attributes mainAttributes = new Attributes();
private HashMap<String, Attributes> entries = new HashMap<String, Attributes>();
@@ -185,11 +200,10 @@ public class Manifest implements Cloneable {
*/
public void read(InputStream is) throws IOException {
byte[] buf;
- // Try to read get a reference to the bytes directly
- try {
- buf = InputStreamHelper.expose(is);
- } catch (UnsupportedOperationException uoe) {
- buf = readFully(is);
+ if (is instanceof ByteArrayInputStream) {
+ buf = exposeByteArrayInputStreamBytes((ByteArrayInputStream) is);
+ } else {
+ buf = Streams.readFully(is);
}
if (buf.length == 0) {
@@ -211,53 +225,31 @@ public class Manifest implements Cloneable {
im.initEntries(entries, chunks);
}
- /*
- * 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.
+ /**
+ * Returns a byte[] containing all the bytes from a ByteArrayInputStream.
+ * Where possible, this returns the actual array rather than a copy.
*/
- private byte[] readFully(InputStream is) throws IOException {
- // Initial read
- byte[] buffer = new byte[4096];
- int count = is.read(buffer);
- int nextByte = is.read();
-
- // Did we get it all in one read?
- if (nextByte == -1) {
- return Arrays.copyOf(buffer, count);
- }
-
- // Does it look like a manifest?
- if (!containsLine(buffer, count)) {
- throw new IOException("Manifest is too long");
- }
-
- // Requires additional reads
- ByteArrayOutputStream baos = new ByteArrayOutputStream(count * 2);
- baos.write(buffer, 0, count);
- baos.write(nextByte);
- while (true) {
- count = is.read(buffer);
- if (count == -1) {
- return baos.toByteArray();
+ private static byte[] exposeByteArrayInputStreamBytes(ByteArrayInputStream bais) {
+ byte[] buffer;
+ synchronized (bais) {
+ byte[] buf;
+ int pos;
+ try {
+ buf = (byte[]) BAIS_BUF.get(bais);
+ pos = BAIS_POS.getInt(bais);
+ } catch (IllegalAccessException iae) {
+ throw new AssertionError(iae);
}
- baos.write(buffer, 0, count);
- }
- }
-
- /*
- * 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;
+ int available = bais.available();
+ if (pos == 0 && buf.length == available) {
+ buffer = buf;
+ } else {
+ buffer = new byte[available];
+ System.arraycopy(buf, pos, buffer, 0, available);
}
+ bais.skip(available);
}
- return false;
+ return buffer;
}
/**
diff --git a/luni/src/main/java/java/util/prefs/AbstractPreferences.java b/luni/src/main/java/java/util/prefs/AbstractPreferences.java
index 34073a1..be768c5 100644
--- a/luni/src/main/java/java/util/prefs/AbstractPreferences.java
+++ b/luni/src/main/java/java/util/prefs/AbstractPreferences.java
@@ -30,6 +30,7 @@ import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import org.apache.harmony.luni.util.Base64;
+import libcore.base.EmptyArray;
/**
* This abstract class is a partial implementation of the abstract class
@@ -439,7 +440,7 @@ public abstract class AbstractPreferences extends Preferences {
return deflt;
}
if (svalue.length() == 0) {
- return new byte[0];
+ return EmptyArray.BYTE;
}
try {
byte[] bavalue = svalue.getBytes(Charsets.US_ASCII);
diff --git a/luni/src/main/java/java/util/prefs/XMLParser.java b/luni/src/main/java/java/util/prefs/XMLParser.java
index ed1fe0d..4934212 100644
--- a/luni/src/main/java/java/util/prefs/XMLParser.java
+++ b/luni/src/main/java/java/util/prefs/XMLParser.java
@@ -21,10 +21,13 @@ import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.io.Reader;
import java.io.StringReader;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
@@ -39,6 +42,7 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
+import libcore.base.EmptyArray;
import libcore.io.IoUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -85,11 +89,6 @@ class XMLParser {
static final String DOCTYPE = "<!DOCTYPE preferences SYSTEM";
/*
- * empty string array constant
- */
- private static final String[] EMPTY_SARRAY = new String[0];
-
- /*
* Constant - used by FilePreferencesImpl, which is default implementation of Linux platform
*/
private static final String FILE_PREFS = "<!DOCTYPE map SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>";
@@ -488,14 +487,14 @@ class XMLParser {
if (!file.exists()) {
file.getParentFile().mkdirs();
} else if (file.canRead()) {
- InputStream in = null;
+ Reader reader = null;
FileLock lock = null;
try {
- FileInputStream istream = new FileInputStream(file);
- in = new BufferedInputStream(istream);
- FileChannel channel = istream.getChannel();
+ FileInputStream fileInputStream = new FileInputStream(file);
+ reader = new InputStreamReader(fileInputStream, "UTF-8");
+ FileChannel channel = fileInputStream.getChannel();
lock = channel.lock(0L, Long.MAX_VALUE, true);
- Document doc = builder.parse(in);
+ Document doc = builder.parse(new InputSource(reader));
NodeList entries = selectNodeList(doc.getDocumentElement(), "entry");
int length = entries.getLength();
for (int i = 0; i < length; i++) {
@@ -509,7 +508,7 @@ class XMLParser {
} catch (SAXException e) {
} finally {
releaseQuietly(lock);
- IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(reader);
}
} else {
file.delete();
@@ -545,7 +544,7 @@ class XMLParser {
out.write(FILE_PREFS);
out.newLine();
if (prefs.size() == 0) {
- exportEntries(EMPTY_SARRAY, EMPTY_SARRAY, out);
+ exportEntries(EmptyArray.STRING, EmptyArray.STRING, out);
} else {
String[] keys = prefs.keySet().toArray(new String[prefs.size()]);
int length = keys.length;
diff --git a/luni/src/main/java/java/util/zip/Adler32.java b/luni/src/main/java/java/util/zip/Adler32.java
index 7682ef9..504d272 100644
--- a/luni/src/main/java/java/util/zip/Adler32.java
+++ b/luni/src/main/java/java/util/zip/Adler32.java
@@ -22,7 +22,7 @@ package java.util.zip;
* of data. Compared to {@link CRC32} it trades reliability for speed.
* Refer to RFC 1950 for the specification.
*/
-public class Adler32 implements java.util.zip.Checksum {
+public class Adler32 implements Checksum {
private long adler = 1;
diff --git a/luni/src/main/java/java/util/zip/CRC32.java b/luni/src/main/java/java/util/zip/CRC32.java
index 8fc6bb7..1529dfe 100644
--- a/luni/src/main/java/java/util/zip/CRC32.java
+++ b/luni/src/main/java/java/util/zip/CRC32.java
@@ -19,9 +19,9 @@ package java.util.zip;
/**
* The CRC32 class is used to compute a CRC32 checksum from data provided as
- * input value.
+ * input value. See also {@link Adler32} which is almost as good, but cheaper.
*/
-public class CRC32 implements java.util.zip.Checksum {
+public class CRC32 implements Checksum {
private long crc = 0L;
diff --git a/luni/src/main/java/java/util/zip/Checksum.java b/luni/src/main/java/java/util/zip/Checksum.java
index fac7e4e..824b85a 100644
--- a/luni/src/main/java/java/util/zip/Checksum.java
+++ b/luni/src/main/java/java/util/zip/Checksum.java
@@ -18,8 +18,7 @@
package java.util.zip;
/**
- * Holds information about a checksum which was computed with the methods
- * implementing a checksum algorithm.
+ * The interface common to checksum classes such as {@link Adler32} and {@link CRC32}.
*/
public interface Checksum {
diff --git a/luni/src/main/java/java/util/zip/Deflater.java b/luni/src/main/java/java/util/zip/Deflater.java
index 755db4e..4442333 100644
--- a/luni/src/main/java/java/util/zip/Deflater.java
+++ b/luni/src/main/java/java/util/zip/Deflater.java
@@ -18,6 +18,7 @@
package java.util.zip;
import dalvik.system.CloseGuard;
+import libcore.base.EmptyArray;
/**
* This class compresses data using the <i>DEFLATE</i> algorithm (see <a
@@ -109,10 +110,6 @@ public class Deflater {
*/
private static final int FINISH = 4;
- // A stub buffer used when deflate() called while inputBuffer has not been
- // set.
- private static final byte[] STUB_INPUT_BUFFER = new byte[0];
-
private int flushParm = NO_FLUSH;
private boolean finished;
@@ -237,7 +234,7 @@ public class Deflater {
throw new ArrayIndexOutOfBoundsException();
}
if (inputBuffer == null) {
- setInput(STUB_INPUT_BUFFER);
+ setInput(EmptyArray.BYTE);
}
return deflateImpl(buf, off, nbytes, streamHandle, flush);
}
diff --git a/luni/src/main/java/java/util/zip/DeflaterOutputStream.java b/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
index 7cb47db..fcbcb59 100644
--- a/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
+++ b/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
@@ -150,6 +150,7 @@ public class DeflaterOutputStream extends FilterOutputStream {
*/
@Override
public void close() throws IOException {
+ // everything closed here should also be closed in ZipOuputStream.close()
if (!def.finished()) {
finish();
}
diff --git a/luni/src/main/java/java/util/zip/GZIPInputStream.java b/luni/src/main/java/java/util/zip/GZIPInputStream.java
index c52bf72..9107164 100644
--- a/luni/src/main/java/java/util/zip/GZIPInputStream.java
+++ b/luni/src/main/java/java/util/zip/GZIPInputStream.java
@@ -27,9 +27,23 @@ import org.apache.harmony.luni.platform.OSMemory;
* 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.
+ *
+ * <h3>Example</h3>
+ * <p>Using {@code GZIPInputStream} is easier than {@link ZipInputStream}
+ * because GZIP is only for compression, and is not a container for multiple files.
+ * This code decompresses the data from a GZIP stream, similar to the {@code gunzip(1)} utility.
+ * <pre>
+ * InputStream is = ...
+ * GZIPInputStream zis = new GZIPInputStream(new BufferedInputStream(is));
+ * try {
+ * // Reading from 'zis' gets you the uncompressed bytes...
+ * processStream(zis);
+ * } finally {
+ * zis.close();
+ * }
+ * </pre>
*/
public class GZIPInputStream extends InflaterInputStream {
-
private static final int FCOMMENT = 16;
private static final int FEXTRA = 4;
diff --git a/luni/src/main/java/java/util/zip/GZIPOutputStream.java b/luni/src/main/java/java/util/zip/GZIPOutputStream.java
index 02510db..7d30ae8 100644
--- a/luni/src/main/java/java/util/zip/GZIPOutputStream.java
+++ b/luni/src/main/java/java/util/zip/GZIPOutputStream.java
@@ -23,6 +23,21 @@ import java.io.OutputStream;
/**
* The {@code GZIPOutputStream} class is used to write data to a stream in the
* GZIP storage format.
+ *
+ * <h3>Example</h3>
+ * <p>Using {@code GZIPOutputStream} is a little easier than {@link ZipOutputStream}
+ * because GZIP is only for compression, and is not a container for multiple files.
+ * This code creates a GZIP stream, similar to the {@code gzip(1)} utility.
+ * <pre>
+ * OutputStream os = ...
+ * byte[] bytes = ...
+ * GZIPOutputStream zos = new GZIPOutputStream(new BufferedOutputStream(os));
+ * try {
+ * zos.write(bytes);
+ * } finally {
+ * zos.close();
+ * }
+ * </pre>
*/
public class GZIPOutputStream extends DeflaterOutputStream {
diff --git a/luni/src/main/java/java/util/zip/ZipInputStream.java b/luni/src/main/java/java/util/zip/ZipInputStream.java
index a502796..bb9f20c 100644
--- a/luni/src/main/java/java/util/zip/ZipInputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipInputStream.java
@@ -30,17 +30,43 @@ import org.apache.harmony.luni.platform.OSMemory;
/**
* This class provides an implementation of {@code FilterInputStream} that
- * decompresses data from a <i>ZIP-archive</i> input stream.
- * <p>
- * A <i>ZIP-archive</i> is a collection of compressed (or uncompressed) files -
- * 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>
- * While {@code InflaterInputStream} can read a compressed <i>ZIP-archive</i>
- * entry, this extension can read uncompressed entries as well.
- * <p>
- * Use {@code ZipFile} if you can access the archive as a file directly.
+ * decompresses data from an {@code InputStream} containing a ZIP archive.
+ *
+ * <p>A ZIP archive is a collection of (possibly) compressed files.
+ * When reading from a {@code ZipInputStream}, you retrieve the
+ * entry's metadata with {@code getNextEntry} before you can read the userdata.
+ *
+ * <p>Although {@code InflaterInputStream} can only read compressed ZIP archive
+ * entries, this class can read non-compressed entries as well.
+ *
+ * <p>Use {@code ZipFile} if you can access the archive as a file directly,
+ * especially if you want random access to entries, rather than needing to
+ * iterate over all entries.
+ *
+ * <h3>Example</h3>
+ * <p>Using {@code ZipInputStream} is a little more complicated than {@link GZIPInputStream}
+ * because ZIP archives are containers that can contain multiple files. This code pulls all the
+ * files out of a ZIP archive, similar to the {@code unzip(1)} utility.
+ * <pre>
+ * InputStream is = ...
+ * ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
+ * try {
+ * ZipEntry ze;
+ * while ((ze = zis.getNextEntry()) != null) {
+ * ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ * byte[] buffer = new byte[1024];
+ * int count;
+ * while ((count = zis.read(buffer)) != -1) {
+ * baos.write(buffer, 0, count);
+ * }
+ * String filename = ze.getName();
+ * byte[] bytes = baos.toByteArray();
+ * // do something with 'filename' and 'bytes'...
+ * }
+ * } finally {
+ * zis.close();
+ * }
+ * </pre>
*
* @see ZipEntry
* @see ZipFile
diff --git a/luni/src/main/java/java/util/zip/ZipOutputStream.java b/luni/src/main/java/java/util/zip/ZipOutputStream.java
index 7586c84..067fc43 100644
--- a/luni/src/main/java/java/util/zip/ZipOutputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipOutputStream.java
@@ -36,11 +36,31 @@ import java.util.Vector;
* href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">file format
* specification</a>.
*
+ * <h3>Example</h3>
+ * <p>Using {@code ZipOutputStream} is a little more complicated than {@link GZIPOutputStream}
+ * because ZIP archives are containers that can contain multiple files. This code creates a ZIP
+ * archive containing several files, similar to the {@code zip(1)} utility.
+ * <pre>
+ * OutputStream os = ...
+ * ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(os));
+ * try {
+ * for (int i = 0; i < fileCount; ++i) {
+ * String filename = ...
+ * byte[] bytes = ...
+ * ZipEntry entry = new ZipEntry(filename);
+ * zos.putNextEntry(entry);
+ * zos.write(bytes);
+ * zos.closeEntry();
+ * }
+ * } finally {
+ * zos.close();
+ * }
+ * </pre>
+ *
* @see ZipEntry
* @see ZipFile
*/
-public class ZipOutputStream extends DeflaterOutputStream implements
- ZipConstants {
+public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
/**
* Indicates deflated entries.
@@ -92,8 +112,10 @@ public class ZipOutputStream extends DeflaterOutputStream implements
*/
@Override
public void close() throws IOException {
+ // don't call super.close() because that calls finish() conditionally
if (out != null) {
finish();
+ def.end();
out.close();
out = null;
}
diff --git a/luni/src/main/java/javax/crypto/spec/PBEKeySpec.java b/luni/src/main/java/javax/crypto/spec/PBEKeySpec.java
index 1d67f68..c84cdcb 100644
--- a/luni/src/main/java/javax/crypto/spec/PBEKeySpec.java
+++ b/luni/src/main/java/javax/crypto/spec/PBEKeySpec.java
@@ -19,6 +19,7 @@ package javax.crypto.spec;
import java.security.spec.KeySpec;
import java.util.Arrays;
+import libcore.base.EmptyArray;
/**
* The key specification for a <i>password based encryption</i> key.
@@ -41,7 +42,7 @@ public class PBEKeySpec implements KeySpec {
*/
public PBEKeySpec(char[] password) {
if (password == null) {
- this.password = new char[0];
+ this.password = EmptyArray.CHAR;
} else {
this.password = new char[password.length];
System.arraycopy(password, 0, this.password, 0, password.length);
@@ -85,7 +86,7 @@ public class PBEKeySpec implements KeySpec {
}
if (password == null) {
- this.password = new char[0];
+ this.password = EmptyArray.CHAR;
} else {
this.password = new char[password.length];
System.arraycopy(password, 0, this.password, 0, password.length);
@@ -123,7 +124,7 @@ public class PBEKeySpec implements KeySpec {
}
if (password == null) {
- this.password = new char[0];
+ this.password = EmptyArray.CHAR;
} else {
this.password = new char[password.length];
System.arraycopy(password, 0, this.password, 0, password.length);
diff --git a/luni/src/main/java/javax/crypto/spec/PSource.java b/luni/src/main/java/javax/crypto/spec/PSource.java
index a7f592c..5f520f3 100644
--- a/luni/src/main/java/javax/crypto/spec/PSource.java
+++ b/luni/src/main/java/javax/crypto/spec/PSource.java
@@ -17,6 +17,8 @@
package javax.crypto.spec;
+import libcore.base.EmptyArray;
+
/**
* The source of the label <code>L</code> as specified in <a
* href="http://www.ietf.org/rfc/rfc3447.txt"> PKCS #1</a>.
@@ -68,7 +70,7 @@ public class PSource {
private PSpecified() {
super("PSpecified");
- p = new byte[0];
+ p = EmptyArray.BYTE;
}
/**
diff --git a/luni/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java b/luni/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java
index 3e58897..267648c 100644
--- a/luni/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java
+++ b/luni/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java
@@ -21,6 +21,7 @@ import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.SocketException;
+import libcore.base.EmptyArray;
/**
* Default inoperative implementation of javax.net.ssl.SSLServerSocketFactory
@@ -35,12 +36,12 @@ class DefaultSSLServerSocketFactory extends SSLServerSocketFactory {
@Override
public String[] getDefaultCipherSuites() {
- return new String[0];
+ return EmptyArray.STRING;
}
@Override
public String[] getSupportedCipherSuites() {
- return new String[0];
+ return EmptyArray.STRING;
}
@Override
diff --git a/luni/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java b/luni/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java
index a44404f..5b5025d 100644
--- a/luni/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java
+++ b/luni/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java
@@ -22,6 +22,7 @@ import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
+import libcore.base.EmptyArray;
/**
* Default inoperative implementation of javax.net.ssl.SSLSocketFactory
@@ -37,12 +38,12 @@ class DefaultSSLSocketFactory extends SSLSocketFactory {
@Override
public String[] getDefaultCipherSuites() {
- return new String[0];
+ return EmptyArray.STRING;
}
@Override
public String[] getSupportedCipherSuites() {
- return new String[0];
+ return EmptyArray.STRING;
}
@Override
diff --git a/luni/src/main/java/libcore/base/EmptyArray.java b/luni/src/main/java/libcore/base/EmptyArray.java
new file mode 100644
index 0000000..6e97876
--- /dev/null
+++ b/luni/src/main/java/libcore/base/EmptyArray.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 libcore.base;
+
+public final class EmptyArray {
+ private EmptyArray() {}
+
+ public static final boolean[] BOOLEAN = new boolean[0];
+ public static final byte[] BYTE = new byte[0];
+ public static final char[] CHAR = new char[0];
+ public static final double[] DOUBLE = new double[0];
+ public static final int[] INT = new int[0];
+
+ public static final Class<?>[] CLASS = new Class[0];
+ public static final Object[] OBJECT = new Object[0];
+ public static final String[] STRING = new String[0];
+}
diff --git a/luni/src/main/java/libcore/base/Streams.java b/luni/src/main/java/libcore/base/Streams.java
index e746389..0ef5189 100644
--- a/luni/src/main/java/libcore/base/Streams.java
+++ b/luni/src/main/java/libcore/base/Streams.java
@@ -16,6 +16,7 @@
package libcore.base;
+import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
@@ -53,6 +54,22 @@ public final class Streams {
}
}
+ /**
+ * Returns a new byte[] containing the entire contents of the given InputStream.
+ * Useful when you don't know in advance how much data there is to be read.
+ */
+ public static byte[] readFully(InputStream in) throws IOException {
+ byte[] buffer = new byte[1024];
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ while (true) {
+ int byteCount = in.read(buffer);
+ if (byteCount == -1) {
+ return bytes.toByteArray();
+ }
+ bytes.write(buffer, 0, byteCount);
+ }
+ }
+
public static void skipAll(InputStream in) throws IOException {
do {
in.skip(Long.MAX_VALUE);
diff --git a/luni/src/main/java/libcore/internal/StringPool.java b/luni/src/main/java/libcore/internal/StringPool.java
new file mode 100644
index 0000000..779a59a
--- /dev/null
+++ b/luni/src/main/java/libcore/internal/StringPool.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 libcore.internal;
+
+/**
+ * A pool of string instances. Unlike the {@link String#intern() VM's
+ * interned strings}, this pool provides no guarantee of reference equality.
+ * It is intended only to save allocations. This class is not thread safe.
+ */
+public final class StringPool {
+
+ private final String[] pool = new String[512];
+
+ /**
+ * Returns a string equal to {@code new String(array, start, length)}.
+ */
+ public String get(char[] array, int start, int length) {
+ // Compute an arbitrary hash of the content
+ int hashCode = 0;
+ for (int i = start; i < start + length; i++) {
+ hashCode = (hashCode * 31) + array[i];
+ }
+
+ // Pick a bucket using Doug Lea's supplemental secondaryHash function (from HashMap)
+ hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12);
+ hashCode ^= (hashCode >>> 7) ^ (hashCode >>> 4);
+ int index = hashCode & (pool.length - 1);
+
+ String pooled = pool[index];
+ if (pooled != null && pooled.contentEquals(array, start, length)) {
+ return pooled;
+ }
+
+ String result = new String(array, start, length);
+ pool[index] = result;
+ return result;
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java b/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java
index 3a70767..78e5440 100644
--- a/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java
+++ b/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java
@@ -17,8 +17,10 @@
package org.apache.harmony.luni.internal.util;
import java.nio.charset.Charsets;
+import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
+import java.util.Formatter;
import java.util.TimeZone;
import libcore.base.Objects;
@@ -41,38 +43,38 @@ final class ZoneInfo extends TimeZone {
private int mRawOffset;
private final int[] mTransitions;
- private final int[] mGmtOffs;
+ private final int[] mOffsets;
private final byte[] mTypes;
private final byte[] mIsDsts;
private final boolean mUseDst;
- ZoneInfo(String name, int[] transitions, byte[] type, int[] gmtoff, byte[] isdst) {
+ ZoneInfo(String name, int[] transitions, byte[] type, int[] gmtOffsets, byte[] isDsts) {
mTransitions = transitions;
mTypes = type;
- mGmtOffs = gmtoff;
- mIsDsts = isdst;
+ mIsDsts = isDsts;
setID(name);
// Use the latest non-daylight offset (if any) as the raw offset.
- int laststd;
- for (laststd = mTransitions.length - 1; laststd >= 0; laststd--) {
- if (mIsDsts[mTypes[laststd] & 0xFF] == 0) {
+ int lastStd;
+ for (lastStd = mTransitions.length - 1; lastStd >= 0; lastStd--) {
+ if (mIsDsts[mTypes[lastStd] & 0xFF] == 0) {
break;
}
}
- if (laststd < 0) {
- laststd = 0;
+ if (lastStd < 0) {
+ lastStd = 0;
}
- if (laststd >= mTypes.length) {
- mRawOffset = mGmtOffs[0];
+ if (lastStd >= mTypes.length) {
+ mRawOffset = gmtOffsets[0];
} else {
- mRawOffset = mGmtOffs[mTypes[laststd] & 0xFF];
+ mRawOffset = gmtOffsets[mTypes[lastStd] & 0xFF];
}
- // Subtract the raw offset from all offsets so it can be changed
- // and affect them too.
- for (int i = 0; i < mGmtOffs.length; i++) {
- mGmtOffs[i] -= mRawOffset;
+ // Rather than keep offsets from UTC, we use offsets from local time, so the raw offset
+ // can be changed and automatically affect all the offsets.
+ mOffsets = gmtOffsets;
+ for (int i = 0; i < mOffsets.length; i++) {
+ mOffsets[i] -= mRawOffset;
}
// Is this zone still observing DST?
@@ -109,8 +111,9 @@ final class ZoneInfo extends TimeZone {
calc += year * (365 * MILLISECONDS_PER_DAY);
calc += ((year + 3) / 4) * MILLISECONDS_PER_DAY;
- if (year > 0)
+ if (year > 0) {
calc -= ((year - 1) / 100) * MILLISECONDS_PER_DAY;
+ }
boolean isLeap = (year == 0 || (year % 4 == 0 && year % 100 != 0));
int[] mlen = isLeap ? LEAP : NORMAL;
@@ -131,13 +134,13 @@ final class ZoneInfo extends TimeZone {
int trans = Arrays.binarySearch(mTransitions, unix);
if (trans == ~0) {
- return mGmtOffs[0] * 1000 + mRawOffset;
+ return mRawOffset + mOffsets[0] * 1000;
}
if (trans < 0) {
trans = ~trans - 1;
}
- return mGmtOffs[mTypes[trans] & 0xFF] * 1000 + mRawOffset;
+ return mRawOffset + mOffsets[mTypes[trans] & 0xFF] * 1000;
}
@Override
@@ -183,7 +186,7 @@ final class ZoneInfo extends TimeZone {
}
return mRawOffset == other.mRawOffset
// Arrays.equals returns true if both arrays are null
- && Arrays.equals(mGmtOffs, other.mGmtOffs)
+ && Arrays.equals(mOffsets, other.mOffsets)
&& Arrays.equals(mIsDsts, other.mIsDsts)
&& Arrays.equals(mTypes, other.mTypes)
&& Arrays.equals(mTransitions, other.mTransitions);
@@ -202,7 +205,7 @@ final class ZoneInfo extends TimeZone {
final int prime = 31;
int result = 1;
result = prime * result + getID().hashCode();
- result = prime * result + Arrays.hashCode(mGmtOffs);
+ result = prime * result + Arrays.hashCode(mOffsets);
result = prime * result + Arrays.hashCode(mIsDsts);
result = prime * result + mRawOffset;
result = prime * result + Arrays.hashCode(mTransitions);
@@ -213,7 +216,29 @@ final class ZoneInfo extends TimeZone {
@Override
public String toString() {
- return getClass().getName() + "[" + getID() + ",mRawOffset=" + mRawOffset +
- ",mUseDst=" + mUseDst + "]";
+ StringBuilder sb = new StringBuilder();
+ // First the basics...
+ sb.append(getClass().getName() + "[" + getID() + ",mRawOffset=" + mRawOffset +
+ ",mUseDst=" + mUseDst + "]");
+ // ...followed by a zdump(1)-like description of all our transition data.
+ sb.append("\n");
+ Formatter f = new Formatter(sb);
+ for (int i = 0; i < mTransitions.length; ++i) {
+ int type = mTypes[i] & 0xff;
+ String utcTime = formatTime(mTransitions[i], TimeZone.getTimeZone("UTC"));
+ String localTime = formatTime(mTransitions[i], this);
+ int offset = mOffsets[type];
+ int gmtOffset = mRawOffset/1000 + offset;
+ f.format("%4d : time=%10d %s = %s isDst=%d offset=%5d gmtOffset=%d\n",
+ i, mTransitions[i], utcTime, localTime, mIsDsts[type], offset, gmtOffset);
+ }
+ return sb.toString();
+ }
+
+ private static String formatTime(int s, TimeZone tz) {
+ SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy zzz");
+ sdf.setTimeZone(tz);
+ long ms = ((long) s) * 1000L;
+ return sdf.format(new Date(ms));
}
}
diff --git a/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfoDB.java b/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfoDB.java
index 264a9a0..e20d82d 100644
--- a/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfoDB.java
+++ b/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfoDB.java
@@ -219,7 +219,14 @@ public final class ZoneInfoDB {
for (int i = 0; i < tzh_typecnt; ++i) {
gmtOffsets[i] = data.readInt();
isDsts[i] = data.readByte();
- data.skip(1); // Skip abbreviation index.
+ // We skip the abbreviation index. This would let us provide historically-accurate
+ // time zone abbreviations (such as "AHST", "YST", and "AKST" for standard time in
+ // America/Anchorage in 1982, 1983, and 1984 respectively). ICU only knows the current
+ // names, though, so even if we did use this data to provide the correct abbreviations
+ // for en_US, we wouldn't be able to provide correct abbreviations for other locales,
+ // nor would we be able to provide correct long forms (such as "Yukon Standard Time")
+ // for any locale. (The RI doesn't do any better than us here either.)
+ data.skip(1);
}
return new ZoneInfo(id, transitions, type, gmtOffsets, isDsts);
diff --git a/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java b/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
index 17d3864..8fa73e0 100644
--- a/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
@@ -29,6 +29,7 @@ import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
+import libcore.base.EmptyArray;
import org.apache.harmony.luni.platform.Platform;
/**
@@ -154,8 +155,7 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
@Override
protected int peek(InetAddress sender) throws IOException {
// We don't actually want the data: we just want the DatagramPacket's filled-in address.
- byte[] bytes = new byte[0];
- DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
+ DatagramPacket packet = new DatagramPacket(EmptyArray.BYTE, 0);
int result = peekData(packet);
Platform.NETWORK.setInetAddress(sender, packet.getAddress().getAddress());
return result;
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/Base64.java b/luni/src/main/java/org/apache/harmony/luni/util/Base64.java
index 52aaa96..3521b2d 100644
--- a/luni/src/main/java/org/apache/harmony/luni/util/Base64.java
+++ b/luni/src/main/java/org/apache/harmony/luni/util/Base64.java
@@ -22,6 +22,7 @@
package org.apache.harmony.luni.util;
import java.nio.charset.Charset;
+import libcore.base.EmptyArray;
/**
* This class implements Base64 encoding/decoding functionality
@@ -40,7 +41,7 @@ public class Base64 {
int length = len / 4 * 3;
// return an empty array on empty or short input without padding
if (length == 0) {
- return new byte[0];
+ return EmptyArray.BYTE;
}
// temporary array
byte[] out = new byte[length];
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/InputStreamHelper.java b/luni/src/main/java/org/apache/harmony/luni/util/InputStreamHelper.java
deleted file mode 100644
index d1ceba5..0000000
--- a/luni/src/main/java/org/apache/harmony/luni/util/InputStreamHelper.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * 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;
-import java.util.Arrays;
-
-/**
- * The class contains static {@link java.io.InputStream} utilities.
- */
-public class InputStreamHelper {
-
- /**
- * 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];
- }
-
- /**
- * The extension of <code>ByteArrayInputStream</code> which exposes an
- * underlying buffer.
- */
- static class ExposedByteArrayInputStream extends ByteArrayInputStream {
- public ExposedByteArrayInputStream(byte[] buf) {
- super(buf);
- }
-
- public ExposedByteArrayInputStream(byte[] buf, int offset, int length) {
- super(buf, offset, length);
- }
-
- /**
- * Reads the whole stream and returns the stream snapshot.
- */
- public synchronized byte[] expose() {
- if (pos == 0 && count == buf.length) {
- skip(count);
- return buf;
- }
-
- final int available = available();
- final byte[] buffer = new byte[available];
- System.arraycopy(buf, pos, buffer, 0, available);
- skip(available);
- return buffer;
- }
- }
-
- /**
- * 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 {
- 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();
- }
-
- /**
- * Reads all the bytes from the given input stream.
- *
- * Calls read multiple times on the given input stream until it receives an
- * end of file marker. Returns the combined results as a byte array. Note
- * that this method may block if the underlying stream read blocks.
- *
- * @param is
- * the input stream to be read.
- * @return the content of the stream as a byte array.
- * @throws IOException
- * if a read error occurs.
- */
- public static byte[] readFullyAndClose(InputStream is) throws IOException {
-
- try {
- // Initial read
- byte[] buffer = new byte[1024];
- int count = is.read(buffer);
- int nextByte = is.read();
-
- // Did we get it all in one read?
- if (nextByte == -1) {
- return Arrays.copyOf(buffer, count);
- }
-
- // Requires additional reads
- ByteArrayOutputStream baos = new ByteArrayOutputStream(count * 2);
- baos.write(buffer, 0, count);
- baos.write(nextByte);
- while (true) {
- count = is.read(buffer);
- if (count == -1) {
- return baos.toByteArray();
- }
- baos.write(buffer, 0, count);
- }
- } finally {
- is.close();
- }
- }
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java
index c07e2b1..1ffd55a 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java
@@ -24,6 +24,7 @@ import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.security.InvalidParameterException;
import java.security.SecureRandomSpi;
+import libcore.base.EmptyArray;
/**
* This class extends the SecureRandomSpi class implementing all its abstract methods. <BR>
@@ -234,13 +235,12 @@ public class SHA1PRNG_SecureRandomImpl extends SecureRandomSpi implements Serial
throw new NegativeArraySizeException(Integer.toString(numBytes));
}
if (numBytes == 0) {
- return new byte[0];
+ return EmptyArray.BYTE;
}
if (myRandom == null) {
myRandom = new SHA1PRNG_SecureRandomImpl();
- myRandom.engineSetSeed(RandomBitsSupplier
- .getRandomBits(DIGEST_LENGTH));
+ myRandom.engineSetSeed(RandomBitsSupplier.getRandomBits(DIGEST_LENGTH));
}
myBytes = new byte[numBytes];
diff --git a/luni/src/main/java/org/apache/harmony/xml/ExpatPullParser.java b/luni/src/main/java/org/apache/harmony/xml/ExpatPullParser.java
index c48962a..2ddf464 100644
--- a/luni/src/main/java/org/apache/harmony/xml/ExpatPullParser.java
+++ b/luni/src/main/java/org/apache/harmony/xml/ExpatPullParser.java
@@ -763,7 +763,7 @@ public class ExpatPullParser implements XmlPullParser {
}
if (length == 0) {
- return;
+ return; // TODO: can't happen?
}
flush(parser, length);
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java
index 6942894..b39b9e4 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java
@@ -20,6 +20,7 @@ package org.apache.harmony.xnet.provider.jsse;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Arrays;
+import libcore.base.EmptyArray;
/**
* Represents Client Hello message
@@ -141,7 +142,7 @@ public class ClientHello extends Message {
if (challenge_length < 16) {
fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello, short challenge data");
}
- session_id = new byte[0];
+ session_id = EmptyArray.BYTE;
cipher_suites = new CipherSuite[cipher_spec_length/3];
for (int i = 0; i < cipher_suites.length; i++) {
byte b0 = (byte) in.read();
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java
index 4103cf4..3f2daeb 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java
@@ -19,6 +19,7 @@ package org.apache.harmony.xnet.provider.jsse;
import java.io.IOException;
import java.math.BigInteger;
+import libcore.base.EmptyArray;
/**
* Represents client key exchange message
@@ -79,7 +80,7 @@ public class ClientKeyExchange extends Message {
*
*/
public ClientKeyExchange() {
- exchange_keys = new byte[0];
+ exchange_keys = EmptyArray.BYTE;
length = 0;
isRSA = false;
}
@@ -97,7 +98,7 @@ public class ClientKeyExchange extends Message {
this.isRSA = isRSA;
if (length == 0) {
this.length = 0;
- exchange_keys = new byte[0];
+ exchange_keys = EmptyArray.BYTE;
} else {
int size;
if (isRSA && !isTLS) {// SSL3.0 RSA
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java
index e0ca33c..a100513 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java
@@ -30,6 +30,7 @@ import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.net.ssl.SSLException;
+import libcore.base.EmptyArray;
/**
* This class represents Signature type, as described in TLS v 1.0 Protocol
@@ -190,15 +191,15 @@ public class DigitalSignature {
} else if (cipher != null) {
return cipher.doFinal();
}
- return new byte[0];
+ return EmptyArray.BYTE;
} catch (DigestException e){
- return new byte[0];
+ return EmptyArray.BYTE;
} catch (SignatureException e){
- return new byte[0];
+ return EmptyArray.BYTE;
} catch (BadPaddingException e){
- return new byte[0];
+ return EmptyArray.BYTE;
} catch (IllegalBlockSizeException e){
- return new byte[0];
+ return EmptyArray.BYTE;
}
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java
index 7df12c0..b5ffa06 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java
@@ -30,6 +30,7 @@ import java.security.cert.CertificateException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactorySpi;
import javax.net.ssl.ManagerFactoryParameters;
+import libcore.base.EmptyArray;
/**
* KeyManagerFactory implementation.
@@ -56,7 +57,7 @@ public class KeyManagerFactoryImpl extends KeyManagerFactorySpi {
if (password != null) {
pwd = password.clone();
} else {
- pwd = new char[0];
+ pwd = EmptyArray.CHAR;
}
} else {
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
@@ -85,7 +86,7 @@ public class KeyManagerFactoryImpl extends KeyManagerFactorySpi {
}
});
if (keyStorePwd == null) {
- pwd = new char[0];
+ pwd = EmptyArray.CHAR;
} else {
pwd = keyStorePwd.toCharArray();
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
index 08c0822..da974f9 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
@@ -20,6 +20,7 @@ package org.apache.harmony.xnet.provider.jsse;
import java.io.PrintStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import libcore.base.EmptyArray;
/**
* This class provides debug logging for JSSE provider implementation
@@ -106,7 +107,7 @@ public class Logger {
}
});
} catch (Exception e) {
- names = new String[0];
+ names = EmptyArray.STRING;
}
}
@@ -119,4 +120,3 @@ public class Logger {
return null;
}
}
-
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index ee2b33a..40724e7 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -32,6 +32,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import javax.net.ssl.SSLException;
/**
@@ -247,8 +248,11 @@ public final class NativeCrypto {
// add(null, "PSK-RC4-SHA");
}
- private static final String[] SUPPORTED_CIPHER_SUITES
- = STANDARD_TO_OPENSSL_CIPHER_SUITES.keySet().toArray(new String[0]);
+ private static final String[] SUPPORTED_CIPHER_SUITES;
+ static {
+ Set<String> suites = STANDARD_TO_OPENSSL_CIPHER_SUITES.keySet();
+ SUPPORTED_CIPHER_SUITES = suites.toArray(new String[suites.size()]);
+ }
// SSL mode from ssl.h
public static long SSL_MODE_HANDSHAKE_CUTTHROUGH = 0x00000040L;
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSocketFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSocketFactoryImpl.java
index 222ebec..1899342 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSocketFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSocketFactoryImpl.java
@@ -22,6 +22,7 @@ import java.net.InetAddress;
import java.net.ServerSocket;
import java.security.KeyManagementException;
import javax.net.ssl.SSLServerSocketFactory;
+import libcore.base.EmptyArray;
/**
* Implementation of SSLServerSocketFactory.
@@ -61,7 +62,7 @@ public class SSLServerSocketFactoryImpl extends SSLServerSocketFactory {
@Override
public String[] getDefaultCipherSuites() {
if (instantiationException != null) {
- return new String[0];
+ return EmptyArray.STRING;
}
return sslParameters.getEnabledCipherSuites();
}
@@ -72,7 +73,7 @@ public class SSLServerSocketFactoryImpl extends SSLServerSocketFactory {
@Override
public String[] getSupportedCipherSuites() {
if (instantiationException != null) {
- return new String[0];
+ return EmptyArray.STRING;
}
return CipherSuite.getSupportedCipherSuiteNames();
}
@@ -127,4 +128,3 @@ public class SSLServerSocketFactoryImpl extends SSLServerSocketFactory {
(SSLParametersImpl) sslParameters.clone());
}
}
-
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java
index 582d316..0bf4007 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java
@@ -33,6 +33,7 @@ import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionBindingEvent;
import javax.net.ssl.SSLSessionBindingListener;
import javax.net.ssl.SSLSessionContext;
+import libcore.base.EmptyArray;
import libcore.base.Objects;
/**
@@ -175,7 +176,7 @@ public class SSLSessionImpl implements SSLSession, Cloneable {
lastAccessedTime = creationTime;
if (cipher_suite == null) {
this.cipherSuite = CipherSuite.SSL_NULL_WITH_NULL_NULL;
- id = new byte[0];
+ id = EmptyArray.BYTE;
isServer = false;
isValid = false;
} else {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java
index 4e185fd..9cd9eeb 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java
@@ -23,6 +23,7 @@ import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import javax.net.ssl.SSLSocketFactory;
+import libcore.base.EmptyArray;
/**
* Implementation of SSLSocketFactory.
@@ -62,7 +63,7 @@ public class SSLSocketFactoryImpl extends SSLSocketFactory {
@Override
public String[] getDefaultCipherSuites() {
if (instantiationException != null) {
- return new String[0];
+ return EmptyArray.STRING;
}
return sslParameters.getEnabledCipherSuites();
}
@@ -73,7 +74,7 @@ public class SSLSocketFactoryImpl extends SSLSocketFactory {
@Override
public String[] getSupportedCipherSuites() {
if (instantiationException != null) {
- return new String[0];
+ return EmptyArray.STRING;
}
return CipherSuite.getSupportedCipherSuiteNames();
}
@@ -159,4 +160,3 @@ public class SSLSocketFactoryImpl extends SSLSocketFactory {
// ------------------------------------------------------------------
}
-
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java
index 149be0c..f894331 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java
@@ -32,6 +32,7 @@ import java.security.cert.CertificateException;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactorySpi;
+import libcore.base.EmptyArray;
/**
*
@@ -91,7 +92,7 @@ public class TrustManagerFactoryImpl extends TrustManagerFactorySpi {
});
char[] pwd;
if (keyStorePwd == null) {
- pwd = new char[0];
+ pwd = EmptyArray.CHAR;
} else {
pwd = keyStorePwd.toCharArray();
}
diff --git a/luni/src/main/java/org/apache/xml/serializer/ToHTMLStream.java b/luni/src/main/java/org/apache/xml/serializer/ToHTMLStream.java
index 4f2927a..9414875 100644
--- a/luni/src/main/java/org/apache/xml/serializer/ToHTMLStream.java
+++ b/luni/src/main/java/org/apache/xml/serializer/ToHTMLStream.java
@@ -25,6 +25,8 @@ import java.util.Properties;
import javax.xml.transform.Result;
+import libcore.base.EmptyArray;
+
import org.apache.xml.serializer.utils.MsgKey;
import org.apache.xml.serializer.utils.Utils;
import org.xml.sax.Attributes;
@@ -2054,7 +2056,7 @@ public class ToHTMLStream extends ToStream
final Node m_Root;
/** helper buffer to convert Strings to char arrays */
- private char[] m_charBuffer = new char[0];
+ private char[] m_charBuffer = EmptyArray.CHAR;
/** true if the search for an object is lower case only with the key */
private final boolean m_lowerCaseOnly;
diff --git a/luni/src/main/java/org/xml/sax/ext/Attributes2Impl.java b/luni/src/main/java/org/xml/sax/ext/Attributes2Impl.java
index 83cee5b..b3ec94a 100644
--- a/luni/src/main/java/org/xml/sax/ext/Attributes2Impl.java
+++ b/luni/src/main/java/org/xml/sax/ext/Attributes2Impl.java
@@ -7,7 +7,7 @@ package org.xml.sax.ext;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.AttributesImpl;
-
+import libcore.base.EmptyArray;
/**
* SAX2 extension helper for additional Attributes information,
@@ -46,8 +46,8 @@ public class Attributes2Impl extends AttributesImpl implements Attributes2
*/
public Attributes2Impl () {
// BEGIN android-added
- declared = new boolean[0];
- specified = new boolean[0];
+ declared = EmptyArray.BOOLEAN;
+ specified = EmptyArray.BOOLEAN;
// END android-added
}
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 380bb7e..653c125 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -29,7 +29,6 @@ extern int register_java_io_Console(JNIEnv* env);
extern int register_java_io_File(JNIEnv* env);
extern int register_java_io_FileDescriptor(JNIEnv* env);
extern int register_java_io_ObjectInputStream(JNIEnv* env);
-extern int register_java_io_ObjectOutputStream(JNIEnv* env);
extern int register_java_io_ObjectStreamClass(JNIEnv* env);
extern int register_java_lang_Character(JNIEnv* env);
extern int register_java_lang_Double(JNIEnv* env);
@@ -80,7 +79,6 @@ extern "C" int registerCoreLibrariesJni(JNIEnv* env) {
register_java_io_File(env) != -1 &&
register_java_io_FileDescriptor(env) != -1 &&
register_java_io_ObjectInputStream(env) != -1 &&
- register_java_io_ObjectOutputStream(env) != -1 &&
register_java_io_ObjectStreamClass(env) != -1 &&
register_java_lang_Character(env) != -1 &&
register_java_lang_Double(env) != -1 &&
diff --git a/luni/src/main/native/java_io_File.cpp b/luni/src/main/native/java_io_File.cpp
index ea8d12b..81794d8 100644
--- a/luni/src/main/native/java_io_File.cpp
+++ b/luni/src/main/native/java_io_File.cpp
@@ -25,6 +25,9 @@
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
#include "StaticAssert.h"
+#include "readlink.h"
+
+#include <string>
#include <dirent.h>
#include <errno.h>
@@ -32,8 +35,8 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <sys/vfs.h>
#include <sys/types.h>
+#include <sys/vfs.h>
#include <time.h>
#include <unistd.h>
#include <utime.h>
@@ -57,8 +60,7 @@ static bool doStat(JNIEnv* env, jstring javaPath, struct stat& sb) {
static jlong File_lengthImpl(JNIEnv* env, jclass, jstring javaPath) {
struct stat sb;
if (!doStat(env, javaPath, sb)) {
- // We must return 0 for files that don't exist.
- // TODO: shouldn't we throw an IOException for ELOOP or EACCES?
+ // The RI returns 0 on error. (Even for errors like EACCES or ELOOP.)
return 0;
}
@@ -126,24 +128,27 @@ static jstring File_readlink(JNIEnv* env, jclass, jstring javaPath) {
return NULL;
}
- // We can't know how big a buffer readlink(2) will need, so we need to
- // loop until it says "that fit".
- size_t bufSize = 512;
- while (true) {
- LocalArray<512> buf(bufSize);
- ssize_t len = readlink(path.c_str(), &buf[0], buf.size() - 1);
- if (len == -1) {
- // An error occurred.
- return javaPath;
- }
- if (static_cast<size_t>(len) < buf.size() - 1) {
- // The buffer was big enough.
- buf[len] = '\0'; // readlink(2) doesn't NUL-terminate.
- return env->NewStringUTF(&buf[0]);
- }
- // Try again with a bigger buffer.
- bufSize *= 2;
+ std::string result;
+ if (!readlink(path.c_str(), result)) {
+ jniThrowIOException(env, errno);
+ return NULL;
+ }
+ return env->NewStringUTF(result.c_str());
+}
+
+static jstring File_realpath(JNIEnv* env, jclass, jstring javaPath) {
+ ScopedUtfChars path(env, javaPath);
+ if (path.c_str() == NULL) {
+ return NULL;
+ }
+
+ extern bool realpath(const char* path, std::string& resolved);
+ std::string result;
+ if (!realpath(path.c_str(), result)) {
+ jniThrowIOException(env, errno);
+ return NULL;
}
+ return env->NewStringUTF(result.c_str());
}
static jboolean File_setLastModifiedImpl(JNIEnv* env, jclass, jstring javaPath, jlong ms) {
@@ -421,6 +426,22 @@ static jboolean File_renameToImpl(JNIEnv* env, jclass, jstring javaOldPath, jstr
return (rename(oldPath.c_str(), newPath.c_str()) == 0);
}
+static void File_symlink(JNIEnv* env, jclass, jstring javaOldPath, jstring javaNewPath) {
+ ScopedUtfChars oldPath(env, javaOldPath);
+ if (oldPath.c_str() == NULL) {
+ return;
+ }
+
+ ScopedUtfChars newPath(env, javaNewPath);
+ if (newPath.c_str() == NULL) {
+ return;
+ }
+
+ if (symlink(oldPath.c_str(), newPath.c_str()) == -1) {
+ jniThrowIOException(env, errno);
+ }
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(File, canExecuteImpl, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(File, canReadImpl, "(Ljava/lang/String;)Z"),
@@ -438,11 +459,13 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(File, listImpl, "(Ljava/lang/String;)[Ljava/lang/String;"),
NATIVE_METHOD(File, mkdirImpl, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(File, readlink, "(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(File, realpath, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(File, renameToImpl, "(Ljava/lang/String;Ljava/lang/String;)Z"),
NATIVE_METHOD(File, setExecutableImpl, "(Ljava/lang/String;ZZ)Z"),
NATIVE_METHOD(File, setLastModifiedImpl, "(Ljava/lang/String;J)Z"),
NATIVE_METHOD(File, setReadableImpl, "(Ljava/lang/String;ZZ)Z"),
NATIVE_METHOD(File, setWritableImpl, "(Ljava/lang/String;ZZ)Z"),
+ NATIVE_METHOD(File, symlink, "(Ljava/lang/String;Ljava/lang/String;)V"),
};
int register_java_io_File(JNIEnv* env) {
return jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods));
diff --git a/luni/src/main/native/java_io_ObjectInputStream.cpp b/luni/src/main/native/java_io_ObjectInputStream.cpp
index 1dd2f89..6d8aae7 100644
--- a/luni/src/main/native/java_io_ObjectInputStream.cpp
+++ b/luni/src/main/native/java_io_ObjectInputStream.cpp
@@ -17,53 +17,7 @@
#define LOG_TAG "ObjectInputStream"
-#include "JNIHelp.h"
#include "JniConstants.h"
-#include "ScopedUtfChars.h"
-
-#define SETTER(FUNCTION_NAME, JNI_C_TYPE, JNI_TYPE_STRING, JNI_SETTER_FUNCTION) \
- static void FUNCTION_NAME(JNIEnv* env, jclass, jobject instance, \
- jclass declaringClass, jstring javaFieldName, JNI_C_TYPE newValue) { \
- if (instance == NULL) { \
- return; \
- } \
- ScopedUtfChars fieldName(env, javaFieldName); \
- if (fieldName.c_str() == NULL) { \
- return; \
- } \
- jfieldID fid = env->GetFieldID(declaringClass, fieldName.c_str(), JNI_TYPE_STRING); \
- if (fid != 0) { \
- env->JNI_SETTER_FUNCTION(instance, fid, newValue); \
- } \
- }
-
-SETTER(ObjectInputStream_setFieldBool, jboolean, "Z", SetBooleanField)
-SETTER(ObjectInputStream_setFieldByte, jbyte, "B", SetByteField)
-SETTER(ObjectInputStream_setFieldChar, jchar, "C", SetCharField)
-SETTER(ObjectInputStream_setFieldDouble, jdouble, "D", SetDoubleField)
-SETTER(ObjectInputStream_setFieldFloat, jfloat, "F", SetFloatField)
-SETTER(ObjectInputStream_setFieldInt, jint, "I", SetIntField)
-SETTER(ObjectInputStream_setFieldLong, jlong, "J", SetLongField)
-SETTER(ObjectInputStream_setFieldShort, jshort, "S", SetShortField)
-
-static void ObjectInputStream_setFieldObject(JNIEnv* env, jclass, jobject instance,
- jclass declaringClass, jstring javaFieldName, jstring javaFieldTypeName, jobject newValue) {
- if (instance == NULL) {
- return;
- }
- ScopedUtfChars fieldName(env, javaFieldName);
- if (fieldName.c_str() == NULL) {
- return;
- }
- ScopedUtfChars fieldTypeName(env, javaFieldTypeName);
- if (fieldTypeName.c_str() == NULL) {
- return;
- }
- jfieldID fid = env->GetFieldID(declaringClass, fieldName.c_str(), fieldTypeName.c_str());
- if (fid != 0) {
- env->SetObjectField(instance, fid, newValue);
- }
-}
static jobject ObjectInputStream_newInstance(JNIEnv* env, jclass,
jclass instantiationClass, jclass constructorClass) {
@@ -76,15 +30,6 @@ static jobject ObjectInputStream_newInstance(JNIEnv* env, jclass,
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ObjectInputStream, newInstance, "(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;"),
- NATIVE_METHOD(ObjectInputStream, setFieldObject, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V"),
- NATIVE_METHOD(ObjectInputStream, setFieldByte, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;B)V"),
- NATIVE_METHOD(ObjectInputStream, setFieldChar, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;C)V"),
- NATIVE_METHOD(ObjectInputStream, setFieldDouble, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;D)V"),
- NATIVE_METHOD(ObjectInputStream, setFieldFloat, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;F)V"),
- NATIVE_METHOD(ObjectInputStream, setFieldInt, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;I)V"),
- NATIVE_METHOD(ObjectInputStream, setFieldLong, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;J)V"),
- NATIVE_METHOD(ObjectInputStream, setFieldShort, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;S)V"),
- NATIVE_METHOD(ObjectInputStream, setFieldBool, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Z)V"),
};
int register_java_io_ObjectInputStream(JNIEnv* env) {
return jniRegisterNativeMethods(env, "java/io/ObjectInputStream", gMethods, NELEM(gMethods));
diff --git a/luni/src/main/native/java_io_ObjectOutputStream.cpp b/luni/src/main/native/java_io_ObjectOutputStream.cpp
deleted file mode 100644
index e99f9b2..0000000
--- a/luni/src/main/native/java_io_ObjectOutputStream.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "ObjectOutputStream"
-
-#include "JNIHelp.h"
-#include "JniConstants.h"
-#include "ScopedUtfChars.h"
-
-#define GETTER(FUNCTION_NAME, JNI_C_TYPE, JNI_TYPE_STRING, JNI_GETTER_FUNCTION) \
- static JNI_C_TYPE FUNCTION_NAME(JNIEnv* env, jclass, jobject instance, jclass declaringClass, \
- jstring javaFieldName) { \
- if (instance == NULL) { \
- return JNI_C_TYPE(); \
- } \
- ScopedUtfChars fieldName(env, javaFieldName); \
- if (fieldName.c_str() == NULL) { \
- return JNI_C_TYPE(); \
- } \
- jfieldID fid = env->GetFieldID(declaringClass, fieldName.c_str(), JNI_TYPE_STRING); \
- if (fid == 0) { \
- return JNI_C_TYPE(); \
- } \
- return env->JNI_GETTER_FUNCTION(instance, fid); \
- }
-
-GETTER(ObjectOutputStream_getFieldBool, jboolean, "Z", GetBooleanField)
-GETTER(ObjectOutputStream_getFieldByte, jbyte, "B", GetByteField)
-GETTER(ObjectOutputStream_getFieldChar, jchar, "C", GetCharField)
-GETTER(ObjectOutputStream_getFieldDouble, jdouble, "D", GetDoubleField)
-GETTER(ObjectOutputStream_getFieldFloat, jfloat, "F", GetFloatField)
-GETTER(ObjectOutputStream_getFieldInt, jint, "I", GetIntField)
-GETTER(ObjectOutputStream_getFieldLong, jlong, "J", GetLongField)
-GETTER(ObjectOutputStream_getFieldShort, jshort, "S", GetShortField)
-
-static jobject ObjectOutputStream_getFieldObj(JNIEnv* env, jclass, jobject instance,
- jclass declaringClass, jstring javaFieldName, jstring javaFieldTypeName) {
- ScopedUtfChars fieldName(env, javaFieldName);
- if (fieldName.c_str() == NULL) {
- return NULL;
- }
- ScopedUtfChars fieldTypeName(env, javaFieldTypeName);
- if (fieldTypeName.c_str() == NULL) {
- return NULL;
- }
- jfieldID fid = env->GetFieldID(declaringClass, fieldName.c_str(), fieldTypeName.c_str());
- if (fid == 0) {
- return NULL;
- }
- return env->GetObjectField(instance, fid);
-}
-
-static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(ObjectOutputStream, getFieldBool, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)Z"),
- NATIVE_METHOD(ObjectOutputStream, getFieldByte, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)B"),
- NATIVE_METHOD(ObjectOutputStream, getFieldChar, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)C"),
- NATIVE_METHOD(ObjectOutputStream, getFieldDouble, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)D"),
- NATIVE_METHOD(ObjectOutputStream, getFieldFloat, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)F"),
- NATIVE_METHOD(ObjectOutputStream, getFieldInt, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)I"),
- NATIVE_METHOD(ObjectOutputStream, getFieldLong, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)J"),
- NATIVE_METHOD(ObjectOutputStream, getFieldObj, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;"),
- NATIVE_METHOD(ObjectOutputStream, getFieldShort, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)S"),
-};
-int register_java_io_ObjectOutputStream(JNIEnv* env) {
- return jniRegisterNativeMethods(env, "java/io/ObjectOutputStream", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
index c41eb82..a5ee710 100644
--- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -1000,9 +1000,11 @@ static jint ExpatParser_initialize(JNIEnv* env, jobject object, jstring javaEnco
}
/**
- * Expat decides for itself what character encoding it's looking at. The interface is in terms of
- * bytes, which may point to UTF-8, UTF-16, ISO-8859-1, or US-ASCII. appendBytes, appendCharacters,
- * and appendString thus all call through to this method, strange though that appears.
+ * Decodes the bytes as characters and parse the characters as XML. This
+ * performs character decoding using the charset specified at XML_Parser
+ * creation. For Java chars, that charset must be UTF-16 so that a Java char[]
+ * can be reinterpreted as a UTF-16 encoded byte[]. appendBytes, appendChars
+ * and appendString all call through this method.
*/
static void append(JNIEnv* env, jobject object, jint pointer,
const char* bytes, size_t byteOffset, size_t byteCount, jboolean isFinal) {
diff --git a/luni/src/main/native/readlink.cpp b/luni/src/main/native/readlink.cpp
new file mode 100644
index 0000000..555d515
--- /dev/null
+++ b/luni/src/main/native/readlink.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 "LocalArray.h"
+#include "readlink.h"
+
+#include <string>
+#include <unistd.h>
+
+bool readlink(const char* path, std::string& result) {
+ // We can't know how big a buffer readlink(2) will need, so we need to
+ // loop until it says "that fit".
+ size_t bufSize = 512;
+ while (true) {
+ LocalArray<512> buf(bufSize);
+ ssize_t len = readlink(path, &buf[0], buf.size());
+ if (len == -1) {
+ // An error occurred.
+ return false;
+ }
+ if (static_cast<size_t>(len) < buf.size()) {
+ // The buffer was big enough.
+ result.assign(&buf[0], len);
+ return true;
+ }
+ // Try again with a bigger buffer.
+ bufSize *= 2;
+ }
+}
diff --git a/luni/src/main/native/readlink.h b/luni/src/main/native/readlink.h
new file mode 100644
index 0000000..14031dc
--- /dev/null
+++ b/luni/src/main/native/readlink.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 <string>
+
+/**
+ * Fills 'result' with the contents of the symbolic link 'path'. Sets errno and returns false on
+ * failure, returns true on success. The contents of 'result' on failure are undefined. Possible
+ * errors are those defined for readlink(2), except that this function takes care of sizing the
+ * buffer appropriately.
+ */
+bool readlink(const char* path, std::string& result);
diff --git a/luni/src/main/native/realpath.cpp b/luni/src/main/native/realpath.cpp
new file mode 100644
index 0000000..d1960a4
--- /dev/null
+++ b/luni/src/main/native/realpath.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "readlink.h"
+
+#include <string>
+
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/**
+ * This differs from realpath(3) mainly in its behavior when a path element does not exist or can
+ * not be searched. realpath(3) treats that as an error and gives up, but we have Java-compatible
+ * behavior where we just assume the path element was not a symbolic link. This leads to a textual
+ * treatment of ".." from that point in the path, which may actually lead us back to a path we
+ * can resolve (as in "/tmp/does-not-exist/../blah.txt" which would be an error for realpath(3)
+ * but "/tmp/blah.txt" under the traditional Java interpretation).
+ *
+ * This implementation also removes all the fixed-length buffers of the C original.
+ */
+bool realpath(const char* path, std::string& resolved) {
+ // 'path' must be an absolute path.
+ if (path[0] != '/') {
+ errno = EINVAL;
+ return false;
+ }
+
+ resolved = "/";
+ if (path[1] == '\0') {
+ return true;
+ }
+
+ // Iterate over path components in 'left'.
+ int symlinkCount = 0;
+ std::string left(path + 1);
+ while (!left.empty()) {
+ // Extract the next path component.
+ size_t nextSlash = left.find('/');
+ std::string nextPathComponent = left.substr(0, nextSlash);
+ if (nextSlash != std::string::npos) {
+ left.erase(0, nextSlash + 1);
+ } else {
+ left.clear();
+ }
+ if (nextPathComponent.empty()) {
+ continue;
+ } else if (nextPathComponent == ".") {
+ continue;
+ } else if (nextPathComponent == "..") {
+ // Strip the last path component except when we have single "/".
+ if (resolved.size() > 1) {
+ resolved.erase(resolved.rfind('/'));
+ }
+ continue;
+ }
+
+ // Append the next path component.
+ if (resolved[resolved.size() - 1] != '/') {
+ resolved += '/';
+ }
+ resolved += nextPathComponent;
+
+ // See if we've got a symbolic link, and resolve it if so.
+ struct stat sb;
+ if (lstat(resolved.c_str(), &sb) == 0 && S_ISLNK(sb.st_mode)) {
+ if (symlinkCount++ > MAXSYMLINKS) {
+ errno = ELOOP;
+ return false;
+ }
+
+ std::string symlink;
+ if (!readlink(resolved.c_str(), symlink)) {
+ return false;
+ }
+ if (symlink[0] == '/') {
+ // The symbolic link is absolute, so we need to start from scratch.
+ resolved = "/";
+ } else if (resolved.size() > 1) {
+ // The symbolic link is relative, so we just lose the last path component (which
+ // was the link).
+ resolved.erase(resolved.rfind('/'));
+ }
+
+ if (!left.empty()) {
+ const char* maybeSlash = (symlink[symlink.size() - 1] != '/') ? "/" : "";
+ left = symlink + maybeSlash + left;
+ } else {
+ left = symlink;
+ }
+ }
+ }
+
+ // Remove trailing slash except when the resolved pathname is a single "/".
+ if (resolved.size() > 1 && resolved[resolved.size() - 1] == '/') {
+ resolved.erase(resolved.size() - 1, 1);
+ }
+ return true;
+}
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index 774e8cb..43127d0 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -26,7 +26,6 @@ LOCAL_SRC_FILES := \
java_io_File.cpp \
java_io_FileDescriptor.cpp \
java_io_ObjectInputStream.cpp \
- java_io_ObjectOutputStream.cpp \
java_io_ObjectStreamClass.cpp \
java_lang_Character.cpp \
java_lang_Double.cpp \
@@ -52,6 +51,8 @@ LOCAL_SRC_FILES := \
org_apache_harmony_luni_platform_OSNetworkSystem.cpp \
org_apache_harmony_luni_util_FloatingPointParser.cpp \
org_apache_harmony_xml_ExpatParser.cpp \
+ readlink.cpp \
+ realpath.cpp \
valueOf.cpp
LOCAL_C_INCLUDES += \
diff --git a/luni/src/test/java/libcore/internal/StringPoolTest.java b/luni/src/test/java/libcore/internal/StringPoolTest.java
new file mode 100644
index 0000000..82c7c4e
--- /dev/null
+++ b/luni/src/test/java/libcore/internal/StringPoolTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 libcore.internal;
+
+import junit.framework.TestCase;
+import libcore.internal.StringPool;
+
+public final class StringPoolTest extends TestCase {
+
+ public void testStringPool() {
+ StringPool stringPool = new StringPool();
+ String bcd = stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3);
+ assertEquals("bcd", bcd);
+ assertSame(bcd, stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3));
+ }
+
+ public void testHashCollision() {
+ StringPool stringPool = new StringPool();
+ char[] a = { (char) 1, (char) 0 };
+ char[] b = { (char) 0, (char) 31 };
+ assertEquals(new String(a).hashCode(), new String(b).hashCode());
+
+ String aString = stringPool.get(a, 0, 2);
+ assertEquals(new String(a), aString);
+ String bString = stringPool.get(b, 0, 2);
+ assertEquals(new String(b), bString);
+ assertSame(bString, stringPool.get(b, 0, 2));
+ assertNotSame(aString, stringPool.get(a, 0, 2));
+ }
+}
diff --git a/luni/src/test/java/libcore/java/io/FileTest.java b/luni/src/test/java/libcore/java/io/FileTest.java
index cd9b877..535975d 100644
--- a/luni/src/test/java/libcore/java/io/FileTest.java
+++ b/luni/src/test/java/libcore/java/io/FileTest.java
@@ -186,18 +186,6 @@ public class FileTest extends junit.framework.TestCase {
}
private static void ln_s(String target, String linkName) throws Exception {
- String[] args = new String[] { "ln", "-s", target, linkName };
- // System.err.println("ln -s " + target + " " + linkName);
- Process p = Runtime.getRuntime().exec(args);
- int result = p.waitFor();
- if (result != 0) {
- BufferedReader r = new BufferedReader(new InputStreamReader(p.getErrorStream()));
- String line;
- while ((line = r.readLine()) != null) {
- System.err.println(line);
- }
- fail("ln -s " + target + " " + linkName + " failed. " +
- "Does that file system support symlinks?");
- }
+ File.symlink(target, linkName);
}
}
diff --git a/luni/src/test/java/libcore/java/lang/ArrayIndexOutOfBoundsExceptionTest.java b/luni/src/test/java/libcore/java/lang/ArrayIndexOutOfBoundsExceptionTest.java
index ee7f6e8..041b931 100644
--- a/luni/src/test/java/libcore/java/lang/ArrayIndexOutOfBoundsExceptionTest.java
+++ b/luni/src/test/java/libcore/java/lang/ArrayIndexOutOfBoundsExceptionTest.java
@@ -24,7 +24,7 @@ public final class ArrayIndexOutOfBoundsExceptionTest extends TestCase {
try {
bs[2] = 0;
fail();
- } catch (Exception ex) {
+ } catch (ArrayIndexOutOfBoundsException ex) {
assertEquals("index=2 length=1", ex.getMessage());
}
}
@@ -34,7 +34,7 @@ public final class ArrayIndexOutOfBoundsExceptionTest extends TestCase {
try {
byte b = bs[2];
fail();
- } catch (Exception ex) {
+ } catch (ArrayIndexOutOfBoundsException ex) {
assertEquals("index=2 length=1", ex.getMessage());
}
}
@@ -44,7 +44,7 @@ public final class ArrayIndexOutOfBoundsExceptionTest extends TestCase {
try {
ds[2] = 0.0;
fail();
- } catch (Exception ex) {
+ } catch (ArrayIndexOutOfBoundsException ex) {
assertEquals("index=2 length=1", ex.getMessage());
}
}
@@ -54,7 +54,7 @@ public final class ArrayIndexOutOfBoundsExceptionTest extends TestCase {
try {
double d = ds[2];
fail();
- } catch (Exception ex) {
+ } catch (ArrayIndexOutOfBoundsException ex) {
assertEquals("index=2 length=1", ex.getMessage());
}
}
@@ -64,7 +64,7 @@ public final class ArrayIndexOutOfBoundsExceptionTest extends TestCase {
try {
os[2] = null;
fail();
- } catch (Exception ex) {
+ } catch (ArrayIndexOutOfBoundsException ex) {
assertEquals("index=2 length=1", ex.getMessage());
}
}
@@ -74,7 +74,7 @@ public final class ArrayIndexOutOfBoundsExceptionTest extends TestCase {
try {
Object o = os[2];
fail();
- } catch (Exception ex) {
+ } catch (ArrayIndexOutOfBoundsException ex) {
assertEquals("index=2 length=1", ex.getMessage());
}
}
diff --git a/luni/src/test/java/libcore/java/lang/ArrayStoreExceptionTest.java b/luni/src/test/java/libcore/java/lang/ArrayStoreExceptionTest.java
new file mode 100644
index 0000000..1b4288e
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/ArrayStoreExceptionTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang;
+
+import junit.framework.TestCase;
+
+public final class ArrayStoreExceptionTest extends TestCase {
+ public void testArrayStoreException() throws Exception {
+ Object[] array = new String[10];
+ Object o = new Exception();
+ try {
+ array[0] = o;
+ fail();
+ } catch (ArrayStoreException ex) {
+ ex.printStackTrace();
+ assertEquals("java.lang.Exception cannot be stored in an array of type java.lang.String[]", ex.getMessage());
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/ClassCastExceptionTest.java b/luni/src/test/java/libcore/java/lang/ClassCastExceptionTest.java
new file mode 100644
index 0000000..780f620
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/ClassCastExceptionTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang;
+
+import java.util.EnumMap;
+import java.util.EnumSet;
+import junit.framework.TestCase;
+
+public final class ClassCastExceptionTest extends TestCase {
+ public void testCast() throws Exception {
+ Object o = new Exception();
+ try {
+ String s = (String) o;
+ fail();
+ } catch (ClassCastException ex) {
+ assertEquals("java.lang.Exception cannot be cast to java.lang.String", ex.getMessage());
+ }
+ }
+
+ public void testClassCast() throws Exception {
+ Object o = new Exception();
+ try {
+ String.class.cast(o);
+ fail();
+ } catch (ClassCastException ex) {
+ assertEquals("java.lang.Exception cannot be cast to java.lang.String", ex.getMessage());
+ }
+ }
+
+ public void testClassAsSubclass() throws Exception {
+ try {
+ Exception.class.asSubclass(String.class);
+ fail();
+ } catch (ClassCastException ex) {
+ assertEquals("java.lang.Exception cannot be cast to java.lang.String", ex.getMessage());
+ }
+ }
+
+ enum E { A, B, C };
+ enum F { A, B, C };
+
+ public void testEnumMapPut() throws Exception {
+ EnumMap m = new EnumMap(E.class);
+ try {
+ m.put(F.A, "world");
+ fail();
+ } catch (ClassCastException ex) {
+ ex.printStackTrace();
+ assertNotNull(ex.getMessage());
+ }
+ }
+
+ public void testMiniEnumSetAdd() throws Exception {
+ EnumSet m = EnumSet.noneOf(E.class);
+ try {
+ m.add(F.A);
+ fail();
+ } catch (ClassCastException ex) {
+ ex.printStackTrace();
+ assertNotNull(ex.getMessage());
+ }
+ }
+
+ public void testMiniEnumSetAddAll() throws Exception {
+ EnumSet m = EnumSet.noneOf(E.class);
+ EnumSet n = EnumSet.allOf(F.class);
+ try {
+ m.addAll(n);
+ fail();
+ } catch (ClassCastException ex) {
+ ex.printStackTrace();
+ assertNotNull(ex.getMessage());
+ }
+ }
+
+ enum HugeE {
+ A0, B0, C0, D0, E0, F0, G0, H0, I0, J0, K0, L0, M0, N0, O0, P0, Q0, R0, S0, T0, U0, V0, W0, X0, Y0, Z0,
+ A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, L1, M1, N1, O1, P1, Q1, R1, S1, T1, U1, V1, W1, X1, Y1, Z1,
+ A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, L2, M2, N2, O2, P2, Q2, R2, S2, T2, U2, V2, W2, X2, Y2, Z2,
+ };
+ enum HugeF {
+ A0, B0, C0, D0, E0, F0, G0, H0, I0, J0, K0, L0, M0, N0, O0, P0, Q0, R0, S0, T0, U0, V0, W0, X0, Y0, Z0,
+ A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, L1, M1, N1, O1, P1, Q1, R1, S1, T1, U1, V1, W1, X1, Y1, Z1,
+ A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, L2, M2, N2, O2, P2, Q2, R2, S2, T2, U2, V2, W2, X2, Y2, Z2,
+ };
+
+ public void testHugeEnumSetAdd() throws Exception {
+ EnumSet m = EnumSet.noneOf(HugeE.class);
+ try {
+ m.add(HugeF.A0);
+ fail();
+ } catch (ClassCastException ex) {
+ ex.printStackTrace();
+ assertNotNull(ex.getMessage());
+ }
+ }
+
+ public void testHugeEnumSetAddAll() throws Exception {
+ EnumSet m = EnumSet.noneOf(HugeE.class);
+ EnumSet n = EnumSet.allOf(HugeF.class);
+ try {
+ m.addAll(n);
+ fail();
+ } catch (ClassCastException ex) {
+ ex.printStackTrace();
+ assertNotNull(ex.getMessage());
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java b/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java
index 5968fdf..726b05a 100644
--- a/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java
+++ b/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java
@@ -28,6 +28,7 @@ import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
+import tests.net.StuckServer;
/**
* Test that Socket.close called on another thread interrupts a thread that's blocked doing
@@ -47,24 +48,28 @@ public class ConcurrentCloseTest extends junit.framework.TestCase {
}
public void test_connect() throws Exception {
+ StuckServer ss = new StuckServer();
Socket s = new Socket();
new Killer(s).start();
try {
System.err.println("connect...");
- s.connect(new InetSocketAddress("10.0.0.1", 7));
+ s.connect(ss.getLocalSocketAddress());
fail("connect returned!");
} catch (SocketException expected) {
assertEquals("Socket closed", expected.getMessage());
+ } finally {
+ ss.close();
}
}
public void test_connect_nonBlocking() throws Exception {
+ StuckServer ss = new StuckServer();
SocketChannel s = SocketChannel.open();
new Killer(s.socket()).start();
try {
System.err.println("connect (non-blocking)...");
s.configureBlocking(false);
- s.connect(new InetSocketAddress("10.0.0.2", 7));
+ s.connect(ss.getLocalSocketAddress());
while (!s.finishConnect()) {
// Spin like a mad thing!
}
@@ -76,6 +81,8 @@ public class ConcurrentCloseTest extends junit.framework.TestCase {
} catch (ClosedChannelException alsoOkay) {
// For now, I'm assuming that we're happy as long as we get any reasonable exception.
// It may be that we're supposed to guarantee only one or the other.
+ } finally {
+ ss.close();
}
}
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index a1f99e9..1a11bd9 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -68,6 +68,7 @@ import tests.http.DefaultResponseCache;
import tests.http.MockResponse;
import tests.http.MockWebServer;
import tests.http.RecordedRequest;
+import tests.net.StuckServer;
public class URLConnectionTest extends junit.framework.TestCase {
@@ -1393,27 +1394,16 @@ public class URLConnectionTest extends junit.framework.TestCase {
}
public void testConnectTimeouts() throws IOException {
- // Set a backlog and use it up so that we can expect the
- // URLConnection to properly timeout. According to Steven's
- // 4.5 "listen function", linux adds 3 to the specified
- // backlog, so we need to connect 4 times before it will hang.
- ServerSocket serverSocket = new ServerSocket(0, 1);
- int serverPort = serverSocket.getLocalPort();
- Socket[] sockets = new Socket[4];
- for (int i = 0; i < sockets.length; i++) {
- sockets[i] = new Socket("localhost", serverPort);
- }
-
+ StuckServer ss = new StuckServer();
+ int serverPort = ss.getLocalPort();
URLConnection urlConnection = new URL("http://localhost:" + serverPort).openConnection();
urlConnection.setConnectTimeout(1000);
try {
urlConnection.getInputStream();
fail();
} catch (SocketTimeoutException expected) {
- }
-
- for (Socket s : sockets) {
- s.close();
+ } finally {
+ ss.close();
}
}
diff --git a/luni/src/test/java/libcore/java/text/OldSimpleDateFormatTest.java b/luni/src/test/java/libcore/java/text/OldSimpleDateFormatTest.java
index a4012a8..4da54e6 100644
--- a/luni/src/test/java/libcore/java/text/OldSimpleDateFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/OldSimpleDateFormatTest.java
@@ -408,12 +408,6 @@ public class OldSimpleDateFormatTest extends junit.framework.TestCase {
assertFalse(test.testsFailed);
}
- public void testDefaultMinimalDaysInFirstWeek() {
- Locale.setDefault(Locale.US);
- assertEquals(1, new GregorianCalendar().getMinimalDaysInFirstWeek());
- assertEquals(1, new GregorianCalendar().getFirstDayOfWeek());
- }
-
/**
* @tests java.text.SimpleDateFormat#format(java.util.Date)
*/
diff --git a/luni/src/test/java/libcore/java/util/zip/GzipTest.java b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
index f7e03dc..a28fae5 100644
--- a/luni/src/test/java/libcore/java/util/zip/GzipTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
@@ -27,23 +27,25 @@ import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import junit.framework.TestCase;
-public final class GzipTest extends TestCase {
-
- public void testRoundtripShortMessage() throws IOException {
- byte[] data = gzip(("Hello World").getBytes("UTF-8"));
- assertTrue(Arrays.equals(data, gunzip(gzip(data))));
+public final class GZIPInputStreamTest extends TestCase {
+ public void testShortMessage() throws IOException {
+ byte[] data = new byte[] {
+ 31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -13, 72, -51, -55, -55, 87, 8, -49,
+ 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0
+ };
+ assertEquals("Hello World", new String(gunzip(data), "UTF-8"));
}
- public void testRoundtripLongMessage() throws IOException {
+ public void testLongMessage() throws IOException {
byte[] data = new byte[1024 * 1024];
new Random().nextBytes(data);
- assertTrue(Arrays.equals(data, gunzip(gzip(data))));
+ assertTrue(Arrays.equals(data, gunzip(GZIPOutputStreamTest.gzip(data))));
}
/** http://b/3042574 GzipInputStream.skip() causing CRC failures */
public void testSkip() throws IOException {
byte[] data = new byte[1024 * 1024];
- byte[] gzipped = gzip(data);
+ byte[] gzipped = GZIPOutputStreamTest.gzip(data);
GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(gzipped));
long totalSkipped = 0;
@@ -53,19 +55,11 @@ public final class GzipTest extends TestCase {
totalSkipped += count;
} while (count > 0);
-
assertEquals(data.length, totalSkipped);
+ in.close();
}
- public byte[] gzip(byte[] bytes) throws IOException {
- ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
- OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
- gzippedOut.write(bytes);
- gzippedOut.close();
- return bytesOut.toByteArray();
- }
-
- public byte[] gunzip(byte[] bytes) throws IOException {
+ public static byte[] gunzip(byte[] bytes) throws IOException {
InputStream in = new GZIPInputStream(new ByteArrayInputStream(bytes));
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
@@ -73,6 +67,7 @@ public final class GzipTest extends TestCase {
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
+ in.close();
return out.toByteArray();
}
}
diff --git a/luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java
new file mode 100644
index 0000000..a61880f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.util.zip;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+import junit.framework.TestCase;
+
+public final class GZIPOutputStreamTest extends TestCase {
+ public void testShortMessage() throws IOException {
+ byte[] data = gzip(("Hello World").getBytes("UTF-8"));
+ assertEquals("[31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -13, 72, -51, -55, -55, 87, 8, -49, " +
+ "47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0]", Arrays.toString(data));
+ }
+
+ public void testLongMessage() throws IOException {
+ byte[] data = new byte[1024 * 1024];
+ new Random().nextBytes(data);
+ assertTrue(Arrays.equals(data, GZIPInputStreamTest.gunzip(gzip(data))));
+ }
+
+ public static byte[] gzip(byte[] bytes) throws IOException {
+ ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+ OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
+ gzippedOut.write(bytes);
+ gzippedOut.close();
+ return bytesOut.toByteArray();
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java
new file mode 100644
index 0000000..cb98322
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.util.zip;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import junit.framework.TestCase;
+
+public final class ZipInputStreamTest extends TestCase {
+ public void testShortMessage() throws IOException {
+ byte[] data = "Hello World".getBytes("UTF-8");
+ byte[] zipped = ZipOutputStreamTest.zip("short", data);
+ assertEquals(Arrays.toString(data), Arrays.toString(unzip("short", zipped)));
+ }
+
+ public void testLongMessage() throws IOException {
+ byte[] data = new byte[1024 * 1024];
+ new Random().nextBytes(data);
+ assertTrue(Arrays.equals(data, unzip("r", ZipOutputStreamTest.zip("r", data))));
+ }
+
+ public static byte[] unzip(String name, byte[] bytes) throws IOException {
+ ZipInputStream in = new ZipInputStream(new ByteArrayInputStream(bytes));
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ ZipEntry entry = in.getNextEntry();
+ assertEquals(name, entry.getName());
+
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ out.write(buffer, 0, count);
+ }
+
+ assertNull(in.getNextEntry()); // There's only one entry in the Zip files we create.
+
+ in.close();
+ return out.toByteArray();
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
new file mode 100644
index 0000000..e7c518f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.util.zip;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import junit.framework.TestCase;
+
+public final class ZipOutputStreamTest extends TestCase {
+ public void testShortMessage() throws IOException {
+ byte[] data = "Hello World".getBytes("UTF-8");
+ byte[] zipped = zip("short", data);
+ assertEquals(Arrays.toString(data), Arrays.toString(ZipInputStreamTest.unzip("short", zipped)));
+ }
+
+ // http://b/3181430 --- a sign-extension bug on CRCs with the top bit set.
+ public void test3181430() throws IOException {
+ byte[] data = new byte[1]; // CRC32({ 0 }) == 0xd202ef8d
+ byte[] zipped = zip("z", data);
+ assertEquals(Arrays.toString(data), Arrays.toString(ZipInputStreamTest.unzip("z", zipped)));
+ }
+
+ public void testLongMessage() throws IOException {
+ byte[] data = new byte[1024 * 1024];
+ new Random().nextBytes(data);
+ assertTrue(Arrays.equals(data, ZipInputStreamTest.unzip("r", zip("r", data))));
+ }
+
+ public static byte[] zip(String name, byte[] bytes) throws IOException {
+ ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+ ZipOutputStream zippedOut = new ZipOutputStream(bytesOut);
+
+ ZipEntry entry = new ZipEntry(name);
+ zippedOut.putNextEntry(entry);
+ zippedOut.write(bytes);
+ zippedOut.closeEntry();
+
+ zippedOut.close();
+ return bytesOut.toByteArray();
+ }
+}
diff --git a/luni/src/test/java/org/apache/harmony/xml/ExpatPullParserTest.java b/luni/src/test/java/libcore/xml/ExpatPullParserTest.java
index 71e2671..5d1725d 100644
--- a/luni/src/test/java/org/apache/harmony/xml/ExpatPullParserTest.java
+++ b/luni/src/test/java/libcore/xml/ExpatPullParserTest.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package org.apache.harmony.xml;
+package libcore.xml;
+import org.apache.harmony.xml.ExpatPullParser;
import org.xmlpull.v1.XmlPullParser;
public final class ExpatPullParserTest extends PullParserTest {
@Override XmlPullParser newPullParser() {
- ExpatPullParser parser = new ExpatPullParser();
- return parser;
+ return new ExpatPullParser();
}
}
diff --git a/luni/src/test/java/org/apache/harmony/xml/ExpatSaxParserTest.java b/luni/src/test/java/libcore/xml/ExpatSaxParserTest.java
index 51804f2..2db8e82 100644
--- a/luni/src/test/java/org/apache/harmony/xml/ExpatSaxParserTest.java
+++ b/luni/src/test/java/libcore/xml/ExpatSaxParserTest.java
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package org.apache.harmony.xml;
+package libcore.xml;
import junit.framework.Assert;
import junit.framework.TestCase;
+import org.apache.harmony.xml.ExpatReader;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
@@ -45,6 +46,19 @@ public class ExpatSaxParserTest extends TestCase {
private static final String SNIPPET = "<dagny dad=\"bob\">hello</dagny>";
+ public void testGlobalReferenceTableOverflow() throws Exception {
+ // We used to use a JNI global reference per interned string.
+ // Framework apps have a limit of 2000 JNI global references per VM.
+ StringBuilder xml = new StringBuilder();
+ xml.append("<root>");
+ for (int i = 0; i < 4000; ++i) {
+ xml.append("<tag" + i + ">");
+ xml.append("</tag" + i + ">");
+ }
+ xml.append("</root>");
+ parse(xml.toString(), new DefaultHandler());
+ }
+
public void testExceptions() {
// From startElement().
ContentHandler contentHandler = new DefaultHandler() {
@@ -675,8 +689,7 @@ public class ExpatSaxParserTest extends TestCase {
XMLReader reader = new ExpatReader();
reader.setContentHandler(contentHandler);
reader.parse(new InputSource(new StringReader(xml)));
- }
- catch (IOException e) {
+ } catch (IOException e) {
throw new AssertionError(e);
}
}
diff --git a/luni/src/test/java/org/apache/harmony/xml/JaxenXPathTestSuite.java b/luni/src/test/java/libcore/xml/JaxenXPathTestSuite.java
index 17f0341..eb790aa 100644
--- a/luni/src/test/java/org/apache/harmony/xml/JaxenXPathTestSuite.java
+++ b/luni/src/test/java/libcore/xml/JaxenXPathTestSuite.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.apache.harmony.xml;
+package libcore.xml;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
diff --git a/luni/src/test/java/org/apache/harmony/xml/KxmlPullParserTest.java b/luni/src/test/java/libcore/xml/KxmlPullParserTest.java
index 3603d89..71f25e9 100644
--- a/luni/src/test/java/org/apache/harmony/xml/KxmlPullParserTest.java
+++ b/luni/src/test/java/libcore/xml/KxmlPullParserTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.apache.harmony.xml;
+package libcore.xml;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
diff --git a/luni/src/test/java/org/apache/harmony/xml/NamespacedAttributesLookupTest.java b/luni/src/test/java/libcore/xml/NamespacedAttributesLookupTest.java
index 4f58262..cf12000 100644
--- a/luni/src/test/java/org/apache/harmony/xml/NamespacedAttributesLookupTest.java
+++ b/luni/src/test/java/libcore/xml/NamespacedAttributesLookupTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.apache.harmony.xml;
+package libcore.xml;
import junit.framework.TestCase;
import org.xml.sax.Attributes;
diff --git a/luni/src/test/java/libcore/xml/PullParserTest.java b/luni/src/test/java/libcore/xml/PullParserTest.java
new file mode 100644
index 0000000..d76bed6
--- /dev/null
+++ b/luni/src/test/java/libcore/xml/PullParserTest.java
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 libcore.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import junit.framework.TestCase;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+public abstract class PullParserTest extends TestCase {
+
+ public void testAttributeNoValueWithRelaxed() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
+ parser.setInput(new StringReader("<input checked></input>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals("input", parser.getName());
+ assertEquals("checked", parser.getAttributeName(0));
+ assertEquals("checked", parser.getAttributeValue(0));
+ }
+
+ public void testAttributeUnquotedValueWithRelaxed() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
+ parser.setInput(new StringReader("<input checked=true></input>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals("input", parser.getName());
+ assertEquals("checked", parser.getAttributeName(0));
+ assertEquals("true", parser.getAttributeValue(0));
+ }
+
+ public void testUnterminatedEntityWithRelaxed() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
+ parser.setInput(new StringReader("<foo bar='A&W'>mac&cheese</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals("foo", parser.getName());
+ assertEquals("bar", parser.getAttributeName(0));
+ assertEquals("A&W", parser.getAttributeValue(0));
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals("mac&cheese", parser.getText());
+ }
+
+ public void testEntitiesAndNamespaces() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setFeature("http://xmlpull.org/v1/doc/features.html#process-namespaces", true);
+ parser.setInput(new StringReader(
+ "<foo:a xmlns:foo='http://foo' xmlns:bar='http://bar'><bar:b/></foo:a>"));
+ testNamespace(parser);
+ }
+
+ public void testEntitiesAndNamespacesWithRelaxed() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setFeature("http://xmlpull.org/v1/doc/features.html#process-namespaces", true);
+ parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
+ parser.setInput(new StringReader(
+ "<foo:a xmlns:foo='http://foo' xmlns:bar='http://bar'><bar:b/></foo:a>"));
+ testNamespace(parser);
+ }
+
+ private void testNamespace(XmlPullParser parser) throws XmlPullParserException, IOException {
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals("http://foo", parser.getNamespace());
+ assertEquals("a", parser.getName());
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals("http://bar", parser.getNamespace());
+ assertEquals("b", parser.getName());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ assertEquals("http://bar", parser.getNamespace());
+ assertEquals("b", parser.getName());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ assertEquals("http://foo", parser.getNamespace());
+ assertEquals("a", parser.getName());
+ }
+
+ public void testRegularNumericEntities() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>&#65;</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.ENTITY_REF, parser.nextToken());
+ assertEquals("#65", parser.getName());
+ assertEquals("A", parser.getText());
+ }
+
+ public void testNumericEntitiesLargerThanChar() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(
+ "<foo>&#2147483647; &#-2147483648;</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertNextFails(parser);
+ }
+
+ public void testNumericEntitiesLargerThanInt() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(
+ "<foo>&#2147483648;</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertNextFails(parser);
+ }
+
+ public void testCharacterReferenceOfHexUtf16Surrogates() throws Exception {
+ testCharacterReferenceOfUtf16Surrogates("<foo>&#x10000; &#x10381; &#x10FFF0;</foo>");
+ }
+
+ public void testCharacterReferenceOfDecimalUtf16Surrogates() throws Exception {
+ testCharacterReferenceOfUtf16Surrogates("<foo>&#65536; &#66433; &#1114096;</foo>");
+ }
+
+ private void testCharacterReferenceOfUtf16Surrogates(String xml) throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(xml));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals(new String(new int[] { 65536, ' ', 66433, ' ', 1114096 }, 0, 5),
+ parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testCharacterReferenceOfLastUtf16Surrogate() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>&#x10FFFF;</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals(new String(new int[] { 0x10FFFF }, 0, 1), parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testOmittedNumericEntities() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>&#;</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertNextFails(parser);
+ }
+
+ /**
+ * Carriage returns followed by line feeds are silently discarded.
+ */
+ public void testCarriageReturnLineFeed() throws Exception {
+ testLineEndings("\r\n<foo\r\na='b\r\nc'\r\n>d\r\ne</foo\r\n>\r\n");
+ }
+
+ /**
+ * Lone carriage returns are treated like newlines.
+ */
+ public void testLoneCarriageReturn() throws Exception {
+ testLineEndings("\r<foo\ra='b\rc'\r>d\re</foo\r>\r");
+ }
+
+ public void testLoneNewLine() throws Exception {
+ testLineEndings("\n<foo\na='b\nc'\n>d\ne</foo\n>\n");
+ }
+
+ private void testLineEndings(String xml) throws XmlPullParserException, IOException {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(xml));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals("foo", parser.getName());
+ assertEquals("b c", parser.getAttributeValue(0));
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals("d\ne", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ assertEquals("foo", parser.getName());
+ assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
+ }
+
+ public void testXmlDeclaration() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(
+ "<?xml version='1.0' encoding='UTF-8' standalone='no'?><foo/>"));
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals("1.0", parser.getProperty(
+ "http://xmlpull.org/v1/doc/properties.html#xmldecl-version"));
+ assertEquals(Boolean.FALSE, parser.getProperty(
+ "http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone"));
+ assertEquals("UTF-8", parser.getInputEncoding());
+ }
+
+ public void testXmlDeclarationExtraAttributes() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(
+ "<?xml version='1.0' encoding='UTF-8' standalone='no' a='b'?><foo/>"));
+ try {
+ parser.nextToken();
+ fail();
+ } catch (XmlPullParserException expected) {
+ }
+ }
+
+ public void testCustomEntitiesUsingNext() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(
+ "<foo a='cd&aaaaaaaaaa;ef'>wx&aaaaaaaaaa;yz</foo>"));
+ parser.defineEntityReplacementText("aaaaaaaaaa", "b");
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals("cdbef", parser.getAttributeValue(0));
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals("wxbyz", parser.getText());
+ }
+
+ public void testCustomEntitiesUsingNextToken() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(
+ "<foo a='cd&aaaaaaaaaa;ef'>wx&aaaaaaaaaa;yz</foo>"));
+ parser.defineEntityReplacementText("aaaaaaaaaa", "b");
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals("cdbef", parser.getAttributeValue(0));
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("wx", parser.getText());
+ assertEquals(XmlPullParser.ENTITY_REF, parser.nextToken());
+ assertEquals("aaaaaaaaaa", parser.getName());
+ assertEquals("b", parser.getText());
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("yz", parser.getText());
+ }
+
+ public void testMissingEntities() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>&aaa;</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertNextFails(parser);
+ }
+
+ public void testMissingEntitiesWithRelaxed() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
+ parser.setInput(new StringReader("<foo>&aaa;</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals(null, parser.getName());
+ assertEquals("&aaa;", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testMissingEntitiesUsingNextToken() throws Exception {
+ XmlPullParser parser = newPullParser();
+ testMissingEntitiesUsingNextToken(parser);
+ }
+
+ public void testMissingEntitiesUsingNextTokenWithRelaxed() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
+ testMissingEntitiesUsingNextToken(parser);
+ }
+
+ private void testMissingEntitiesUsingNextToken(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ parser.setInput(new StringReader("<foo>&aaa;</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals(XmlPullParser.ENTITY_REF, parser.nextToken());
+ assertEquals("aaa", parser.getName());
+ assertEquals(null, parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testEntityInAttributeWithNextToken() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo bar=\"&amp;\"></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals("foo", parser.getName());
+ assertEquals("&", parser.getAttributeValue(null, "bar"));
+ }
+
+ public void testGreaterThanInText() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals(">", parser.getText());
+ }
+
+ public void testGreaterThanInAttribute() throws Exception{
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo a='>'></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(">", parser.getAttributeValue(0));
+ }
+
+ public void testLessThanInText() throws Exception{
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo><</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertNextFails(parser);
+ }
+
+ public void testLessThanInAttribute() throws Exception{
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo a='<'></foo>"));
+ assertNextFails(parser);
+ }
+
+ public void testQuotesInAttribute() throws Exception{
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo a='\"' b=\"'\"></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals("\"", parser.getAttributeValue(0));
+ assertEquals("'", parser.getAttributeValue(1));
+ }
+
+ public void testQuotesInText() throws Exception{
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>\" '</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals("\" '", parser.getText());
+ }
+
+ public void testCdataDelimiterInAttribute() throws Exception{
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo a=']]>'></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals("]]>", parser.getAttributeValue(0));
+ }
+
+ public void testCdataDelimiterInText() throws Exception{
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>]]></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertNextFails(parser);
+ }
+
+ public void testUnexpectedEof() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo><![C"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertNextFails(parser);
+ }
+
+ public void testUnexpectedSequence() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo><![Cdata[bar]]></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertNextFails(parser);
+ }
+
+ public void testThreeDashCommentDelimiter() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo><!--a---></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertNextFails(parser);
+ }
+
+ public void testTwoDashesInComment() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo><!-- -- --></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertNextFails(parser);
+ }
+
+ public void testEmptyComment() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo><!----></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.COMMENT, parser.nextToken());
+ assertEquals("", parser.getText());
+ }
+
+ /**
+ * Close braces require lookaheads because we need to defend against "]]>".
+ */
+ public void testManyCloseBraces() throws Exception{
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>]]]]]]]]]]]]]]]]]]]]]]]</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals("]]]]]]]]]]]]]]]]]]]]]]]", parser.getText());
+ }
+
+ public void testCommentWithNext() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>ab<!-- comment! -->cd</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals("abcd", parser.getText());
+ }
+
+ public void testCommentWithNextToken() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>ab<!-- comment! -->cd</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("ab", parser.getText());
+ assertEquals(XmlPullParser.COMMENT, parser.nextToken());
+ assertEquals(" comment! ", parser.getText());
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("cd", parser.getText());
+ }
+
+ public void testCdataWithNext() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>ab<![CDATA[cdef]]gh&amp;i]]>jk</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals("abcdef]]gh&amp;ijk", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testCdataWithNextToken() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>ab<![CDATA[cdef]]gh&amp;i]]>jk</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("ab", parser.getText());
+ assertEquals(XmlPullParser.CDSECT, parser.nextToken());
+ assertEquals("cdef]]gh&amp;i", parser.getText());
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("jk", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+ }
+
+ public void testEntityLooksLikeCdataClose() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>&#93;&#93;></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals("]]>", parser.getText());
+ }
+
+ public void testDoctypeWithNext() throws Exception {
+ String s = "<!DOCTYPE foo ["
+ + " <!ENTITY bb \"bar baz\">"
+ + " <!NOTATION png SYSTEM \"image/png\">"
+ + "]><foo>a&bb;c</foo>";
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(
+ "<!DOCTYPE foo [<!ENTITY bb \"bar baz\">]><foo>a&bb;c</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals("abar bazc", parser.getText()); // TODO: this fails on gingerbread
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testDoctypeWithNextToken() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(
+ "<!DOCTYPE foo [<!ENTITY bb \"bar baz\">]><foo>a&bb;c</foo>"));
+ assertEquals(XmlPullParser.DOCDECL, parser.nextToken());
+ assertEquals(" foo [<!ENTITY bb \"bar baz\">]", parser.getText());
+ assertNull(parser.getName());
+
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("a", parser.getText());
+ assertEquals(XmlPullParser.ENTITY_REF, parser.nextToken());
+ assertEquals("bb", parser.getName());
+ assertEquals("bar baz", parser.getText()); // TODO: this fails on gingerbread
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("c", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testProcessingInstructionWithNext() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>ab<?cd efg hij?>kl</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals("abkl", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testProcessingInstructionWithNextToken() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>ab<?cd efg hij?>kl</foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("ab", parser.getText());
+ assertEquals(XmlPullParser.PROCESSING_INSTRUCTION, parser.nextToken());
+ assertEquals("cd efg hij", parser.getText());
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("kl", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testWhitespaceWithNextToken() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader(" \n <foo> \n </foo> \n "));
+ assertEquals(XmlPullParser.IGNORABLE_WHITESPACE, parser.nextToken());
+ assertEquals(true, parser.isWhitespace());
+ assertEquals(" \n ", parser.getText());
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals(true, parser.isWhitespace());
+ assertEquals(" \n ", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+ assertEquals(XmlPullParser.IGNORABLE_WHITESPACE, parser.nextToken());
+ assertEquals(true, parser.isWhitespace());
+ assertEquals(" \n ", parser.getText());
+ assertEquals(XmlPullParser.END_DOCUMENT, parser.nextToken());
+ }
+
+ public void testLinesAndColumns() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("\n"
+ + " <foo><bar a='\n"
+ + "' b='cde'></bar\n"
+ + "><!--\n"
+ + "\n"
+ + "--><baz/>fg\n"
+ + "</foo>"));
+ assertEquals("1,1", parser.getLineNumber() + "," + parser.getColumnNumber());
+ assertEquals(XmlPullParser.IGNORABLE_WHITESPACE, parser.nextToken());
+ assertEquals("2,3", parser.getLineNumber() + "," + parser.getColumnNumber());
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals("2,8", parser.getLineNumber() + "," + parser.getColumnNumber());
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals("3,11", parser.getLineNumber() + "," + parser.getColumnNumber());
+ assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+ assertEquals("4,2", parser.getLineNumber() + "," + parser.getColumnNumber());
+ assertEquals(XmlPullParser.COMMENT, parser.nextToken());
+ assertEquals("6,4", parser.getLineNumber() + "," + parser.getColumnNumber());
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals("6,10", parser.getLineNumber() + "," + parser.getColumnNumber());
+ assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+ assertEquals("6,10", parser.getLineNumber() + "," + parser.getColumnNumber());
+ assertEquals(XmlPullParser.TEXT, parser.nextToken());
+ assertEquals("7,1", parser.getLineNumber() + "," + parser.getColumnNumber());
+ assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+ assertEquals("7,7", parser.getLineNumber() + "," + parser.getColumnNumber());
+ assertEquals(XmlPullParser.END_DOCUMENT, parser.nextToken());
+ assertEquals("7,7", parser.getLineNumber() + "," + parser.getColumnNumber());
+ }
+
+ public void testEmptyEntityReferenceWithNext() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>&empty;</foo>"));
+ parser.defineEntityReplacementText("empty", "");
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testEmptyEntityReferenceWithNextToken() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo>&empty;</foo>"));
+ parser.defineEntityReplacementText("empty", "");
+ assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+ assertEquals(XmlPullParser.ENTITY_REF, parser.nextToken());
+ assertEquals("empty", parser.getName());
+ assertEquals("", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+ }
+
+ public void testEmptyCdataWithNext() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo><![CDATA[]]></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testEmptyCdataWithNextToken() throws Exception {
+ XmlPullParser parser = newPullParser();
+ parser.setInput(new StringReader("<foo><![CDATA[]]></foo>"));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(XmlPullParser.CDSECT, parser.nextToken());
+ assertEquals("", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ }
+
+ public void testParseReader() throws Exception {
+ String snippet = "<dagny dad=\"bob\">hello</dagny>";
+ XmlPullParser parser = newPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+ parser.setInput(new StringReader(snippet));
+ validate(parser);
+ }
+
+ public void testParseInputStream() throws Exception {
+ String snippet = "<dagny dad=\"bob\">hello</dagny>";
+ XmlPullParser parser = newPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+ parser.setInput(new ByteArrayInputStream(snippet.getBytes()), "UTF-8");
+ validate(parser);
+ }
+
+ static void validate(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ assertEquals(XmlPullParser.START_DOCUMENT, parser.getEventType());
+ assertEquals(0, parser.getDepth());
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(1, parser.getDepth());
+ assertEquals("dagny", parser.getName());
+ assertEquals(1, parser.getAttributeCount());
+ assertEquals("dad", parser.getAttributeName(0));
+ assertEquals("bob", parser.getAttributeValue(0));
+ assertEquals("bob", parser.getAttributeValue(null, "dad"));
+ assertEquals(XmlPullParser.TEXT, parser.next());
+ assertEquals(1, parser.getDepth());
+ assertEquals("hello", parser.getText());
+ assertEquals(XmlPullParser.END_TAG, parser.next());
+ assertEquals(1, parser.getDepth());
+ assertEquals("dagny", parser.getName());
+ assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
+ assertEquals(0, parser.getDepth());
+ }
+
+ public void testNamespaces() throws Exception {
+ String xml = "<one xmlns='ns:default' xmlns:n1='ns:1' a='b'>\n"
+ + " <n1:two c='d' n1:e='f' xmlns:n2='ns:2'>text</n1:two>\n"
+ + "</one>";
+
+ XmlPullParser parser = newPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(new StringReader(xml));
+
+ assertEquals(0, parser.getDepth());
+ assertEquals(0, parser.getNamespaceCount(0));
+
+ try {
+ parser.getNamespaceCount(1);
+ fail();
+ } catch (IndexOutOfBoundsException e) { /* expected */ }
+
+ // one
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(1, parser.getDepth());
+
+ checkNamespacesInOne(parser);
+
+ // n1:two
+ assertEquals(XmlPullParser.START_TAG, parser.nextTag());
+
+ assertEquals(2, parser.getDepth());
+ checkNamespacesInTwo(parser);
+
+ // Body of two.
+ assertEquals(XmlPullParser.TEXT, parser.next());
+
+ // End of two.
+ assertEquals(XmlPullParser.END_TAG, parser.nextTag());
+
+ // Depth should still be 2.
+ assertEquals(2, parser.getDepth());
+
+ // We should still be able to see the namespaces from two.
+ checkNamespacesInTwo(parser);
+
+ // End of one.
+ assertEquals(XmlPullParser.END_TAG, parser.nextTag());
+
+ // Depth should be back to 1.
+ assertEquals(1, parser.getDepth());
+
+ // We can still see the namespaces in one.
+ checkNamespacesInOne(parser);
+
+ // We shouldn't be able to see the namespaces in two anymore.
+ try {
+ parser.getNamespaceCount(2);
+ fail();
+ } catch (IndexOutOfBoundsException e) { /* expected */ }
+
+ assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
+
+ // We shouldn't be able to see the namespaces in one anymore.
+ try {
+ parser.getNamespaceCount(1);
+ fail();
+ } catch (IndexOutOfBoundsException e) { /* expected */ }
+
+ assertEquals(0, parser.getNamespaceCount(0));
+ }
+
+ private void checkNamespacesInOne(XmlPullParser parser) throws XmlPullParserException {
+ assertEquals(2, parser.getNamespaceCount(1));
+
+ // Prefix for default namespace is null.
+ assertNull(parser.getNamespacePrefix(0));
+ assertEquals("ns:default", parser.getNamespaceUri(0));
+
+ assertEquals("n1", parser.getNamespacePrefix(1));
+ assertEquals("ns:1", parser.getNamespaceUri(1));
+
+ assertEquals("ns:default", parser.getNamespace(null));
+
+ // KXML returns null.
+ // assertEquals("ns:default", parser.getNamespace(""));
+ }
+
+ private void checkNamespacesInTwo(XmlPullParser parser) throws XmlPullParserException {
+ // These should still be valid.
+ checkNamespacesInOne(parser);
+
+ assertEquals(3, parser.getNamespaceCount(2));
+
+ // Default ns should still be in the stack
+ assertNull(parser.getNamespacePrefix(0));
+ assertEquals("ns:default", parser.getNamespaceUri(0));
+ }
+
+ private void assertNextFails(XmlPullParser parser) throws IOException {
+ try {
+ parser.next();
+ fail();
+ } catch (XmlPullParserException expected) {
+ }
+ }
+
+ /**
+ * Creates a new pull parser with namespace support.
+ */
+ abstract XmlPullParser newPullParser();
+}
diff --git a/luni/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java b/luni/src/test/java/libcore/xml/XsltXPathConformanceTestSuite.java
index 8623d58..7bf1980 100644
--- a/luni/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java
+++ b/luni/src/test/java/libcore/xml/XsltXPathConformanceTestSuite.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.apache.harmony.xml;
+package libcore.xml;
import junit.framework.Assert;
import junit.framework.AssertionFailedError;
diff --git a/luni/src/test/java/org/apache/harmony/xml/PullParserTest.java b/luni/src/test/java/org/apache/harmony/xml/PullParserTest.java
deleted file mode 100644
index d6d5370..0000000
--- a/luni/src/test/java/org/apache/harmony/xml/PullParserTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed 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.xml;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.StringReader;
-import junit.framework.TestCase;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-public abstract class PullParserTest extends TestCase {
- private static final String SNIPPET = "<dagny dad=\"bob\">hello</dagny>";
-
- public void testPullParser() {
- try {
- XmlPullParser parser = newPullParser();
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
-
- // Test reader.
- parser.setInput(new StringReader(SNIPPET));
- validate(parser);
-
- // Test input stream.
- parser.setInput(new ByteArrayInputStream(SNIPPET.getBytes()),
- "UTF-8");
- validate(parser);
- } catch (XmlPullParserException e) {
- throw new RuntimeException(e);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- static void validate(XmlPullParser parser)
- throws XmlPullParserException, IOException {
- assertEquals(XmlPullParser.START_DOCUMENT, parser.getEventType());
-
- assertEquals(0, parser.getDepth());
-
- assertEquals(XmlPullParser.START_TAG, parser.next());
-
- assertEquals(1, parser.getDepth());
-
- assertEquals("dagny", parser.getName());
- assertEquals(1, parser.getAttributeCount());
- assertEquals("dad", parser.getAttributeName(0));
- assertEquals("bob", parser.getAttributeValue(0));
- assertEquals("bob", parser.getAttributeValue(null, "dad"));
-
- assertEquals(XmlPullParser.TEXT, parser.next());
-
- assertEquals(1, parser.getDepth());
-
- assertEquals("hello", parser.getText());
-
- assertEquals(XmlPullParser.END_TAG, parser.next());
-
- assertEquals(1, parser.getDepth());
-
- assertEquals("dagny", parser.getName());
-
- assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
-
- assertEquals(0, parser.getDepth());
- }
-
- static final String XML =
- "<one xmlns='ns:default' xmlns:n1='ns:1' a='b'>\n"
- + " <n1:two c='d' n1:e='f' xmlns:n2='ns:2'>text</n1:two>\n"
- + "</one>";
-
- public void testExpatPullParserNamespaces() throws Exception {
- XmlPullParser parser = newPullParser();
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new StringReader(XML));
-
- assertEquals(0, parser.getDepth());
- assertEquals(0, parser.getNamespaceCount(0));
-
- try {
- parser.getNamespaceCount(1);
- fail();
- } catch (IndexOutOfBoundsException e) { /* expected */ }
-
- // one
- assertEquals(XmlPullParser.START_TAG, parser.next());
- assertEquals(1, parser.getDepth());
-
- checkNamespacesInOne(parser);
-
- // n1:two
- assertEquals(XmlPullParser.START_TAG, parser.nextTag());
-
- assertEquals(2, parser.getDepth());
- checkNamespacesInTwo(parser);
-
- // Body of two.
- assertEquals(XmlPullParser.TEXT, parser.next());
-
- // End of two.
- assertEquals(XmlPullParser.END_TAG, parser.nextTag());
-
- // Depth should still be 2.
- assertEquals(2, parser.getDepth());
-
- // We should still be able to see the namespaces from two.
- checkNamespacesInTwo(parser);
-
- // End of one.
- assertEquals(XmlPullParser.END_TAG, parser.nextTag());
-
- // Depth should be back to 1.
- assertEquals(1, parser.getDepth());
-
- // We can still see the namespaces in one.
- checkNamespacesInOne(parser);
-
- // We shouldn't be able to see the namespaces in two anymore.
- try {
- parser.getNamespaceCount(2);
- fail();
- } catch (IndexOutOfBoundsException e) { /* expected */ }
-
- assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
-
- // We shouldn't be able to see the namespaces in one anymore.
- try {
- parser.getNamespaceCount(1);
- fail();
- } catch (IndexOutOfBoundsException e) { /* expected */ }
-
- assertEquals(0, parser.getNamespaceCount(0));
- }
-
- private void checkNamespacesInOne(XmlPullParser parser) throws XmlPullParserException {
- assertEquals(2, parser.getNamespaceCount(1));
-
- // Prefix for default namespace is null.
- assertNull(parser.getNamespacePrefix(0));
- assertEquals("ns:default", parser.getNamespaceUri(0));
-
- assertEquals("n1", parser.getNamespacePrefix(1));
- assertEquals("ns:1", parser.getNamespaceUri(1));
-
- assertEquals("ns:default", parser.getNamespace(null));
-
- // KXML returns null.
- // assertEquals("ns:default", parser.getNamespace(""));
- }
-
- private void checkNamespacesInTwo(XmlPullParser parser) throws XmlPullParserException {
- // These should still be valid.
- checkNamespacesInOne(parser);
-
- assertEquals(3, parser.getNamespaceCount(2));
-
- // Default ns should still be in the stack
- assertNull(parser.getNamespacePrefix(0));
- assertEquals("ns:default", parser.getNamespaceUri(0));
- }
-
- /**
- * Creates a new pull parser with namespace support.
- */
- abstract XmlPullParser newPullParser();
-}
diff --git a/support/src/test/java/tests/net/StuckServer.java b/support/src/test/java/tests/net/StuckServer.java
new file mode 100644
index 0000000..4230f17
--- /dev/null
+++ b/support/src/test/java/tests/net/StuckServer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 tests.net;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+
+/**
+ * A test ServerSocket that you can't connect to --- connects will time out.
+ */
+public final class StuckServer {
+ private ServerSocket serverSocket;
+ private ArrayList<Socket> clients = new ArrayList<Socket>();
+
+ public StuckServer() throws IOException {
+ // Set a backlog and use it up so that we can expect the
+ // connection to time out. According to Steven's
+ // 4.5 "listen function", Linux adds 3 to the specified
+ // backlog, so we need to connect 4 times before it will hang.
+ serverSocket = new ServerSocket(0, 1);
+ for (int i = 0; i < 4; i++) {
+ clients.add(new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()));
+ }
+ }
+
+ public InetSocketAddress getLocalSocketAddress() {
+ return (InetSocketAddress) serverSocket.getLocalSocketAddress();
+ }
+
+ public int getLocalPort() {
+ return serverSocket.getLocalPort();
+ }
+
+ public void close() throws IOException {
+ serverSocket.close();
+ for (Socket client : clients) {
+ client.close();
+ }
+ }
+}
diff --git a/support/src/test/java/tests/resources/GZIPInputStream/hyts_gInput.txt.gz b/support/src/test/java/tests/resources/GZIPInputStream/hyts_gInput.txt.gz
deleted file mode 100644
index e0f5a00..0000000
--- a/support/src/test/java/tests/resources/GZIPInputStream/hyts_gInput.txt.gz
+++ /dev/null
Binary files differ
diff --git a/xml/src/main/java/org/kxml2/io/KXmlParser.java b/xml/src/main/java/org/kxml2/io/KXmlParser.java
index dd73f0b..4f41fe0 100644
--- a/xml/src/main/java/org/kxml2/io/KXmlParser.java
+++ b/xml/src/main/java/org/kxml2/io/KXmlParser.java
@@ -22,31 +22,45 @@
package org.kxml2.io;
-import java.io.*;
-import java.util.*;
-
-import org.xmlpull.v1.*;
-
-/** A simple, pull based XML parser. This classe replaces the kXML 1
- XmlParser class and the corresponding event classes. */
-
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.Map;
+import libcore.internal.StringPool;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * A pull based XML parser.
+ */
public class KXmlParser implements XmlPullParser {
- private Object location;
+ private static final char[] START_COMMENT = { '<', '!', '-', '-' };
+ private static final char[] END_COMMENT = { '-', '-', '>' };
+ private static final char[] COMMENT_DOUBLE_DASH = { '-', '-' };
+ private static final char[] START_CDATA = { '<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[' };
+ private static final char[] END_CDATA = { ']', ']', '>' };
+ private static final char[] START_PROCESSING_INSTRUCTION = { '<', '?' };
+ private static final char[] END_PROCESSING_INSTRUCTION = { '?', '>' };
+ private static final char[] START_DOCTYPE = { '<', '!', 'D', 'O', 'C', 'T', 'Y', 'P', 'E' };
+ // no END_DOCTYPE because doctype must be parsed
+
static final private String UNEXPECTED_EOF = "Unexpected EOF";
static final private String ILLEGAL_TYPE = "Wrong event type";
- static final private int LEGACY = 999;
- static final private int XML_DECL = 998;
+ static final private int XML_DECLARATION = 998;
// general
+ private String location;
private String version;
private Boolean standalone;
private boolean processNsp;
private boolean relaxed;
- private boolean keepNamespaceAttributes; // android-added
- private Hashtable entityMap;
+ private boolean keepNamespaceAttributes;
+ private Map<String, String> entityMap;
private int depth;
private String[] elementStack = new String[16];
private String[] nspStack = new String[8];
@@ -56,33 +70,31 @@ public class KXmlParser implements XmlPullParser {
private Reader reader;
private String encoding;
- private char[] srcBuf;
-
- private int srcPos;
- private int srcCount;
-
- private int line;
- private int column;
-
- // txtbuffer
+ private final char[] buffer = new char[8192];
+ private int position = 0;
+ private int limit = 0;
- /** Target buffer for storing incoming text (including aggregated resolved entities) */
- private char[] txtBuf = new char[128];
- /** Write position */
- private int txtPos;
+ /*
+ * Track the number of newlines and columns preceding the current buffer. To
+ * compute the line and column of a position in the buffer, compute the line
+ * and column in the buffer and add the preceding values.
+ */
+ private int bufferStartLine;
+ private int bufferStartColumn;
- // Event-related
+ // the current token
private int type;
private boolean isWhitespace;
private String namespace;
private String prefix;
private String name;
+ private String text;
private boolean degenerated;
private int attributeCount;
- /**
+ /*
* The current element's attributes arranged in groups of 4:
* i + 0 = attribute namespace URI
* i + 1 = attribute namespace prefix
@@ -90,54 +102,38 @@ public class KXmlParser implements XmlPullParser {
* i + 3 = attribute value
*/
private String[] attributes = new String[16];
-// private int stackMismatch = 0;
- private String error;
-
- /**
- * A separate peek buffer seems simpler than managing
- * wrap around in the first level read buffer */
- private int[] peek = new int[2];
- private int peekCount;
- private boolean wasCR;
+ private String error;
private boolean unresolved;
- private boolean token;
- public KXmlParser() {
- srcBuf =
- new char[Runtime.getRuntime().freeMemory() >= 1048576 ? 8192 : 128];
- }
+ public final StringPool stringPool = new StringPool();
- // BEGIN android-added
/**
- * Retains namespace attributes like {@code xmlns="http://foo"} or {@code
- * xmlns:foo="http:foo"} in pulled elements. Most applications will only be
- * interested in the effective namespaces of their elements, so these
- * attributes aren't useful. But for structure preserving wrappers like DOM,
- * it is necessary to keep the namespace data around.
+ * Retains namespace attributes like {@code xmlns="http://foo"} or {@code xmlns:foo="http:foo"}
+ * in pulled elements. Most applications will only be interested in the effective namespaces of
+ * their elements, so these attributes aren't useful. But for structure preserving wrappers like
+ * DOM, it is necessary to keep the namespace data around.
*/
public void keepNamespaceAttributes() {
this.keepNamespaceAttributes = true;
}
- // END android-added
- private final boolean isProp(String n1, boolean prop, String n2) {
- if (!n1.startsWith("http://xmlpull.org/v1/doc/"))
+ private boolean isProp(String n1, boolean prop, String n2) {
+ if (!n1.startsWith("http://xmlpull.org/v1/doc/")) {
return false;
- if (prop)
+ }
+ if (prop) {
return n1.substring(42).equals(n2);
- else
+ } else {
return n1.substring(40).equals(n2);
+ }
}
- private final boolean adjustNsp() throws XmlPullParserException {
-
+ private boolean adjustNsp() throws XmlPullParserException {
boolean any = false;
for (int i = 0; i < attributeCount << 2; i += 4) {
- // * 4 - 4; i >= 0; i -= 4) {
-
String attrName = attributes[i + 2];
int cut = attrName.indexOf(':');
String prefix;
@@ -145,32 +141,26 @@ public class KXmlParser implements XmlPullParser {
if (cut != -1) {
prefix = attrName.substring(0, cut);
attrName = attrName.substring(cut + 1);
- }
- else if (attrName.equals("xmlns")) {
+ } else if (attrName.equals("xmlns")) {
prefix = attrName;
attrName = null;
- }
- else
+ } else {
continue;
+ }
if (!prefix.equals("xmlns")) {
any = true;
- }
- else {
+ } else {
int j = (nspCounts[depth]++) << 1;
nspStack = ensureCapacity(nspStack, j + 2);
nspStack[j] = attrName;
nspStack[j + 1] = attributes[i + 3];
- if (attrName != null && attributes[i + 3].isEmpty())
- error("illegal empty namespace");
-
- // prefixMap = new PrefixMap (prefixMap, attrName, attr.getValue ());
-
- //System.out.println (prefixMap);
+ if (attrName != null && attributes[i + 3].isEmpty()) {
+ checkRelaxed("illegal empty namespace");
+ }
- // BEGIN android-changed
if (keepNamespaceAttributes) {
// explicitly set the namespace for unprefixed attributes
// such as xmlns="http://foo"
@@ -186,7 +176,6 @@ public class KXmlParser implements XmlPullParser {
i -= 4;
}
- // END android-changed
}
}
@@ -196,45 +185,33 @@ public class KXmlParser implements XmlPullParser {
String attrName = attributes[i + 2];
int cut = attrName.indexOf(':');
- if (cut == 0 && !relaxed)
+ if (cut == 0 && !relaxed) {
throw new RuntimeException(
- "illegal attribute name: " + attrName + " at " + this);
-
- else if (cut != -1) {
+ "illegal attribute name: " + attrName + " at " + this);
+ } else if (cut != -1) {
String attrPrefix = attrName.substring(0, cut);
attrName = attrName.substring(cut + 1);
String attrNs = getNamespace(attrPrefix);
- if (attrNs == null && !relaxed)
+ if (attrNs == null && !relaxed) {
throw new RuntimeException(
- "Undefined Prefix: " + attrPrefix + " in " + this);
+ "Undefined Prefix: " + attrPrefix + " in " + this);
+ }
attributes[i] = attrNs;
attributes[i + 1] = attrPrefix;
attributes[i + 2] = attrName;
-
- /*
- if (!relaxed) {
- for (int j = (attributeCount << 2) - 4; j > i; j -= 4)
- if (attrName.equals(attributes[j + 2])
- && attrNs.equals(attributes[j]))
- exception(
- "Duplicate Attribute: {"
- + attrNs
- + "}"
- + attrName);
- }
- */
}
}
}
int cut = name.indexOf(':');
- if (cut == 0)
- error("illegal tag name: " + name);
+ if (cut == 0) {
+ checkRelaxed("illegal tag name: " + name);
+ }
if (cut != -1) {
prefix = name.substring(0, cut);
@@ -244,488 +221,494 @@ public class KXmlParser implements XmlPullParser {
this.namespace = getNamespace(prefix);
if (this.namespace == null) {
- if (prefix != null)
- error("undefined prefix: " + prefix);
+ if (prefix != null) {
+ checkRelaxed("undefined prefix: " + prefix);
+ }
this.namespace = NO_NAMESPACE;
}
return any;
}
- private final String[] ensureCapacity(String[] arr, int required) {
- if (arr.length >= required)
+ private String[] ensureCapacity(String[] arr, int required) {
+ if (arr.length >= required) {
return arr;
+ }
String[] bigger = new String[required + 16];
System.arraycopy(arr, 0, bigger, 0, arr.length);
return bigger;
}
- private final void error(String desc) throws XmlPullParserException {
- if (relaxed) {
- if (error == null)
- error = "ERR: " + desc;
+ private void checkRelaxed(String errorMessage) throws XmlPullParserException {
+ if (!relaxed) {
+ throw new XmlPullParserException(errorMessage, this, null);
+ }
+ if (error == null) {
+ error = "Error: " + errorMessage;
}
- else
- exception(desc);
}
- private final void exception(String desc) throws XmlPullParserException {
- throw new XmlPullParserException(
- desc.length() < 100 ? desc : desc.substring(0, 100) + "\n",
- this,
- null);
+ public int next() throws XmlPullParserException, IOException {
+ return next(false);
}
- /**
- * common base for next and nextToken. Clears the state, except from
- * txtPos and whitespace. Does not set the type variable */
-
- private final void nextImpl() throws IOException, XmlPullParserException {
+ public int nextToken() throws XmlPullParserException, IOException {
+ return next(true);
+ }
- if (reader == null)
- exception("No Input specified");
+ private int next(boolean justOneToken) throws IOException, XmlPullParserException {
+ if (reader == null) {
+ throw new XmlPullParserException("setInput() must be called first.", this, null);
+ }
- if (type == END_TAG)
+ if (type == END_TAG) {
depth--;
+ }
- while (true) {
- attributeCount = -1;
-
- // degenerated needs to be handled before error because of possible
- // processor expectations(!)
-
- if (degenerated) {
- degenerated = false;
- type = END_TAG;
- return;
- }
+ // degenerated needs to be handled before error because of possible
+ // processor expectations(!)
+ if (degenerated) {
+ degenerated = false;
+ type = END_TAG;
+ return type;
+ }
- if (error != null) {
- for (int i = 0; i < error.length(); i++)
- push(error.charAt(i));
- //text = error;
- error = null;
+ if (error != null) {
+ if (justOneToken) {
+ text = error;
type = COMMENT;
- return;
+ error = null;
+ return type;
+ } else {
+ error = null;
}
+ }
+ type = peekType();
-// if (relaxed
-// && (stackMismatch > 0 || (peek(0) == -1 && depth > 0))) {
-// int sp = (depth - 1) << 2;
-// type = END_TAG;
-// namespace = elementStack[sp];
-// prefix = elementStack[sp + 1];
-// name = elementStack[sp + 2];
-// if (stackMismatch != 1)
-// error = "missing end tag /" + name + " inserted";
-// if (stackMismatch > 0)
-// stackMismatch--;
-// return;
-// }
-
- prefix = null;
- name = null;
- namespace = null;
- // text = null;
-
+ if (type == XML_DECLARATION) {
+ readXmlDeclaration();
type = peekType();
+ }
- switch (type) {
+ text = null;
+ isWhitespace = true;
+ prefix = null;
+ name = null;
+ namespace = null;
+ attributeCount = -1;
- case ENTITY_REF :
- pushEntity();
- return;
+ while (true) {
+ switch (type) {
- case START_TAG :
- parseStartTag(false);
- return;
+ /*
+ * Return immediately after encountering a start tag, end tag, or
+ * the end of the document.
+ */
+ case START_TAG:
+ parseStartTag(false);
+ return type;
+ case END_TAG:
+ readEndTag();
+ return type;
+ case END_DOCUMENT:
+ return type;
+
+ /*
+ * Return after any text token when we're looking for a single
+ * token. Otherwise concatenate all text between tags.
+ */
+ case ENTITY_REF:
+ if (justOneToken) {
+ StringBuilder entityTextBuilder = new StringBuilder();
+ readEntity(entityTextBuilder, true);
+ text = entityTextBuilder.toString();
+ break;
+ }
+ // fall-through
+ case TEXT:
+ text = readValue('<', !justOneToken, false);
+ if (depth == 0 && isWhitespace) {
+ type = IGNORABLE_WHITESPACE;
+ }
+ break;
+ case CDSECT:
+ read(START_CDATA);
+ text = readUntil(END_CDATA, true);
+ break;
- case END_TAG :
- parseEndTag();
- return;
+ /*
+ * Comments, processing instructions and declarations are returned
+ * when we're looking for a single token. Otherwise they're skipped.
+ */
+ case COMMENT:
+ String commentText = readComment(justOneToken);
+ if (justOneToken) {
+ text = commentText;
+ }
+ break;
+ case PROCESSING_INSTRUCTION:
+ read(START_PROCESSING_INSTRUCTION);
+ String processingInstruction = readUntil(END_PROCESSING_INSTRUCTION, justOneToken);
+ if (justOneToken) {
+ text = processingInstruction;
+ }
+ break;
+ case DOCDECL:
+ String doctype = readDoctype(justOneToken);
+ if (justOneToken) {
+ text = doctype;
+ }
+ break;
+ }
- case END_DOCUMENT :
- return;
+ if (justOneToken) {
+ return type;
+ }
- case TEXT :
- // BEGIN android-changed: distinguish attribute values from normal text.
- pushText('<', !token, false);
- // END android-changed
- if (depth == 0) {
- if (isWhitespace)
- type = IGNORABLE_WHITESPACE;
- // make exception switchable for instances.chg... !!!!
- // else
- // exception ("text '"+getText ()+"' not allowed outside root element");
- }
- return;
+ if (type == IGNORABLE_WHITESPACE) {
+ text = null;
+ }
- default :
- type = parseLegacy(token);
- if (type != XML_DECL)
- return;
+ /*
+ * We've read all that we can of a non-empty text block. Always
+ * report this as text, even if it was a CDATA block or entity
+ * reference.
+ */
+ int peek = peekType();
+ if (text != null && !text.isEmpty() && peek < TEXT) {
+ type = TEXT;
+ return type;
}
+
+ type = peek;
}
}
- private final int parseLegacy(boolean push)
- throws IOException, XmlPullParserException {
-
- String req = "";
- int term;
- int result;
- int prev = 0;
-
- read(); // <
- int c = read();
-
- if (c == '?') {
- if ((peek(0) == 'x' || peek(0) == 'X')
- && (peek(1) == 'm' || peek(1) == 'M')) {
+ /**
+ * Reads text until the specified delimiter is encountered. Consumes the
+ * text and the delimiter.
+ *
+ * @param returnText true to return the read text excluding the delimiter;
+ * false to return null.
+ */
+ private String readUntil(char[] delimiter, boolean returnText)
+ throws IOException, XmlPullParserException {
+ int start = position;
+ StringBuilder result = null;
+
+ if (returnText && text != null) {
+ result = new StringBuilder();
+ result.append(text);
+ }
- if (push) {
- push(peek(0));
- push(peek(1));
+ search:
+ while (true) {
+ if (position + delimiter.length >= limit) {
+ if (start < position && returnText) {
+ if (result == null) {
+ result = new StringBuilder();
+ }
+ result.append(buffer, start, position - start);
}
- read();
- read();
-
- if ((peek(0) == 'l' || peek(0) == 'L') && peek(1) <= ' ') {
-
- if (line != 1 || column > 4)
- error("PI must not start with xml");
-
- parseStartTag(true);
+ if (!fillBuffer(delimiter.length)) {
+ checkRelaxed(UNEXPECTED_EOF);
+ type = COMMENT;
+ return null;
+ }
+ start = position;
+ }
- if (attributeCount < 1 || !"version".equals(attributes[2]))
- error("version expected");
+ // TODO: replace with Arrays.equals(buffer, position, delimiter, 0, delimiter.length)
+ // when the VM has better method inlining
+ for (int i = 0; i < delimiter.length; i++) {
+ if (buffer[position + i] != delimiter[i]) {
+ position++;
+ continue search;
+ }
+ }
- version = attributes[3];
+ break;
+ }
- int pos = 1;
+ int end = position;
+ position += delimiter.length;
- if (pos < attributeCount
- && "encoding".equals(attributes[2 + 4])) {
- encoding = attributes[3 + 4];
- pos++;
- }
+ if (!returnText) {
+ return null;
+ } else if (result == null) {
+ return stringPool.get(buffer, start, end - start);
+ } else {
+ result.append(buffer, start, end - start);
+ return result.toString();
+ }
+ }
- if (pos < attributeCount
- && "standalone".equals(attributes[4 * pos + 2])) {
- String st = attributes[3 + 4 * pos];
- if ("yes".equals(st))
- standalone = new Boolean(true);
- else if ("no".equals(st))
- standalone = new Boolean(false);
- else
- error("illegal standalone value: " + st);
- pos++;
- }
+ /**
+ * Returns true if an XML declaration was read.
+ */
+ private void readXmlDeclaration() throws IOException, XmlPullParserException {
+ if (bufferStartLine != 0 || bufferStartColumn != 0 || position != 0) {
+ checkRelaxed("processing instructions must not start with xml");
+ }
- if (pos != attributeCount)
- error("illegal xmldecl");
+ read(START_PROCESSING_INSTRUCTION);
+ parseStartTag(true);
- isWhitespace = true;
- txtPos = 0;
+ if (attributeCount < 1 || !"version".equals(attributes[2])) {
+ checkRelaxed("version expected");
+ }
- return XML_DECL;
- }
- }
+ version = attributes[3];
- /* int c0 = read ();
- int c1 = read ();
- int */
+ int pos = 1;
- term = '?';
- result = PROCESSING_INSTRUCTION;
+ if (pos < attributeCount && "encoding".equals(attributes[2 + 4])) {
+ encoding = attributes[3 + 4];
+ pos++;
}
- else if (c == '!') {
- if (peek(0) == '-') {
- result = COMMENT;
- req = "--";
- term = '-';
- }
- else if (peek(0) == '[') {
- result = CDSECT;
- req = "[CDATA[";
- term = ']';
- push = true;
- }
- else {
- result = DOCDECL;
- req = "DOCTYPE";
- term = -1;
+
+ if (pos < attributeCount && "standalone".equals(attributes[4 * pos + 2])) {
+ String st = attributes[3 + 4 * pos];
+ if ("yes".equals(st)) {
+ standalone = Boolean.TRUE;
+ } else if ("no".equals(st)) {
+ standalone = Boolean.FALSE;
+ } else {
+ checkRelaxed("illegal standalone value: " + st);
}
+ pos++;
}
- else {
- error("illegal: <" + c);
- return COMMENT;
- }
-
- for (int i = 0; i < req.length(); i++)
- read(req.charAt(i));
-
- if (result == DOCDECL)
- parseDoctype(push);
- else {
- while (true) {
- c = read();
- if (c == -1){
- error(UNEXPECTED_EOF);
- return COMMENT;
- }
- if (push)
- push(c);
-
- if ((term == '?' || c == term)
- && peek(0) == term
- && peek(1) == '>')
- break;
-
- prev = c;
- }
+ if (pos != attributeCount) {
+ checkRelaxed("unexpected attributes in XML declaration");
+ }
- if (term == '-' && prev == '-' && !relaxed)
- error("illegal comment delimiter: --->");
+ isWhitespace = true;
+ text = null;
+ }
- read();
- read();
+ private String readComment(boolean returnText) throws IOException, XmlPullParserException {
+ read(START_COMMENT);
- if (push && term != '?')
- txtPos--;
+ if (relaxed) {
+ return readUntil(END_COMMENT, returnText);
+ }
+ String commentText = readUntil(COMMENT_DOUBLE_DASH, returnText);
+ if (peekCharacter() != '>') {
+ throw new XmlPullParserException("Comments may not contain --", this, null);
}
- return result;
+ position++;
+ return commentText;
}
- /** precondition: &lt! consumed */
-
- private final void parseDoctype(boolean push)
- throws IOException, XmlPullParserException {
+ private String readDoctype(boolean returnText) throws IOException, XmlPullParserException {
+ read(START_DOCTYPE);
+ int start = position;
+ StringBuilder result = null;
int nesting = 1;
boolean quoted = false;
- // read();
-
while (true) {
- int i = read();
- switch (i) {
-
- case -1 :
- error(UNEXPECTED_EOF);
- return;
-
- case '\'' :
- quoted = !quoted;
- break;
+ if (position >= limit) {
+ if (start < position && returnText) {
+ if (result == null) {
+ result = new StringBuilder();
+ }
+ result.append(buffer, start, position - start);
+ }
+ if (!fillBuffer(1)) {
+ checkRelaxed(UNEXPECTED_EOF);
+ return null;
+ }
+ start = position;
+ }
- case '<' :
- if (!quoted)
- nesting++;
- break;
+ char i = buffer[position++];
- case '>' :
- if (!quoted) {
- if ((--nesting) == 0)
- return;
- }
+ if (i == '\'') {
+ quoted = !quoted; // TODO: should this include a double quote as well?
+ } else if (i == '<') {
+ if (!quoted) {
+ nesting++;
+ }
+ } else if (i == '>') {
+ if (!quoted && --nesting == 0) {
break;
+ }
}
- if (push)
- push(i);
}
- }
- /* precondition: &lt;/ consumed */
-
- private final void parseEndTag()
- throws IOException, XmlPullParserException {
+ if (!returnText) {
+ return null;
+ } else if (result == null) {
+ return stringPool.get(buffer, start, position - start - 1); // omit the '>'
+ } else {
+ result.append(buffer, start, position - start - 1); // omit the '>'
+ return result.toString();
+ }
+ }
- read(); // '<'
- read(); // '/'
- name = readName();
+ private void readEndTag() throws IOException, XmlPullParserException {
+ read('<');
+ read('/');
+ name = readName(); // TODO: pass the expected name in as a hint?
skip();
read('>');
- int sp = (depth - 1) << 2;
+ int sp = (depth - 1) * 4;
if (depth == 0) {
- error("element stack empty");
+ checkRelaxed("read end tag " + name + " with no tags open");
type = COMMENT;
return;
}
- if (!relaxed) {
- if (!name.equals(elementStack[sp + 3])) {
- error("expected: /" + elementStack[sp + 3] + " read: " + name);
-
- // become case insensitive in relaxed mode
-
-// int probe = sp;
-// while (probe >= 0 && !name.toLowerCase().equals(elementStack[probe + 3].toLowerCase())) {
-// stackMismatch++;
-// probe -= 4;
-// }
-//
-// if (probe < 0) {
-// stackMismatch = 0;
-// // text = "unexpected end tag ignored";
-// type = COMMENT;
-// return;
-// }
- }
-
- namespace = elementStack[sp];
- prefix = elementStack[sp + 1];
- name = elementStack[sp + 2];
+ if (name.equals(elementStack[sp + 3])) {
+ namespace = elementStack[sp];
+ prefix = elementStack[sp + 1];
+ name = elementStack[sp + 2];
+ } else if (!relaxed) {
+ throw new XmlPullParserException(
+ "expected: /" + elementStack[sp + 3] + " read: " + name, this, null);
}
}
- private final int peekType() throws IOException {
- switch (peek(0)) {
- case -1 :
- return END_DOCUMENT;
- case '&' :
- return ENTITY_REF;
- case '<' :
- switch (peek(1)) {
- case '/' :
- return END_TAG;
- case '?' :
- case '!' :
- return LEGACY;
- default :
- return START_TAG;
- }
- default :
- return TEXT;
+ /**
+ * Returns the type of the next token.
+ */
+ private int peekType() throws IOException, XmlPullParserException {
+ if (position >= limit && !fillBuffer(1)) {
+ return END_DOCUMENT;
}
- }
-
- private final String get(int pos) {
- return new String(txtBuf, pos, txtPos - pos);
- }
-
- /*
- private final String pop (int pos) {
- String result = new String (txtBuf, pos, txtPos - pos);
- txtPos = pos;
- return result;
- }
- */
- private final void push(int c) {
+ if (buffer[position] == '&') {
+ return ENTITY_REF;
- isWhitespace &= c <= ' ';
+ } else if (buffer[position] == '<') {
+ if (position + 2 >= limit && !fillBuffer(3)) {
+ throw new XmlPullParserException("Dangling <", this, null);
+ }
- if (txtPos == txtBuf.length) {
- char[] bigger = new char[txtPos * 4 / 3 + 4];
- System.arraycopy(txtBuf, 0, bigger, 0, txtPos);
- txtBuf = bigger;
+ if (buffer[position + 1] == '/') {
+ return END_TAG;
+ } else if (buffer[position + 1] == '?') {
+ // we're looking for "<?xml " with case insensitivity
+ if ((position + 5 < limit || fillBuffer(6))
+ && (buffer[position + 2] == 'x' || buffer[position + 2] == 'X')
+ && (buffer[position + 3] == 'm' || buffer[position + 3] == 'M')
+ && (buffer[position + 4] == 'l' || buffer[position + 4] == 'L')
+ && (buffer[position + 5] == ' ')) {
+ return XML_DECLARATION;
+ } else {
+ return PROCESSING_INSTRUCTION;
+ }
+ } else if (buffer[position + 1] == '!') {
+ if (buffer[position + 2] == START_DOCTYPE[2]) {
+ return DOCDECL;
+ } else if (buffer[position + 2] == START_CDATA[2]) {
+ return CDSECT;
+ } else if (buffer[position + 2] == START_COMMENT[2]) {
+ return COMMENT;
+ } else {
+ throw new XmlPullParserException("Unexpected <!", this, null);
+ }
+ } else {
+ return START_TAG;
+ }
+ } else {
+ return TEXT;
}
-
- txtBuf[txtPos++] = (char) c;
}
- /** Sets name and attributes */
-
- private final void parseStartTag(boolean xmldecl)
- throws IOException, XmlPullParserException {
-
- if (!xmldecl)
- read();
+ /**
+ * Sets name and attributes
+ */
+ private void parseStartTag(boolean xmldecl) throws IOException, XmlPullParserException {
+ if (!xmldecl) {
+ read('<');
+ }
name = readName();
attributeCount = 0;
while (true) {
skip();
- int c = peek(0);
+ if (position >= limit && !fillBuffer(1)) {
+ checkRelaxed(UNEXPECTED_EOF);
+ return;
+ }
+
+ int c = buffer[position];
if (xmldecl) {
if (c == '?') {
- read();
+ position++;
read('>');
return;
}
- }
- else {
+ } else {
if (c == '/') {
degenerated = true;
- read();
+ position++;
skip();
read('>');
break;
- }
-
- if (c == '>' && !xmldecl) {
- read();
+ } else if (c == '>') {
+ position++;
break;
}
}
- if (c == -1) {
- error(UNEXPECTED_EOF);
- //type = COMMENT;
- return;
- }
-
String attrName = readName();
- if (attrName.length() == 0) {
- error("attr name expected");
- //type = COMMENT;
- break;
- }
-
- int i = (attributeCount++) << 2;
-
+ int i = (attributeCount++) * 4;
attributes = ensureCapacity(attributes, i + 4);
-
attributes[i++] = "";
attributes[i++] = null;
attributes[i++] = attrName;
skip();
+ if (position >= limit && !fillBuffer(1)) {
+ checkRelaxed(UNEXPECTED_EOF);
+ return;
+ }
+
+ if (buffer[position] == '=') {
+ position++;
- if (peek(0) != '=') {
- if(!relaxed){
- error("Attr.value missing f. "+attrName);
- }
- attributes[i] = attrName;
- } else {
- read('=');
skip();
- int delimiter = peek(0);
+ if (position >= limit && !fillBuffer(1)) {
+ checkRelaxed(UNEXPECTED_EOF);
+ return;
+ }
+ char delimiter = buffer[position];
- if (delimiter != '\'' && delimiter != '"') {
- if(!relaxed){
- error("attr value delimiter missing!");
- }
+ if (delimiter == '\'' || delimiter == '"') {
+ position++;
+ } else if (relaxed) {
delimiter = ' ';
} else {
- read();
+ throw new XmlPullParserException("attr value delimiter missing!", this, null);
}
- int p = txtPos;
- // BEGIN android-changed: distinguish attribute values from normal text.
- pushText(delimiter, true, true);
- // END android-changed
-
- attributes[i] = get(p);
- txtPos = p;
+ attributes[i] = readValue(delimiter, true, true);
- if (delimiter != ' ')
- read(); // skip endquote
+ if (delimiter != ' ') {
+ position++; // end quote
+ }
+ } else if (relaxed) {
+ attributes[i] = attrName;
+ } else {
+ checkRelaxed("Attr.value missing f. " + attrName);
+ attributes[i] = attrName;
}
}
- int sp = depth++ << 2;
-
+ int sp = depth++ * 4;
elementStack = ensureCapacity(elementStack, sp + 4);
elementStack[sp + 3] = name;
@@ -737,20 +720,11 @@ public class KXmlParser implements XmlPullParser {
nspCounts[depth] = nspCounts[depth - 1];
- /*
- if(!relaxed){
- for (int i = attributeCount - 1; i > 0; i--) {
- for (int j = 0; j < i; j++) {
- if (getAttributeName(i).equals(getAttributeName(j)))
- exception("Duplicate Attribute: " + getAttributeName(i));
- }
- }
- }
- */
- if (processNsp)
+ if (processNsp) {
adjustNsp();
- else
+ } else {
namespace = "";
+ }
elementStack[sp] = namespace;
elementStack[sp + 1] = prefix;
@@ -758,236 +732,355 @@ public class KXmlParser implements XmlPullParser {
}
/**
- * result: isWhitespace; if the setName parameter is set,
- * the name of the entity is stored in "name" */
-
- private final void pushEntity()
- throws IOException, XmlPullParserException {
-
- push(read()); // &
+ * Reads an entity reference from the buffer, resolves it, and writes the
+ * resolved entity to {@code out}. If the entity cannot be read or resolved,
+ * {@code out} will contain the partial entity reference.
+ */
+ private void readEntity(StringBuilder out, boolean isEntityToken)
+ throws IOException, XmlPullParserException {
+ int start = out.length();
+ if (buffer[position++] != '&') {
+ throw new AssertionError();
+ }
- int pos = txtPos;
+ out.append('&');
while (true) {
- int c = peek(0);
- if (c == ';') {
- read();
- break;
- }
- if (c < 128 && (c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z')
- && c != '_' && c != '-' && c != '#') {
- if(!relaxed){
- error("unterminated entity ref");
- }
+ int c = peekCharacter();
- // BEGIN android-removed: avoid log spam.
- // System.out.println("broken entitiy: "+get(pos-1));
- // END android-removed
+ if (c == ';') {
+ out.append(';');
+ position++;
+ break;
- //; ends with:"+(char)c);
-// if (c != -1)
-// push(c);
+ } else if (c >= 128
+ || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || c == '-'
+ || c == '#') {
+ position++;
+ out.append((char) c);
+
+ } else if (relaxed) {
+ // intentionally leave the partial reference in 'out'
return;
- }
- push(read());
+ } else {
+ throw new XmlPullParserException("unterminated entity ref", this, null);
+ }
}
- String code = get(pos);
- txtPos = pos - 1;
- if (token && type == ENTITY_REF){
- name = code;
- }
+ String code = out.substring(start + 1, out.length() - 1);
- if (code.charAt(0) == '#') {
- int c =
- (code.charAt(1) == 'x'
- ? Integer.parseInt(code.substring(2), 16)
- : Integer.parseInt(code.substring(1)));
- push(c);
- return;
+ if (isEntityToken) {
+ name = code;
}
- String result = (String) entityMap.get(code);
-
- unresolved = result == null;
-
- if (unresolved) {
- if (!token)
- error("unresolved: &" + code + ";");
- }
- else {
- for (int i = 0; i < result.length(); i++)
- push(result.charAt(i));
+ String resolved;
+ if (code.startsWith("#")) {
+ try {
+ int c = code.startsWith("#x")
+ ? Integer.parseInt(code.substring(2), 16)
+ : Integer.parseInt(code.substring(1));
+ out.delete(start, out.length());
+ out.appendCodePoint(c);
+ unresolved = false;
+ } catch (NumberFormatException notANumber) {
+ throw new XmlPullParserException("Invalid character reference: &" + code);
+ } catch (IllegalArgumentException invalidCodePoint) {
+ throw new XmlPullParserException("Invalid character reference: &" + code);
+ }
+ } else if ((resolved = entityMap.get(code)) != null) {
+ out.delete(start, out.length());
+ out.append(resolved);
+ unresolved = false;
+ } else {
+ // keep the unresolved entity "&code;" in the text for relaxed clients
+ unresolved = true;
+ if (!isEntityToken) {
+ checkRelaxed("unresolved: &" + code + ";");
+ }
}
}
- /** types:
- '<': parse to any token (for nextToken ())
- '"': parse to quote
- ' ': parse to whitespace or '>'
- */
-
- private final void pushText(int delimiter, boolean resolveEntities, boolean inAttributeValue)
- throws IOException, XmlPullParserException {
+ /**
+ * Returns the current text or attribute value. This also has the side
+ * effect of setting isWhitespace to false if a non-whitespace character is
+ * encountered.
+ *
+ * @param delimiter {@code >} for text, {@code "} and {@code '} for quoted
+ * attributes, or a space for unquoted attributes.
+ */
+ private String readValue(char delimiter, boolean resolveEntities,
+ boolean inAttributeValue) throws IOException, XmlPullParserException {
- int next = peek(0);
- int cbrCount = 0;
+ /*
+ * This method returns all of the characters from the current position
+ * through to an appropriate delimiter.
+ *
+ * If we're lucky (which we usually are), we'll return a single slice of
+ * the buffer. This fast path avoids allocating a string builder.
+ *
+ * There are 5 unlucky characters we could encounter:
+ * - "&": entities must be resolved.
+ * - "<": this isn't permitted in attributes unless relaxed.
+ * - "]": this requires a lookahead to defend against the forbidden
+ * CDATA section delimiter "]]>".
+ * - "\r": If a "\r" is followed by a "\n", we discard the "\r". If it
+ * isn't followed by "\n", we replace "\r" with either a "\n"
+ * in text nodes or a space in attribute values.
+ * - "\n": In attribute values, "\n" must be replaced with a space.
+ *
+ * We could also get unlucky by needing to refill the buffer midway
+ * through the text.
+ */
+
+ int start = position;
+ StringBuilder result = null;
+
+ // if a text section was already started, prefix the start
+ if (!inAttributeValue && text != null) {
+ result = new StringBuilder();
+ result.append(text);
+ }
- while (next != -1 && next != delimiter) { // covers eof, '<', '"'
+ while (true) {
- if (delimiter == ' ')
- if (next <= ' ' || next == '>')
- break;
+ /*
+ * Make sure we have at least a single character to read from the
+ * buffer. This mutates the buffer, so save the partial result
+ * to the slow path string builder first.
+ */
+ if (position >= limit) {
+ if (start < position) {
+ if (result == null) {
+ result = new StringBuilder();
+ }
+ result.append(buffer, start, position - start);
+ }
+ if (!fillBuffer(1)) {
+ return result != null ? result.toString() : "";
+ }
+ start = position;
+ }
- // BEGIN android-changed: "<" is not allowed in attribute values.
- if (next == '&') {
- if (!resolveEntities)
- break;
+ char c = buffer[position];
- pushEntity();
+ if (c == delimiter
+ || (delimiter == ' ' && (c <= ' ' || c == '>'))
+ || c == '&' && !resolveEntities) {
+ break;
}
- else if (next == '<' && inAttributeValue) {
- error("Illegal: \"<\" inside attribute value");
+
+ if (c != '\r'
+ && (c != '\n' || !inAttributeValue)
+ && c != '&'
+ && c != '<'
+ && (c != ']' || inAttributeValue)) {
+ isWhitespace &= (c <= ' ');
+ position++;
+ continue;
}
- else if (next == '\n' && type == START_TAG) {
- read();
- push(' ');
+
+ /*
+ * We've encountered an unlucky character! Convert from fast
+ * path to slow path if we haven't done so already.
+ */
+ if (result == null) {
+ result = new StringBuilder();
}
- else
- push(read());
- // END android-changed
-
- // BEGIN android-changed: "]]>" *is* allowed in attribute values, but
- // is not allowed in regular text between markup.
- final boolean allowCloseCdata = inAttributeValue;
- if (!allowCloseCdata && (next == '>' && cbrCount >= 2 && delimiter != ']')) {
- error("Illegal: \"]]>\" outside CDATA section");
+ result.append(buffer, start, position - start);
+
+ if (c == '\r') {
+ if ((position + 1 < limit || fillBuffer(2)) && buffer[position + 1] == '\n') {
+ position++;
+ }
+ c = inAttributeValue ? ' ' : '\n';
+
+ } else if (c == '\n') {
+ c = ' ';
+
+ } else if (c == '&') {
+ isWhitespace = false; // TODO: what if the entity resolves to whitespace?
+ readEntity(result, false);
+ start = position;
+ continue;
+
+ } else if (c == '<') {
+ if (inAttributeValue) {
+ checkRelaxed("Illegal: \"<\" inside attribute value");
+ }
+ isWhitespace = false;
+
+ } else if (c == ']') {
+ if ((position + 2 < limit || fillBuffer(3))
+ && buffer[position + 1] == ']' && buffer[position + 2] == '>') {
+ checkRelaxed("Illegal: \"]]>\" outside CDATA section");
+ }
+ isWhitespace = false;
+
+ } else {
+ throw new AssertionError();
}
- // END android-changed
- if (next == ']')
- cbrCount++;
- else
- cbrCount = 0;
+ position++;
+ result.append(c);
+ start = position;
+ }
- next = peek(0);
+ if (result == null) {
+ return stringPool.get(buffer, start, position - start);
+ } else {
+ result.append(buffer, start, position - start);
+ return result.toString();
}
}
- private final void read(char c)
- throws IOException, XmlPullParserException {
- int a = read();
- if (a != c)
- error("expected: '" + c + "' actual: '" + ((char) a) + "'");
+ private void read(char expected) throws IOException, XmlPullParserException {
+ int c = peekCharacter();
+ if (c != expected) {
+ checkRelaxed("expected: '" + expected + "' actual: '" + ((char) c) + "'");
+ }
+ position++;
}
- private final int read() throws IOException {
- int result;
-
- if (peekCount == 0)
- result = peek(0);
- else {
- result = peek[0];
- peek[0] = peek[1];
+ private void read(char[] chars) throws IOException, XmlPullParserException {
+ if (position + chars.length >= limit && !fillBuffer(chars.length)) {
+ checkRelaxed("expected: '" + new String(chars) + "' but was EOF");
+ return;
}
- // else {
- // result = peek[0];
- // System.arraycopy (peek, 1, peek, 0, peekCount-1);
- // }
- peekCount--;
- column++;
+ // TODO: replace with Arrays.equals(buffer, position, delimiter, 0, delimiter.length)
+ // when the VM has better method inlining
+ for (int i = 0; i < chars.length; i++) {
+ if (buffer[position + i] != chars[i]) {
+ checkRelaxed("expected: \"" + new String(chars) + "\" but was \""
+ + new String(buffer, position, chars.length) + "...\"");
+ }
+ }
- if (result == '\n') {
+ position += chars.length;
+ }
- line++;
- column = 1;
+ private int peekCharacter() throws IOException, XmlPullParserException {
+ if (position < limit || fillBuffer(1)) {
+ return buffer[position];
}
-
- return result;
+ return -1;
}
- /** Does never read more than needed */
+ /**
+ * Returns true once {@code limit - position >= minimum}. If the data is
+ * exhausted before that many characters are available, this returns
+ * false.
+ */
+ private boolean fillBuffer(int minimum) throws IOException {
+ // Before clobbering the old characters, update where buffer starts
+ for (int i = 0; i < position; i++) {
+ if (buffer[i] == '\n') {
+ bufferStartLine++;
+ bufferStartColumn = 0;
+ } else {
+ bufferStartColumn++;
+ }
+ }
- private final int peek(int pos) throws IOException {
+ if (limit != position) {
+ limit -= position;
+ System.arraycopy(buffer, position, buffer, 0, limit);
+ } else {
+ limit = 0;
+ }
- while (pos >= peekCount) {
+ position = 0;
+ int total;
+ while ((total = reader.read(buffer, limit, buffer.length - limit)) != -1) {
+ limit += total;
+ if (limit >= minimum) {
+ return true;
+ }
+ }
+ return false;
+ }
- int nw;
+ /**
+ * Returns an element or attribute name. This is always non-empty for
+ * non-relaxed parsers.
+ */
+ private String readName() throws IOException, XmlPullParserException {
+ if (position >= limit && !fillBuffer(1)) {
+ checkRelaxed("name expected");
+ return "";
+ }
- if (srcBuf.length <= 1)
- nw = reader.read();
- else if (srcPos < srcCount)
- nw = srcBuf[srcPos++];
- else {
- srcCount = reader.read(srcBuf, 0, srcBuf.length);
- if (srcCount <= 0)
- nw = -1;
- else
- nw = srcBuf[0];
+ int start = position;
+ StringBuilder result = null;
+
+ // read the first character
+ char c = buffer[position];
+ if ((c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || c == ':'
+ || c >= '\u00c0' // TODO: check the XML spec
+ || relaxed) {
+ position++;
+ } else {
+ checkRelaxed("name expected");
+ return "";
+ }
- srcPos = 1;
+ while (true) {
+ /*
+ * Make sure we have at least a single character to read from the
+ * buffer. This mutates the buffer, so save the partial result
+ * to the slow path string builder first.
+ */
+ if (position >= limit) {
+ if (result == null) {
+ result = new StringBuilder();
+ }
+ result.append(buffer, start, position - start);
+ if (!fillBuffer(1)) {
+ return result.toString();
+ }
+ start = position;
}
- if (nw == '\r') {
- wasCR = true;
- peek[peekCount++] = '\n';
+ // read another character
+ c = buffer[position];
+ if ((c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9')
+ || c == '_'
+ || c == '-'
+ || c == ':'
+ || c == '.'
+ || c >= '\u00b7') { // TODO: check the XML spec
+ position++;
+ continue;
}
- else {
- if (nw == '\n') {
- if (!wasCR)
- peek[peekCount++] = '\n';
- }
- else
- peek[peekCount++] = nw;
- wasCR = false;
+ // we encountered a non-name character. done!
+ if (result == null) {
+ return stringPool.get(buffer, start, position - start);
+ } else {
+ result.append(buffer, start, position - start);
+ return result.toString();
}
}
-
- return peek[pos];
}
- private final String readName()
- throws IOException, XmlPullParserException {
-
- int pos = txtPos;
- int c = peek(0);
- if ((c < 'a' || c > 'z')
- && (c < 'A' || c > 'Z')
- && c != '_'
- && c != ':'
- && c < 0x0c0
- && !relaxed)
- error("name expected");
-
- do {
- push(read());
- c = peek(0);
- }
- while ((c >= 'a' && c <= 'z')
- || (c >= 'A' && c <= 'Z')
- || (c >= '0' && c <= '9')
- || c == '_'
- || c == '-'
- || c == ':'
- || c == '.'
- || c >= 0x0b7);
-
- String result = get(pos);
- txtPos = pos;
- return result;
- }
-
- private final void skip() throws IOException {
-
- while (true) {
- int c = peek(0);
- if (c > ' ' || c == -1)
+ private void skip() throws IOException {
+ while (position < limit || fillBuffer(1)) {
+ int c = buffer[position];
+ if (c > ' ') {
break;
- read();
+ }
+ position++;
}
}
@@ -996,8 +1089,6 @@ public class KXmlParser implements XmlPullParser {
public void setInput(Reader reader) throws XmlPullParserException {
this.reader = reader;
- line = 1;
- column = 0;
type = START_DOCUMENT;
name = null;
namespace = null;
@@ -1007,15 +1098,17 @@ public class KXmlParser implements XmlPullParser {
version = null;
standalone = null;
- if (reader == null)
+ if (reader == null) {
return;
+ }
- srcPos = 0;
- srcCount = 0;
- peekCount = 0;
+ position = 0;
+ limit = 0;
+ bufferStartLine = 0;
+ bufferStartColumn = 0;
depth = 0;
- entityMap = new Hashtable();
+ entityMap = new HashMap<String, String>();
entityMap.put("amp", "&");
entityMap.put("apos", "'");
entityMap.put("gt", ">");
@@ -1023,82 +1116,81 @@ public class KXmlParser implements XmlPullParser {
entityMap.put("quot", "\"");
}
- public void setInput(InputStream is, String _enc)
- throws XmlPullParserException {
-
- srcPos = 0;
- srcCount = 0;
+ public void setInput(InputStream is, String _enc) throws XmlPullParserException {
+ position = 0;
+ limit = 0;
String enc = _enc;
- if (is == null)
+ if (is == null) {
throw new IllegalArgumentException();
+ }
try {
-
if (enc == null) {
- // read four bytes
-
- int chk = 0;
-
- while (srcCount < 4) {
+ // read the four bytes looking for an indication of the encoding in use
+ int firstFourBytes = 0;
+ while (limit < 4) {
int i = is.read();
- if (i == -1)
+ if (i == -1) {
break;
- chk = (chk << 8) | i;
- srcBuf[srcCount++] = (char) i;
+ }
+ firstFourBytes = (firstFourBytes << 8) | i;
+ buffer[limit++] = (char) i;
}
- if (srcCount == 4) {
- switch (chk) {
- case 0x00000FEFF :
+ if (limit == 4) {
+ switch (firstFourBytes) {
+ case 0x00000FEFF: // UTF-32BE BOM
enc = "UTF-32BE";
- srcCount = 0;
+ limit = 0;
break;
- case 0x0FFFE0000 :
+ case 0x0FFFE0000: // UTF-32LE BOM
enc = "UTF-32LE";
- srcCount = 0;
+ limit = 0;
break;
- case 0x03c :
+ case 0x0000003c: // '>' in UTF-32BE
enc = "UTF-32BE";
- srcBuf[0] = '<';
- srcCount = 1;
+ buffer[0] = '<';
+ limit = 1;
break;
- case 0x03c000000 :
+ case 0x03c000000: // '<' in UTF-32LE
enc = "UTF-32LE";
- srcBuf[0] = '<';
- srcCount = 1;
+ buffer[0] = '<';
+ limit = 1;
break;
- case 0x0003c003f :
+ case 0x0003c003f: // "<?" in UTF-16BE
enc = "UTF-16BE";
- srcBuf[0] = '<';
- srcBuf[1] = '?';
- srcCount = 2;
+ buffer[0] = '<';
+ buffer[1] = '?';
+ limit = 2;
break;
- case 0x03c003f00 :
+ case 0x03c003f00: // "<?" in UTF-16LE
enc = "UTF-16LE";
- srcBuf[0] = '<';
- srcBuf[1] = '?';
- srcCount = 2;
+ buffer[0] = '<';
+ buffer[1] = '?';
+ limit = 2;
break;
- case 0x03c3f786d :
+ case 0x03c3f786d: // "<?xm" in ASCII etc.
while (true) {
int i = is.read();
- if (i == -1)
+ if (i == -1) {
break;
- srcBuf[srcCount++] = (char) i;
+ }
+ buffer[limit++] = (char) i;
if (i == '>') {
- String s = new String(srcBuf, 0, srcCount);
+ String s = new String(buffer, 0, limit);
int i0 = s.indexOf("encoding");
if (i0 != -1) {
while (s.charAt(i0) != '"'
- && s.charAt(i0) != '\'')
+ && s.charAt(i0) != '\'') {
i0++;
+ }
char deli = s.charAt(i0++);
int i1 = s.indexOf(deli, i0);
enc = s.substring(i0, i1);
@@ -1107,51 +1199,46 @@ public class KXmlParser implements XmlPullParser {
}
}
- default :
- if ((chk & 0x0ffff0000) == 0x0FEFF0000) {
+ default:
+ // handle a byte order mark followed by something other than <?
+ if ((firstFourBytes & 0x0ffff0000) == 0x0FEFF0000) {
enc = "UTF-16BE";
- srcBuf[0] =
- (char) ((srcBuf[2] << 8) | srcBuf[3]);
- srcCount = 1;
- }
- else if ((chk & 0x0ffff0000) == 0x0fffe0000) {
+ buffer[0] = (char) ((buffer[2] << 8) | buffer[3]);
+ limit = 1;
+ } else if ((firstFourBytes & 0x0ffff0000) == 0x0fffe0000) {
enc = "UTF-16LE";
- srcBuf[0] =
- (char) ((srcBuf[3] << 8) | srcBuf[2]);
- srcCount = 1;
- }
- else if ((chk & 0x0ffffff00) == 0x0EFBBBF00) {
+ buffer[0] = (char) ((buffer[3] << 8) | buffer[2]);
+ limit = 1;
+ } else if ((firstFourBytes & 0x0ffffff00) == 0x0EFBBBF00) {
enc = "UTF-8";
- srcBuf[0] = srcBuf[3];
- srcCount = 1;
+ buffer[0] = buffer[3];
+ limit = 1;
}
}
}
}
- if (enc == null)
+ if (enc == null) {
enc = "UTF-8";
+ }
- int sc = srcCount;
+ int sc = limit;
setInput(new InputStreamReader(is, enc));
encoding = _enc;
- srcCount = sc;
- }
- catch (Exception e) {
- throw new XmlPullParserException(
- "Invalid stream or encoding: " + e.toString(),
- this,
- e);
+ limit = sc;
+ } catch (Exception e) {
+ throw new XmlPullParserException("Invalid stream or encoding: " + e, this, e);
}
}
public boolean getFeature(String feature) {
- if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature))
+ if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature)) {
return processNsp;
- else if (isProp(feature, false, "relaxed"))
+ } else if (isProp(feature, false, "relaxed")) {
return relaxed;
- else
+ } else {
return false;
+ }
}
public String getInputEncoding() {
@@ -1159,50 +1246,58 @@ public class KXmlParser implements XmlPullParser {
}
public void defineEntityReplacementText(String entity, String value)
- throws XmlPullParserException {
- if (entityMap == null)
+ throws XmlPullParserException {
+ if (entityMap == null) {
throw new RuntimeException("entity replacement text must be defined after setInput!");
+ }
entityMap.put(entity, value);
}
public Object getProperty(String property) {
- if (isProp(property, true, "xmldecl-version"))
+ if (isProp(property, true, "xmldecl-version")) {
return version;
- if (isProp(property, true, "xmldecl-standalone"))
+ }
+ if (isProp(property, true, "xmldecl-standalone")) {
return standalone;
- if (isProp(property, true, "location"))
+ }
+ if (isProp(property, true, "location")) {
return location != null ? location : reader.toString();
+ }
return null;
}
public int getNamespaceCount(int depth) {
- if (depth > this.depth)
+ if (depth > this.depth) {
throw new IndexOutOfBoundsException();
+ }
return nspCounts[depth];
}
public String getNamespacePrefix(int pos) {
- return nspStack[pos << 1];
+ return nspStack[pos * 2];
}
public String getNamespaceUri(int pos) {
- return nspStack[(pos << 1) + 1];
+ return nspStack[(pos * 2) + 1];
}
public String getNamespace(String prefix) {
- if ("xml".equals(prefix))
+ if ("xml".equals(prefix)) {
return "http://www.w3.org/XML/1998/namespace";
- if ("xmlns".equals(prefix))
+ }
+ if ("xmlns".equals(prefix)) {
return "http://www.w3.org/2000/xmlns/";
+ }
for (int i = (getNamespaceCount(depth) << 1) - 2; i >= 0; i -= 2) {
if (prefix == null) {
- if (nspStack[i] == null)
+ if (nspStack[i] == null) {
return nspStack[i + 1];
- }
- else if (prefix.equals(nspStack[i]))
+ }
+ } else if (prefix.equals(nspStack[i])) {
return nspStack[i + 1];
+ }
}
return null;
}
@@ -1212,50 +1307,52 @@ public class KXmlParser implements XmlPullParser {
}
public String getPositionDescription() {
-
- StringBuffer buf =
- new StringBuffer(type < TYPES.length ? TYPES[type] : "unknown");
+ StringBuilder buf = new StringBuilder(type < TYPES.length ? TYPES[type] : "unknown");
buf.append(' ');
if (type == START_TAG || type == END_TAG) {
- if (degenerated)
+ if (degenerated) {
buf.append("(empty) ");
+ }
buf.append('<');
- if (type == END_TAG)
+ if (type == END_TAG) {
buf.append('/');
+ }
- if (prefix != null)
+ if (prefix != null) {
buf.append("{" + namespace + "}" + prefix + ":");
+ }
buf.append(name);
- int cnt = attributeCount << 2;
+ int cnt = attributeCount * 4;
for (int i = 0; i < cnt; i += 4) {
buf.append(' ');
- if (attributes[i + 1] != null)
- buf.append(
- "{" + attributes[i] + "}" + attributes[i + 1] + ":");
+ if (attributes[i + 1] != null) {
+ buf.append("{" + attributes[i] + "}" + attributes[i + 1] + ":");
+ }
buf.append(attributes[i + 2] + "='" + attributes[i + 3] + "'");
}
buf.append('>');
- }
- else if (type == IGNORABLE_WHITESPACE);
- else if (type != TEXT)
+ } else if (type == IGNORABLE_WHITESPACE) {
+ ;
+ } else if (type != TEXT) {
buf.append(getText());
- else if (isWhitespace)
+ } else if (isWhitespace) {
buf.append("(whitespace)");
- else {
+ } else {
String text = getText();
- if (text.length() > 16)
+ if (text.length() > 16) {
text = text.substring(0, 16) + "...";
+ }
buf.append(text);
}
- buf.append("@"+line + ":" + column);
- if(location != null){
+ buf.append("@" + getLineNumber() + ":" + getColumnNumber());
+ if (location != null) {
buf.append(" in ");
buf.append(location);
- } else if(reader != null){
+ } else if (reader != null) {
buf.append(" in ");
buf.append(reader.toString());
}
@@ -1263,39 +1360,55 @@ public class KXmlParser implements XmlPullParser {
}
public int getLineNumber() {
- return line;
+ int result = bufferStartLine;
+ for (int i = 0; i < position; i++) {
+ if (buffer[i] == '\n') {
+ result++;
+ }
+ }
+ return result + 1; // the first line is '1'
}
public int getColumnNumber() {
- return column;
+ int result = bufferStartColumn;
+ for (int i = 0; i < position; i++) {
+ if (buffer[i] == '\n') {
+ result = 0;
+ } else {
+ result++;
+ }
+ }
+ return result + 1; // the first column is '1'
}
public boolean isWhitespace() throws XmlPullParserException {
- if (type != TEXT && type != IGNORABLE_WHITESPACE && type != CDSECT)
- exception(ILLEGAL_TYPE);
+ if (type != TEXT && type != IGNORABLE_WHITESPACE && type != CDSECT) {
+ throw new XmlPullParserException(ILLEGAL_TYPE, this, null);
+ }
return isWhitespace;
}
public String getText() {
- return type < TEXT
- || (type == ENTITY_REF && unresolved) ? null : get(0);
+ if (type < TEXT || (type == ENTITY_REF && unresolved)) {
+ return null;
+ } else if (text == null) {
+ return "";
+ } else {
+ return text;
+ }
}
public char[] getTextCharacters(int[] poslen) {
- if (type >= TEXT) {
- if (type == ENTITY_REF) {
- poslen[0] = 0;
- poslen[1] = name.length();
- return name.toCharArray();
- }
- poslen[0] = 0;
- poslen[1] = txtPos;
- return txtBuf;
+ String text = getText();
+ if (text == null) {
+ poslen[0] = -1;
+ poslen[1] = -1;
+ return null;
}
-
- poslen[0] = -1;
- poslen[1] = -1;
- return null;
+ char[] result = text.toCharArray();
+ poslen[0] = 0;
+ poslen[1] = result.length;
+ return result;
}
public String getNamespace() {
@@ -1311,8 +1424,9 @@ public class KXmlParser implements XmlPullParser {
}
public boolean isEmptyElementTag() throws XmlPullParserException {
- if (type != START_TAG)
- exception(ILLEGAL_TYPE);
+ if (type != START_TAG) {
+ throw new XmlPullParserException(ILLEGAL_TYPE, this, null);
+ }
return degenerated;
}
@@ -1329,35 +1443,39 @@ public class KXmlParser implements XmlPullParser {
}
public String getAttributeNamespace(int index) {
- if (index >= attributeCount)
+ if (index >= attributeCount) {
throw new IndexOutOfBoundsException();
- return attributes[index << 2];
+ }
+ return attributes[index * 4];
}
public String getAttributeName(int index) {
- if (index >= attributeCount)
+ if (index >= attributeCount) {
throw new IndexOutOfBoundsException();
- return attributes[(index << 2) + 2];
+ }
+ return attributes[(index * 4) + 2];
}
public String getAttributePrefix(int index) {
- if (index >= attributeCount)
+ if (index >= attributeCount) {
throw new IndexOutOfBoundsException();
- return attributes[(index << 2) + 1];
+ }
+ return attributes[(index * 4) + 1];
}
public String getAttributeValue(int index) {
- if (index >= attributeCount)
+ if (index >= attributeCount) {
throw new IndexOutOfBoundsException();
- return attributes[(index << 2) + 3];
+ }
+ return attributes[(index * 4) + 3];
}
public String getAttributeValue(String namespace, String name) {
-
- for (int i = (attributeCount << 2) - 4; i >= 0; i -= 4) {
+ for (int i = (attributeCount * 4) - 4; i >= 0; i -= 4) {
if (attributes[i + 2].equals(name)
- && (namespace == null || attributes[i].equals(namespace)))
+ && (namespace == null || attributes[i].equals(namespace))) {
return attributes[i + 3];
+ }
}
return null;
@@ -1367,123 +1485,70 @@ public class KXmlParser implements XmlPullParser {
return type;
}
- public int next() throws XmlPullParserException, IOException {
-
- txtPos = 0;
- isWhitespace = true;
- int minType = 9999;
- token = false;
-
- do {
- nextImpl();
- if (type < minType)
- minType = type;
- // if (curr <= TEXT) type = curr;
- }
- while (minType > ENTITY_REF // ignorable
- || (minType >= TEXT && peekType() >= TEXT));
-
- type = minType;
- if (type > TEXT)
- type = TEXT;
-
- return type;
- }
-
- public int nextToken() throws XmlPullParserException, IOException {
-
- isWhitespace = true;
- txtPos = 0;
-
- token = true;
- nextImpl();
- return type;
- }
-
- //
// utility methods to make XML parsing easier ...
public int nextTag() throws XmlPullParserException, IOException {
-
next();
- if (type == TEXT && isWhitespace)
+ if (type == TEXT && isWhitespace) {
next();
+ }
- if (type != END_TAG && type != START_TAG)
- exception("unexpected type");
+ if (type != END_TAG && type != START_TAG) {
+ throw new XmlPullParserException("unexpected type", this, null);
+ }
return type;
}
public void require(int type, String namespace, String name)
- throws XmlPullParserException, IOException {
+ throws XmlPullParserException, IOException {
if (type != this.type
- || (namespace != null && !namespace.equals(getNamespace()))
- || (name != null && !name.equals(getName())))
- exception(
- "expected: " + TYPES[type] + " {" + namespace + "}" + name);
+ || (namespace != null && !namespace.equals(getNamespace()))
+ || (name != null && !name.equals(getName()))) {
+ throw new XmlPullParserException(
+ "expected: " + TYPES[type] + " {" + namespace + "}" + name, this, null);
+ }
}
public String nextText() throws XmlPullParserException, IOException {
- if (type != START_TAG)
- exception("precondition: START_TAG");
+ if (type != START_TAG) {
+ throw new XmlPullParserException("precondition: START_TAG", this, null);
+ }
next();
String result;
-
if (type == TEXT) {
result = getText();
next();
- }
- else
+ } else {
result = "";
+ }
- if (type != END_TAG)
- exception("END_TAG expected");
+ if (type != END_TAG) {
+ throw new XmlPullParserException("END_TAG expected", this, null);
+ }
return result;
}
- public void setFeature(String feature, boolean value)
- throws XmlPullParserException {
- if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature))
+ public void setFeature(String feature, boolean value) throws XmlPullParserException {
+ if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature)) {
processNsp = value;
- else if (isProp(feature, false, "relaxed"))
+ } else if (isProp(feature, false, "relaxed")) {
+ // "http://xmlpull.org/v1/doc/features.html#relaxed"
relaxed = value;
- else
- exception("unsupported feature: " + feature);
- }
-
- public void setProperty(String property, Object value)
- throws XmlPullParserException {
- if(isProp(property, true, "location")) {
- location = value;
} else {
- throw new XmlPullParserException("unsupported property: " + property);
+ throw new XmlPullParserException("unsupported feature: " + feature, this, null);
}
}
- /**
- * Skip sub tree that is currently porser positioned on.
- * <br>NOTE: parser must be on START_TAG and when funtion returns
- * parser will be positioned on corresponding END_TAG.
- */
-
- // Implementation copied from Alek's mail...
-
- public void skipSubTree() throws XmlPullParserException, IOException {
- require(START_TAG, null, null);
- int level = 1;
- while (level > 0) {
- int eventType = next();
- if (eventType == END_TAG) {
- --level;
- }
- else if (eventType == START_TAG) {
- ++level;
- }
+ public void setProperty(String property, Object value) throws XmlPullParserException {
+ if (isProp(property, true, "location")) {
+ location = String.valueOf(value);
+ } else {
+ throw new XmlPullParserException("unsupported property: " + property);
}
}
}
diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
index 66c8f4d..36e6025 100644
--- a/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
+++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
@@ -59,7 +59,7 @@ import java.io.Reader;
* getProperty(&quot;<a href="http://xmlpull.org/v1/doc/properties.html#xmldecl-version">http://xmlpull.org/v1/doc/properties.html#xmldecl-version</a>&quot;)
* returns String ("1.0") or null if XMLDecl was not read or if property is not supported
* <li><b>standalone</b>:
- * getProperty(&quot;<a href="http://xmlpull.org/v1/doc/features.html#xmldecl-standalone">http://xmlpull.org/v1/doc/features.html#xmldecl-standalone</a>&quot;)
+ * getProperty(&quot;<a href="http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone">http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone</a>&quot;)
* returns Boolean: null if there was no standalone declaration
* or if property is not supported
* otherwise returns Boolean(true) if standalone="yes" and Boolean(false) when standalone="no"
@@ -74,7 +74,7 @@ import java.io.Reader;
* import java.io.StringReader;
*
* import org.xmlpull.v1.XmlPullParser;
- * import org.xmlpull.v1.<a href="XmlPullParserException.html">XmlPullParserException.html</a>;
+ * import org.xmlpull.v1.<a href="XmlPullParserException.html">XmlPullParserException</a>;
* import org.xmlpull.v1.<a href="XmlPullParserFactory.html">XmlPullParserFactory</a>;
*
* public class SimpleXmlPullApp
@@ -92,8 +92,6 @@ import java.io.Reader;
* while (eventType != XmlPullParser.END_DOCUMENT) {
* if(eventType == XmlPullParser.START_DOCUMENT) {
* System.out.println("Start document");
- * } else if(eventType == XmlPullParser.END_DOCUMENT) {
- * System.out.println("End document");
* } else if(eventType == XmlPullParser.START_TAG) {
* System.out.println("Start tag "+xpp.<a href="#getName()">getName()</a>);
* } else if(eventType == XmlPullParser.END_TAG) {
@@ -103,6 +101,7 @@ import java.io.Reader;
* }
* eventType = xpp.next();
* }
+ * System.out.println("End document");
* }
* }
* </pre>