summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--archive/src/main/java/java/util/zip/ZipFile.java60
-rw-r--r--archive/src/main/native/java_util_zip_Adler32.c9
-rw-r--r--archive/src/main/native/java_util_zip_CRC32.c8
-rw-r--r--archive/src/main/native/java_util_zip_Deflater.c25
-rw-r--r--archive/src/main/native/java_util_zip_Inflater.c27
-rw-r--r--archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java35
-rw-r--r--dalvik/src/main/java/dalvik/system/DexClassLoader.java2
-rw-r--r--icu/src/main/native/BidiWrapperInterface.c44
-rw-r--r--icu/src/main/native/DecimalFormatInterface.cpp108
-rw-r--r--icu/src/main/native/ErrorCode.h8
-rw-r--r--icu/src/main/native/RBNFInterface.cpp112
-rw-r--r--icu/src/main/native/RegExInterface.cpp18
-rw-r--r--icu/src/main/native/ResourceInterface.cpp26
-rw-r--r--luni-kernel/src/main/java/java/lang/Package.java5
-rw-r--r--luni-kernel/src/main/java/java/lang/ref/Reference.java8
-rw-r--r--luni/src/main/java/java/io/PipedInputStream.java123
-rw-r--r--luni/src/main/java/java/net/InetAddress.java183
-rw-r--r--luni/src/main/java/java/util/Formatter.java79
-rw-r--r--luni/src/main/java/java/util/HashMap.java2
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java17
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java5
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java3
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java238
-rw-r--r--luni/src/main/native/java_net_InetAddress.cpp64
-rw-r--r--luni/src/main/native/java_net_NetworkInterface.c14
-rw-r--r--luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp160
-rw-r--r--luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp10
-rw-r--r--luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp533
-rw-r--r--luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java2
-rw-r--r--luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringBuffer2Test.java1
-rw-r--r--luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet4AddressTest.java9
-rw-r--r--luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet6AddressTest.java17
-rw-r--r--luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java7
-rw-r--r--luni/src/test/java/tests/api/java/util/FormatterTest.java30
-rw-r--r--luni/src/test/java/tests/api/java/util/TimeZoneTest.java13
-rw-r--r--math/src/main/java/java/math/BigInt.java6
-rw-r--r--openssl/src/main/java/org/openssl/NativeBN.java3
-rw-r--r--openssl/src/main/native/BNInterface.c28
-rw-r--r--regex/src/main/java/java/util/regex/MatchResult.java67
-rw-r--r--regex/src/main/java/java/util/regex/Matcher.java752
-rw-r--r--regex/src/main/java/java/util/regex/Pattern.java355
-rw-r--r--regex/src/main/java/java/util/regex/PatternSyntaxException.java56
-rw-r--r--sql/src/main/native/sqlite_jni.c33
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java10
-rw-r--r--x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp4
-rw-r--r--x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp31
-rw-r--r--x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp4
-rw-r--r--x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h5
-rw-r--r--x-net/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java23
-rw-r--r--x-net/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java22
-rw-r--r--xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java2
-rw-r--r--xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp26
-rw-r--r--xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java1
-rw-r--r--xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTestSupport.java2
-rw-r--r--xml/src/test/java/tests/xml/AllTests.java1
-rw-r--r--xml/src/test/java/tests/xml/NodeTests.java47
56 files changed, 1743 insertions, 1740 deletions
diff --git a/archive/src/main/java/java/util/zip/ZipFile.java b/archive/src/main/java/java/util/zip/ZipFile.java
index 653b2c9..b5f3678 100644
--- a/archive/src/main/java/java/util/zip/ZipFile.java
+++ b/archive/src/main/java/java/util/zip/ZipFile.java
@@ -52,12 +52,12 @@ public class ZipFile implements ZipConstants {
File fileToDeleteOnClose;
/**
- * Open zip file for read.
+ * Open ZIP file for read.
*/
public static final int OPEN_READ = 1;
/**
- * Delete zip file when closed.
+ * Delete ZIP file when closed.
*/
public static final int OPEN_DELETE = 4;
@@ -140,7 +140,7 @@ public class ZipFile implements ZipConstants {
}
/**
- * Closes this ZIP file.
+ * Closes this ZIP file. This method is idempotent.
*
* @throws IOException
* if an IOException occurs.
@@ -166,23 +166,32 @@ public class ZipFile implements ZipConstants {
}
}
+ private void checkNotClosed() {
+ if (mRaf == null) {
+ throw new IllegalStateException("Zip File closed.");
+ }
+ }
+
/**
* Returns an enumeration of the entries. The entries are listed in the
* order in which they appear in the ZIP archive.
*
* @return the enumeration of the entries.
+ * @throws IllegalStateException if this ZIP file has been closed.
*/
public Enumeration<? extends ZipEntry> entries() {
+ checkNotClosed();
+
return new Enumeration<ZipEntry>() {
private int i = 0;
public boolean hasMoreElements() {
- if (mRaf == null) throw new IllegalStateException("Zip File closed.");
+ checkNotClosed();
return i < mEntryList.size();
}
public ZipEntry nextElement() {
- if (mRaf == null) throw new IllegalStateException("Zip File closed.");
+ checkNotClosed();
if (i >= mEntryList.size())
throw new NoSuchElementException();
return (ZipEntry) mEntryList.get(i++);
@@ -197,8 +206,10 @@ public class ZipFile implements ZipConstants {
* the name of the entry in the ZIP file.
* @return a {@code ZipEntry} or {@code null} if the entry name does not
* exist in the ZIP file.
+ * @throws IllegalStateException if this ZIP file has been closed.
*/
public ZipEntry getEntry(String entryName) {
+ checkNotClosed();
if (entryName != null) {
ZipEntry ze = mFastLookup.get(entryName);
if (ze == null) ze = mFastLookup.get(entryName + "/");
@@ -215,6 +226,7 @@ public class ZipFile implements ZipConstants {
* @return an input stream of the data contained in the {@code ZipEntry}.
* @throws IOException
* if an {@code IOException} occurs.
+ * @throws IllegalStateException if this ZIP file has been closed.
*/
public InputStream getInputStream(ZipEntry entry) throws IOException {
/*
@@ -229,27 +241,25 @@ public class ZipFile implements ZipConstants {
* Create a ZipInputStream at the right part of the file.
*/
RandomAccessFile raf = mRaf;
- if (raf != null) {
- synchronized (raf) {
- // Unfortunately we don't know the entry data's start position.
- // All we have is the position of the entry's local header.
- // At position 28 we find the length of the extra data.
- // In some cases this length differs from the one coming in
- // the central header!!!
- RAFStream rafstrm = new RAFStream(raf, entry.mLocalHeaderRelOffset + 28);
- int localExtraLenOrWhatever = ler.readShortLE(rafstrm);
- // Now we need to skip the name
- // and this "extra" data or whatever it is:
- rafstrm.skip(entry.nameLen + localExtraLenOrWhatever);
- rafstrm.mLength = rafstrm.mOffset + entry.compressedSize;
- if (entry.compressionMethod == ZipEntry.DEFLATED) {
- return new InflaterInputStream(rafstrm, new Inflater(true));
- } else {
- return rafstrm;
- }
+ synchronized (raf) {
+ // Unfortunately we don't know the entry data's start position.
+ // All we have is the position of the entry's local header.
+ // At position 28 we find the length of the extra data.
+ // In some cases this length differs from the one coming in
+ // the central header!!!
+ RAFStream rafstrm = new RAFStream(raf,
+ entry.mLocalHeaderRelOffset + 28);
+ int localExtraLenOrWhatever = ler.readShortLE(rafstrm);
+ // Now we need to skip the name
+ // and this "extra" data or whatever it is:
+ rafstrm.skip(entry.nameLen + localExtraLenOrWhatever);
+ rafstrm.mLength = rafstrm.mOffset + entry.compressedSize;
+ if (entry.compressionMethod == ZipEntry.DEFLATED) {
+ return new InflaterInputStream(rafstrm, new Inflater(true));
+ } else {
+ return rafstrm;
}
}
- throw new IllegalStateException("Zip File closed");
}
/**
@@ -265,8 +275,10 @@ public class ZipFile implements ZipConstants {
* Returns the number of {@code ZipEntries} in this {@code ZipFile}.
*
* @return the number of entries in this file.
+ * @throws IllegalStateException if this ZIP file has been closed.
*/
public int size() {
+ checkNotClosed();
return mEntryList.size();
}
diff --git a/archive/src/main/native/java_util_zip_Adler32.c b/archive/src/main/native/java_util_zip_Adler32.c
index 1b02a11..0fcf549 100644
--- a/archive/src/main/native/java_util_zip_Adler32.c
+++ b/archive/src/main/native/java_util_zip_Adler32.c
@@ -25,16 +25,11 @@ Java_java_util_zip_Adler32_updateImpl (JNIEnv * env, jobject recv,
jbyteArray buf, int off, int len,
jlong crc)
{
- jbyte *b;
- jboolean isCopy;
- jlong result;
-
- b = (*env)->GetPrimitiveArrayCritical (env, buf, &isCopy);
+ jbyte* b = (*env)->GetPrimitiveArrayCritical (env, buf, NULL);
if (b == NULL) {
- throwNewOutOfMemoryError(env, "");
return 0;
}
- result = (jlong) adler32 ((uLong) crc, (Bytef *) (b + off), (uInt) len);
+ jlong result = (jlong) adler32 ((uLong) crc, (Bytef *) (b + off), (uInt) len);
(*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT);
return result;
diff --git a/archive/src/main/native/java_util_zip_CRC32.c b/archive/src/main/native/java_util_zip_CRC32.c
index cee25e5..fe50fca 100644
--- a/archive/src/main/native/java_util_zip_CRC32.c
+++ b/archive/src/main/native/java_util_zip_CRC32.c
@@ -25,15 +25,11 @@ Java_java_util_zip_CRC32_updateImpl (JNIEnv * env, jobject recv,
jbyteArray buf, int off, int len,
jlong crc)
{
- jbyte *b;
- jlong result;
-
- b = ((*env)->GetPrimitiveArrayCritical (env, buf, 0));
+ jbyte* b = ((*env)->GetPrimitiveArrayCritical (env, buf, 0));
if (b == NULL) {
- throwNewOutOfMemoryError(env, "");
return -1;
}
- result = crc32 ((uLong) crc, (Bytef *) (b + off), (uInt) len);
+ jlong result = crc32 ((uLong) crc, (Bytef *) (b + off), (uInt) len);
((*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT));
return result;
}
diff --git a/archive/src/main/native/java_util_zip_Deflater.c b/archive/src/main/native/java_util_zip_Deflater.c
index 2e0e268..af0bfcc 100644
--- a/archive/src/main/native/java_util_zip_Deflater.c
+++ b/archive/src/main/native/java_util_zip_Deflater.c
@@ -162,29 +162,19 @@ Java_java_util_zip_Deflater_setInputImpl (JNIEnv * env, jobject recv,
{
PORT_ACCESS_FROM_ENV (env);
- jbyte *in;
- JCLZipStream *stream;
-
- stream = (JCLZipStream *) ((IDATA) handle);
- if (stream->inaddr != NULL) /*Input has already been provided, free the old buffer */
+ JCLZipStream* stream = (JCLZipStream *) ((IDATA) handle);
+ if (stream->inaddr != NULL) {
+ /* Input has already been provided, free the old buffer. */
jclmem_free_memory (env, stream->inaddr);
+ }
stream->inaddr = jclmem_allocate_memory (env, len);
- if (stream->inaddr == NULL)
- {
- throwNewOutOfMemoryError (env, "");
- return;
- }
- in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0));
- if (in == NULL) {
- throwNewOutOfMemoryError(env, "");
+ if (stream->inaddr == NULL) {
+ throwNewOutOfMemoryError (env, "");
return;
}
- memcpy (stream->inaddr, (in + off), len);
- ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT));
+ (*env)->GetByteArrayRegion(env, buf, off, len, (jbyte*) stream->inaddr);
stream->stream->next_in = (Bytef *) stream->inaddr;
stream->stream->avail_in = len;
-
- return;
}
JNIEXPORT jint JNICALL
@@ -209,7 +199,6 @@ Java_java_util_zip_Deflater_deflateImpl (JNIEnv * env, jobject recv,
sout = stream->stream->total_out;
out = ((*env)->GetPrimitiveArrayCritical (env, buf, 0));
if (out == NULL) {
- throwNewOutOfMemoryError(env, "");
return -1;
}
stream->stream->next_out = (Bytef *) out + off;
diff --git a/archive/src/main/native/java_util_zip_Inflater.c b/archive/src/main/native/java_util_zip_Inflater.c
index 4b30d4e..c04a223 100644
--- a/archive/src/main/native/java_util_zip_Inflater.c
+++ b/archive/src/main/native/java_util_zip_Inflater.c
@@ -104,29 +104,20 @@ Java_java_util_zip_Inflater_setInputImpl (JNIEnv * env, jobject recv,
{
PORT_ACCESS_FROM_ENV (env);
- jbyte *in;
- U_8 *baseAddr;
JCLZipStream *stream = (JCLZipStream *) ((IDATA) handle);
-
- if (stream->inaddr != NULL) /*Input has already been provided, free the old buffer */
+ if (stream->inaddr != NULL) {
+ /* Input has already been provided, free the old buffer. */
jclmem_free_memory (env, stream->inaddr);
- baseAddr = jclmem_allocate_memory (env, len);
- if (baseAddr == NULL)
- {
- throwNewOutOfMemoryError (env, "");
- return;
- }
+ }
+ U_8* baseAddr = jclmem_allocate_memory (env, len);
+ if (baseAddr == NULL) {
+ throwNewOutOfMemoryError (env, "");
+ return;
+ }
stream->inaddr = baseAddr;
stream->stream->next_in = (Bytef *) baseAddr;
stream->stream->avail_in = len;
- in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0));
- if (in == NULL) {
- throwNewOutOfMemoryError(env, "");
- return;
- }
- memcpy (baseAddr, (in + off), len);
- ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT));
- return;
+ (*env)->GetByteArrayRegion(env, buf, off, len, (jbyte*) baseAddr);
}
JNIEXPORT jint JNICALL
diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java
index b025e11..146c679 100644
--- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java
+++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java
@@ -271,14 +271,23 @@ public class ZipFileTest extends junit.framework.TestCase {
Enumeration<? extends ZipEntry> enumeration = zfile.entries();
zfile.close();
- zfile = null;
- boolean pass = false;
+ try {
+ enumeration.nextElement();
+ fail("did not detect closed file");
+ } catch (IllegalStateException expected) {
+ }
+
try {
enumeration.hasMoreElements();
- } catch (IllegalStateException e) {
- pass = true;
+ fail("did not detect closed file");
+ } catch (IllegalStateException expected) {
+ }
+
+ try {
+ zfile.entries();
+ fail("did not detect closed file");
+ } catch (IllegalStateException expected) {
}
- assertTrue("did not detect closed jar file", pass);
}
/**
@@ -349,20 +358,15 @@ public class ZipFileTest extends junit.framework.TestCase {
method = "getEntry",
args = {java.lang.String.class}
)
- @KnownFailure("Android does not throw IllegalStateException when using "
- + "getEntry() after close().")
public void test_getEntryLjava_lang_String_Ex() throws IOException {
java.util.zip.ZipEntry zentry = zfile.getEntry("File1.txt");
assertNotNull("Could not obtain ZipEntry", zentry);
- int r;
- InputStream in;
zfile.close();
try {
- zentry = zfile.getEntry("File2.txt");
- fail("IllegalStateException expected"); // Android fails here!
+ zfile.getEntry("File2.txt");
+ fail("IllegalStateException expected");
} catch (IllegalStateException ee) {
- // expected
}
}
@@ -435,16 +439,13 @@ public class ZipFileTest extends junit.framework.TestCase {
method = "size",
args = {}
)
- @KnownFailure("IllegalStateException not thrown when using ZipFile.size() "
- + "after close().")
public void test_size() throws IOException {
assertEquals(6, zfile.size());
zfile.close();
try {
zfile.size();
- fail("IllegalStateException expected"); // Android fails here!
- } catch (IllegalStateException ee) {
- // expected
+ fail("IllegalStateException expected");
+ } catch (IllegalStateException expected) {
}
}
diff --git a/dalvik/src/main/java/dalvik/system/DexClassLoader.java b/dalvik/src/main/java/dalvik/system/DexClassLoader.java
index 73e6fe4..4a440b5 100644
--- a/dalvik/src/main/java/dalvik/system/DexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/DexClassLoader.java
@@ -114,7 +114,7 @@ public class DexClassLoader extends ClassLoader {
/* open all Zip and DEX files up front */
for (int i = 0; i < length; i++) {
- System.out.println("My path is: " + dexPathList[i]);
+ //System.out.println("My path is: " + dexPathList[i]);
File pathFile = new File(dexPathList[i]);
mFiles[i] = pathFile;
diff --git a/icu/src/main/native/BidiWrapperInterface.c b/icu/src/main/native/BidiWrapperInterface.c
index 2c6b3cd..55be7a0 100644
--- a/icu/src/main/native/BidiWrapperInterface.c
+++ b/icu/src/main/native/BidiWrapperInterface.c
@@ -43,44 +43,34 @@ JNIEXPORT void JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1close
ubidi_close ((*data).pBiDi);
- if ((*data).embeddingLevels != NULL)
- free((*data).embeddingLevels);
- free(data);
+ free((*data).embeddingLevels);
+ free(data);
}
JNIEXPORT void JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1setPara
(JNIEnv * env, jclass clazz, jlong pBiDi, jcharArray text, jint length,
- jbyte paraLevel, jbyteArray embeddingLevels)
+ jbyte paraLevel, jbyteArray newEmbeddingLevels)
{
- UErrorCode err = 0;
- jchar *_text = NULL;
BiDiData *data = (BiDiData *)pBiDi;
- /* Remembering old embedding levels */
- void *embLvls = (*data).embeddingLevels;
-
- _text = (*env)->GetCharArrayElements (env, text, NULL);
-
- if (embeddingLevels != NULL)
- {
- jbyte *el = (*env)->GetByteArrayElements (env, embeddingLevels, NULL);
- (*data).embeddingLevels = malloc(length);
- memcpy(((*data).embeddingLevels), el, length);
- (*env)->ReleaseByteArrayElements (env, embeddingLevels, el, 0);
- } else
- {
- (*data).embeddingLevels = NULL;
- }
+ void *oldEmbeddingLevels = (*data).embeddingLevels;
+
+ // Copy the new embedding levels from the Java heap to the native heap.
+ if (newEmbeddingLevels != NULL) {
+ (*data).embeddingLevels = malloc(length);
+ (*env)->GetByteArrayRegion(env, newEmbeddingLevels, 0, length,
+ (*data).embeddingLevels);
+ } else {
+ (*data).embeddingLevels = NULL;
+ }
+ UErrorCode err = 0;
+ jchar* _text = (*env)->GetCharArrayElements(env, text, NULL);
ubidi_setPara ((*data).pBiDi, _text, length, paraLevel,
((*data).embeddingLevels), &err);
+ (*env)->ReleaseCharArrayElements (env, text, _text, 0);
check_fail (env, err);
- /* Freeing old embedding levels */
- if (embLvls != NULL) {
- free(embLvls);
- }
-
- (*env)->ReleaseCharArrayElements (env, text, _text, 0);
+ free(oldEmbeddingLevels);
}
JNIEXPORT jlong JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1setLine
diff --git a/icu/src/main/native/DecimalFormatInterface.cpp b/icu/src/main/native/DecimalFormatInterface.cpp
index 7e37d6c..252ec33 100644
--- a/icu/src/main/native/DecimalFormatInterface.cpp
+++ b/icu/src/main/native/DecimalFormatInterface.cpp
@@ -29,33 +29,6 @@
#include <string.h>
#include "cutils/log.h"
-
-static UBool icuError(JNIEnv *env, UErrorCode errorcode)
-{
- const char *emsg = u_errorName(errorcode);
- jclass exception;
-
- if (U_FAILURE(errorcode)) {// errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) {
- switch (errorcode) {
- case U_ILLEGAL_ARGUMENT_ERROR :
- exception = env->FindClass("java/lang/IllegalArgumentException");
- break;
- case U_INDEX_OUTOFBOUNDS_ERROR :
- case U_BUFFER_OVERFLOW_ERROR :
- exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
- break;
- case U_UNSUPPORTED_ERROR :
- exception = env->FindClass("java/lang/UnsupportedOperationException");
- break;
- default :
- exception = env->FindClass("java/lang/RuntimeException");
- }
-
- return (env->ThrowNew(exception, emsg) != 0);
- }
- return 0;
-}
-
static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale,
jstring pattern) {
@@ -78,7 +51,7 @@ static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale,
env->ReleaseStringUTFChars(locale, localeChars);
// check for an error
- if ( icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
return 0;
}
@@ -115,8 +88,8 @@ static void setSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol,
// release previously allocated space
env->ReleaseStringChars(text, textChars);
- // check if an error occured
- icuError(env, status);
+ // check if an error occurred
+ icu4jni_error(env, status);
}
static jstring getSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol) {
@@ -144,7 +117,7 @@ static jstring getSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol) {
reslenneeded=unum_getSymbol(fmt, (UNumberFormatSymbol) symbol, result,
resultlength, &status);
}
- if (icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
return NULL;
}
@@ -189,7 +162,7 @@ static void setTextAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol,
env->ReleaseStringChars(text, textChars);
- icuError(env, status);
+ icu4jni_error(env, status);
}
static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr,
@@ -219,7 +192,7 @@ static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr,
(UNumberFormatTextAttribute) symbol, result, resultlength,
&status);
}
- if (icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
return NULL;
}
@@ -246,7 +219,7 @@ static void applyPatternImpl(JNIEnv *env, jclass clazz, jint addr,
env->ReleaseStringChars(pattern, pattChars);
- icuError(env, status);
+ icu4jni_error(env, status);
}
static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr,
@@ -274,7 +247,7 @@ static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr,
reslenneeded=unum_toPattern(fmt, localized, result, resultlength,
&status);
}
- if (icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
return NULL;
}
@@ -335,7 +308,7 @@ static jstring formatLong(JNIEnv *env, jclass clazz, jint addr, jlong value,
res->extract(result, reslenneeded + 1, status);
}
- if (icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
free(attrBuffer->buffer);
free(attrBuffer);
free(result);
@@ -444,7 +417,7 @@ static jstring formatDouble(JNIEnv *env, jclass clazz, jint addr, jdouble value,
res->extract(result, reslenneeded + 1, status);
}
- if (icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
free(attrBuffer->buffer);
free(attrBuffer);
free(result);
@@ -510,7 +483,7 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val
// env->ReleaseStringUTFChars(value, valueUTF);
if (scale < 0) {
- icuError(env, U_ILLEGAL_ARGUMENT_ERROR);
+ icu4jni_error(env, U_ILLEGAL_ARGUMENT_ERROR);
return NULL;
}
@@ -582,7 +555,7 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val
res.extract(result, reslenneeded + 1, status);
- if (icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
if(fieldType != NULL) {
env->ReleaseStringUTFChars(fieldType, fieldName);
}
@@ -671,28 +644,12 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val
static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text,
jobject position) {
-
- const char * textUTF = env->GetStringUTFChars(text, NULL);
- env->ReleaseStringUTFChars(text, textUTF);
-
- const char * parsePositionClassName = "java/text/ParsePosition";
- const char * longClassName = "java/lang/Long";
- const char * doubleClassName = "java/lang/Double";
- const char * bigDecimalClassName = "java/math/BigDecimal";
- const char * bigIntegerClassName = "java/math/BigInteger";
-
- UErrorCode status = U_ZERO_ERROR;
-
- UNumberFormat *fmt = (UNumberFormat *)(int)addr;
-
- jchar *str = (UChar *)env->GetStringChars(text, NULL);
- int strlength = env->GetStringLength(text);
-
- jclass parsePositionClass = env->FindClass(parsePositionClassName);
- jclass longClass = env->FindClass(longClassName);
- jclass doubleClass = env->FindClass(doubleClassName);
- jclass bigDecimalClass = env->FindClass(bigDecimalClassName);
- jclass bigIntegerClass = env->FindClass(bigIntegerClassName);
+ // TODO: cache these?
+ jclass parsePositionClass = env->FindClass("java/text/ParsePosition");
+ jclass longClass = env->FindClass("java/lang/Long");
+ jclass doubleClass = env->FindClass("java/lang/Double");
+ jclass bigDecimalClass = env->FindClass("java/math/BigDecimal");
+ jclass bigIntegerClass = env->FindClass("java/math/BigInteger");
jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass,
"getIndex", "()I");
@@ -707,27 +664,26 @@ static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text,
jmethodID bigIntegerInitMethodID = env->GetMethodID(bigIntegerClass, "<init>", "(Ljava/lang/String;)V");
jmethodID doubleValueMethodID = env->GetMethodID(bigDecimalClass, "doubleValue", "()D");
- bool resultAssigned;
- int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL);
-
// make sure the ParsePosition is valid. Actually icu4c would parse a number
// correctly even if the parsePosition is set to -1, but since the RI fails
// for that case we have to fail too
+ int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL);
+ const int strlength = env->GetStringLength(text);
if(parsePos < 0 || parsePos > strlength) {
return NULL;
}
-
- Formattable res;
-
- const UnicodeString src((UChar*)str, strlength, strlength);
+
ParsePosition pp;
-
pp.setIndex(parsePos);
DigitList digits;
-
+
+ UNumberFormat *fmt = (UNumberFormat *)(int)addr;
+ Formattable res;
+ bool resultAssigned;
+ jchar *str = (UChar *)env->GetStringChars(text, NULL);
+ const UnicodeString src((UChar*)str, strlength, strlength);
((const DecimalFormat*)fmt)->parse(src, resultAssigned, res, pp, FALSE, digits);
-
env->ReleaseStringChars(text, str);
if(pp.getErrorIndex() == -1) {
@@ -738,20 +694,14 @@ static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text,
return NULL;
}
- Formattable::Type numType;
- numType = res.getType();
+ Formattable::Type numType = res.getType();
UErrorCode fmtStatus;
double resultDouble;
long resultLong;
int64_t resultInt64;
- UnicodeString resultString;
jstring resultStr;
- int resLength;
- const char * resultUTF;
jobject resultObject1, resultObject2;
- jdouble doubleTest;
- jchar * result;
if (resultAssigned)
{
@@ -809,7 +759,7 @@ static jint cloneImpl(JNIEnv *env, jclass clazz, jint addr) {
UNumberFormat *result = unum_clone(fmt, &status);
- if(icuError(env, status) != FALSE) {
+ if(icu4jni_error(env, status) != FALSE) {
return 0;
}
diff --git a/icu/src/main/native/ErrorCode.h b/icu/src/main/native/ErrorCode.h
index a5bbfc6..e42a519 100644
--- a/icu/src/main/native/ErrorCode.h
+++ b/icu/src/main/native/ErrorCode.h
@@ -13,6 +13,10 @@
#include "unicode/utypes.h"
#include "unicode/putil.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Checks if an error has occured.
* Throws a generic Java RuntimeException if an error has occured.
@@ -24,4 +28,8 @@
*/
UBool icu4jni_error(JNIEnv *env, UErrorCode errorcode);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/icu/src/main/native/RBNFInterface.cpp b/icu/src/main/native/RBNFInterface.cpp
index d9bf460..c7486e1 100644
--- a/icu/src/main/native/RBNFInterface.cpp
+++ b/icu/src/main/native/RBNFInterface.cpp
@@ -25,32 +25,6 @@
#include <stdlib.h>
#include <string.h>
-static UBool icuError(JNIEnv *env, UErrorCode errorcode)
-{
- const char *emsg = u_errorName(errorcode);
- jclass exception;
-
- if (errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) {
- switch (errorcode) {
- case U_ILLEGAL_ARGUMENT_ERROR :
- exception = env->FindClass("java/lang/IllegalArgumentException");
- break;
- case U_INDEX_OUTOFBOUNDS_ERROR :
- case U_BUFFER_OVERFLOW_ERROR :
- exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
- break;
- case U_UNSUPPORTED_ERROR :
- exception = env->FindClass("java/lang/UnsupportedOperationException");
- break;
- default :
- exception = env->FindClass("java/lang/RuntimeException");
- }
-
- return (env->ThrowNew(exception, emsg) != 0);
- }
- return 0;
-}
-
static jint openRBNFImpl1(JNIEnv* env, jclass clazz,
jint type, jstring locale) {
@@ -72,7 +46,7 @@ static jint openRBNFImpl1(JNIEnv* env, jclass clazz,
} else if(type == 3) {
style = URBNF_COUNT;
} else {
- icuError(env, U_ILLEGAL_ARGUMENT_ERROR);
+ icu4jni_error(env, U_ILLEGAL_ARGUMENT_ERROR);
}
Locale loc = Locale::createFromName(localeChars);
@@ -84,7 +58,7 @@ static jint openRBNFImpl1(JNIEnv* env, jclass clazz,
env->ReleaseStringUTFChars(locale, localeChars);
// check for an error
- if ( icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
return 0;
}
@@ -117,7 +91,7 @@ static jint openRBNFImpl2(JNIEnv* env, jclass clazz,
env->ReleaseStringUTFChars(locale, localeChars);
// check for an error
- if ( icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
return 0;
}
@@ -183,7 +157,7 @@ static jstring formatLongRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jlong va
res.extract(result, reslenneeded + 1, status);
}
- if (icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
free(result);
return NULL;
}
@@ -245,7 +219,7 @@ static jstring formatDoubleRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jdoubl
res.extract(result, reslenneeded + 1, status);
}
- if (icuError(env, status) != FALSE) {
+ if (icu4jni_error(env, status) != FALSE) {
free(result);
return NULL;
}
@@ -265,22 +239,11 @@ static jobject parseRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jstring text,
jobject position, jboolean lenient) {
// LOGI("ENTER parseRBNFImpl");
-
- const char * parsePositionClassName = "java/text/ParsePosition";
- const char * longClassName = "java/lang/Long";
- const char * doubleClassName = "java/lang/Double";
-
-
- UErrorCode status = U_ZERO_ERROR;
-
- UNumberFormat *fmt = (UNumberFormat *)(int)addr;
-
- jchar *str = (UChar *)env->GetStringChars(text, NULL);
- int strlength = env->GetStringLength(text);
-
- jclass parsePositionClass = env->FindClass(parsePositionClassName);
- jclass longClass = env->FindClass(longClassName);
- jclass doubleClass = env->FindClass(doubleClassName);
+
+ // TODO: cache these?
+ jclass parsePositionClass = env->FindClass("java/text/ParsePosition");
+ jclass longClass = env->FindClass("java/lang/Long");
+ jclass doubleClass = env->FindClass("java/lang/Double");
jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass,
"getIndex", "()I");
@@ -292,22 +255,25 @@ static jobject parseRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jstring text,
jmethodID longInitMethodID = env->GetMethodID(longClass, "<init>", "(J)V");
jmethodID dblInitMethodID = env->GetMethodID(doubleClass, "<init>", "(D)V");
- int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL);
-
// make sure the ParsePosition is valid. Actually icu4c would parse a number
// correctly even if the parsePosition is set to -1, but since the RI fails
// for that case we have to fail too
+ int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL);
+ const int strlength = env->GetStringLength(text);
if(parsePos < 0 || parsePos > strlength) {
return NULL;
}
-
+
Formattable res;
-
+
+ jchar *str = (UChar *)env->GetStringChars(text, NULL);
+
const UnicodeString src((UChar*)str, strlength, strlength);
ParsePosition pp;
pp.setIndex(parsePos);
+ UNumberFormat *fmt = (UNumberFormat *)(int)addr;
if(lenient) {
unum_setAttribute(fmt, UNUM_LENIENT_PARSE, JNI_TRUE);
}
@@ -328,35 +294,23 @@ static jobject parseRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jstring text,
return NULL;
}
- Formattable::Type numType;
- numType = res.getType();
- UErrorCode fmtStatus;
-
- double resultDouble;
- long resultLong;
- int64_t resultInt64;
-
- switch(numType) {
- case Formattable::kDouble:
- resultDouble = res.getDouble();
- env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
- return env->NewObject(doubleClass, dblInitMethodID,
- (jdouble) resultDouble);
- case Formattable::kLong:
- resultLong = res.getLong();
- env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
- return env->NewObject(longClass, longInitMethodID,
- (jlong) resultLong);
- case Formattable::kInt64:
- resultInt64 = res.getInt64();
- env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
- return env->NewObject(longClass, longInitMethodID,
- (jlong) resultInt64);
- default:
- break;
+ Formattable::Type numType = res.getType();
+ if (numType == Formattable::kDouble) {
+ double resultDouble = res.getDouble();
+ env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
+ return env->NewObject(doubleClass, dblInitMethodID,
+ (jdouble) resultDouble);
+ } else if (numType == Formattable::kLong) {
+ long resultLong = res.getLong();
+ env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
+ return env->NewObject(longClass, longInitMethodID, (jlong) resultLong);
+ } else if (numType == Formattable::kInt64) {
+ int64_t resultInt64 = res.getInt64();
+ env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos);
+ return env->NewObject(longClass, longInitMethodID, (jlong) resultInt64);
+ } else {
+ return NULL;
}
-
- return NULL;
}
static JNINativeMethod gMethods[] = {
diff --git a/icu/src/main/native/RegExInterface.cpp b/icu/src/main/native/RegExInterface.cpp
index afa5cc4..0ca3d06 100644
--- a/icu/src/main/native/RegExInterface.cpp
+++ b/icu/src/main/native/RegExInterface.cpp
@@ -32,12 +32,12 @@ static jchar EMPTY_STRING = 0;
* character data it refers to (but does not have a copy of), so we can
* manage memory properly.
*/
-typedef struct RegExDataStruct {
+struct RegExData {
// A pointer to the ICU regular expression
URegularExpression* regex;
// A pointer to (a copy of) the input text that *we* manage
jchar* text;
-} RegExData;
+};
static void throwPatternSyntaxException(JNIEnv* env, UErrorCode status,
jstring pattern, UParseError error)
@@ -63,8 +63,8 @@ static void _close(JNIEnv* env, jclass clazz, RegExData* data)
uregex_close(data->regex);
}
- if (data->text != NULL && data->text != &EMPTY_STRING) {
- free(data->text);
+ if (data->text != &EMPTY_STRING) {
+ delete[] data->text;
}
free(data);
@@ -125,8 +125,8 @@ static void setText(JNIEnv* env, jclass clazz, RegExData* data, jstring text)
return;
}
- if (data->text != NULL && data->text != &EMPTY_STRING) {
- free(data->text);
+ if (data->text != &EMPTY_STRING) {
+ delete[] data->text;
data->text = NULL;
}
@@ -134,11 +134,9 @@ static void setText(JNIEnv* env, jclass clazz, RegExData* data, jstring text)
if (textLen == 0) {
data->text = &EMPTY_STRING;
} else {
- jchar const * textRaw = env->GetStringChars(text, NULL);
- data->text = (jchar*)malloc((textLen + 1) * sizeof(jchar));
- memcpy(data->text, textRaw, textLen * sizeof(jchar));
+ data->text = new jchar[textLen + 1];
+ env->GetStringRegion(text, 0, textLen, data->text);
data->text[textLen] = 0;
- env->ReleaseStringChars(text, textRaw);
}
uregex_setText(data->regex, data->text, textLen, &status);
diff --git a/icu/src/main/native/ResourceInterface.cpp b/icu/src/main/native/ResourceInterface.cpp
index a88e15c..731cf3f 100644
--- a/icu/src/main/native/ResourceInterface.cpp
+++ b/icu/src/main/native/ResourceInterface.cpp
@@ -40,32 +40,6 @@
jclass string_class;
-static UBool icuError(JNIEnv *env, UErrorCode errorcode)
-{
- const char *emsg = u_errorName(errorcode);
- jclass exception;
-
- if (U_FAILURE(errorcode)) {
- switch (errorcode) {
- case U_ILLEGAL_ARGUMENT_ERROR :
- exception = env->FindClass("java/lang/IllegalArgumentException");
- break;
- case U_INDEX_OUTOFBOUNDS_ERROR :
- case U_BUFFER_OVERFLOW_ERROR :
- exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
- break;
- case U_UNSUPPORTED_ERROR :
- exception = env->FindClass("java/lang/UnsupportedOperationException");
- break;
- default :
- exception = env->FindClass("java/lang/RuntimeException");
- }
-
- return (env->ThrowNew(exception, emsg) != 0);
- }
- return 0;
-}
-
static Locale getLocale(JNIEnv *env, jstring locale) {
const char *name = env->GetStringUTFChars(locale, NULL);
Locale result = Locale::createFromName(name);
diff --git a/luni-kernel/src/main/java/java/lang/Package.java b/luni-kernel/src/main/java/java/lang/Package.java
index 2817404..4d98959 100644
--- a/luni-kernel/src/main/java/java/lang/Package.java
+++ b/luni-kernel/src/main/java/java/lang/Package.java
@@ -268,8 +268,8 @@ public class Package implements AnnotatedElement {
*/
public boolean isCompatibleWith(String version)
throws NumberFormatException {
- String[] requested = version.split(".");
- String[] provided = specVersion.split(".");
+ String[] requested = version.split("\\.");
+ String[] provided = specVersion.split("\\.");
for (int i = 0; i < Math.min(requested.length, provided.length); i++) {
int reqNum = Integer.parseInt(requested[i]);
@@ -318,4 +318,3 @@ public class Package implements AnnotatedElement {
return "package " + name;
}
}
-
diff --git a/luni-kernel/src/main/java/java/lang/ref/Reference.java b/luni-kernel/src/main/java/java/lang/ref/Reference.java
index ca7290b..c695830 100644
--- a/luni-kernel/src/main/java/java/lang/ref/Reference.java
+++ b/luni-kernel/src/main/java/java/lang/ref/Reference.java
@@ -49,7 +49,7 @@ public abstract class Reference<T> {
* VM requirement: this field <em>must</em> be called "referent"
* and be an object.
*/
- T referent;
+ volatile T referent;
/**
* If non-null, the queue on which this reference will be enqueued
@@ -58,7 +58,7 @@ public abstract class Reference<T> {
* and be a java.lang.ref.ReferenceQueue.
*/
@SuppressWarnings("unchecked")
- ReferenceQueue queue;
+ volatile ReferenceQueue queue;
/**
* Used internally by java.lang.ref.ReferenceQueue.
@@ -66,7 +66,7 @@ public abstract class Reference<T> {
* and be a java.lang.ref.Reference.
*/
@SuppressWarnings("unchecked")
- Reference queueNext;
+ volatile Reference queueNext;
/**
* Used internally by Dalvik.
@@ -74,7 +74,7 @@ public abstract class Reference<T> {
* and be an int.
*/
@SuppressWarnings("unused")
- private int vmData;
+ volatile private int vmData;
/**
* Constructs a new instance of this class.
diff --git a/luni/src/main/java/java/io/PipedInputStream.java b/luni/src/main/java/java/io/PipedInputStream.java
index 83987ec..a6b0336 100644
--- a/luni/src/main/java/java/io/PipedInputStream.java
+++ b/luni/src/main/java/java/io/PipedInputStream.java
@@ -33,7 +33,22 @@ public class PipedInputStream extends InputStream {
private boolean isClosed = false;
/**
- * The circular buffer through which data is passed.
+ * The circular buffer through which data is passed. Data is read from the
+ * range {@code [out, in)} and written to the range {@code [in, out)}.
+ * Data in the buffer is either sequential: <pre>
+ * { - - - X X X X X X X - - - - - }
+ * ^ ^
+ * | |
+ * out in</pre>
+ * ...or wrapped around the buffer's end: <pre>
+ * { X X X X - - - - - - - - X X X }
+ * ^ ^
+ * | |
+ * in out</pre>
+ * When the buffer is empty, {@code in == -1}. Reading when the buffer is
+ * empty will block until data is available. When the buffer is full,
+ * {@code in == out}. Writing when the buffer is full will block until free
+ * space is available.
*/
protected byte buffer[];
@@ -158,10 +173,14 @@ public class PipedInputStream extends InputStream {
throw new IOException(Msg.getString("K0075")); //$NON-NLS-1$
}
- if (lastWriter != null && !lastWriter.isAlive() && (in < 0)) {
- // KA030=Write end dead
- throw new IOException(Msg.getString("KA030")); //$NON-NLS-1$
- }
+ // BEGIN android-removed
+ // eagerly throwing prevents checking isClosed and returning normally
+ // if (lastWriter != null && !lastWriter.isAlive() && (in < 0)) {
+ // // KA030=Write end dead
+ // throw new IOException(Msg.getString("KA030")); //$NON-NLS-1$
+ // }
+ // END android-removed
+
/**
* Set the last thread to be reading on this PipedInputStream. If
* lastReader dies while someone is waiting to write an IOException of
@@ -187,7 +206,8 @@ public class PipedInputStream extends InputStream {
throw new InterruptedIOException();
}
- byte result = buffer[out++];
+ // BEGIN android-changed
+ int result = buffer[out++] & 0xff;
if (out == buffer.length) {
out = 0;
}
@@ -196,7 +216,12 @@ public class PipedInputStream extends InputStream {
in = -1;
out = 0;
}
- return result & 0xff;
+
+ // let blocked writers write to the newly available buffer space
+ notifyAll();
+
+ return result;
+ // END android-changed
}
/**
@@ -261,10 +286,13 @@ public class PipedInputStream extends InputStream {
throw new IOException(Msg.getString("K0075")); //$NON-NLS-1$
}
- if (lastWriter != null && !lastWriter.isAlive() && (in < 0)) {
- // KA030=Write end dead
- throw new IOException(Msg.getString("KA030")); //$NON-NLS-1$
- }
+ // BEGIN android-removed
+ // eagerly throwing prevents checking isClosed and returning normally
+ // if (lastWriter != null && !lastWriter.isAlive() && (in < 0)) {
+ // // KA030=Write end dead
+ // throw new IOException(Msg.getString("KA030")); //$NON-NLS-1$
+ // }
+ // END android-removed
/**
* Set the last thread to be reading on this PipedInputStream. If
@@ -291,13 +319,15 @@ public class PipedInputStream extends InputStream {
throw new InterruptedIOException();
}
- int copyLength = 0;
- /* Copy bytes from out to end of buffer first */
+ // BEGIN android-changed
+ int totalCopied = 0;
+
+ // copy bytes from out thru the end of buffer
if (out >= in) {
- copyLength = count > (buffer.length - out) ? buffer.length - out
- : count;
- System.arraycopy(buffer, out, bytes, offset, copyLength);
- out += copyLength;
+ int leftInBuffer = buffer.length - out;
+ int length = leftInBuffer < count ? leftInBuffer : count;
+ System.arraycopy(buffer, out, bytes, offset, length);
+ out += length;
if (out == buffer.length) {
out = 0;
}
@@ -306,28 +336,29 @@ public class PipedInputStream extends InputStream {
in = -1;
out = 0;
}
+ totalCopied += length;
}
- /*
- * Did the read fully succeed in the previous copy or is the buffer
- * empty?
- */
- if (copyLength == count || in == -1) {
- return copyLength;
+ // copy bytes from out thru in
+ if (totalCopied < count && in != -1) {
+ int leftInBuffer = in - out;
+ int leftToCopy = count - totalCopied;
+ int length = leftToCopy < leftInBuffer ? leftToCopy : leftInBuffer;
+ System.arraycopy(buffer, out, bytes, offset + totalCopied, length);
+ out += length;
+ if (out == in) {
+ // empty buffer
+ in = -1;
+ out = 0;
+ }
+ totalCopied += length;
}
- int bytesCopied = copyLength;
- /* Copy bytes from 0 to the number of available bytes */
- copyLength = in - out > (count - bytesCopied) ? count - bytesCopied
- : in - out;
- System.arraycopy(buffer, out, bytes, offset + bytesCopied, copyLength);
- out += copyLength;
- if (out == in) {
- // empty buffer
- in = -1;
- out = 0;
- }
- return bytesCopied + copyLength;
+ // let blocked writers write to the newly available buffer space
+ notifyAll();
+
+ return totalCopied;
+ // END android-changed
}
/**
@@ -351,9 +382,12 @@ public class PipedInputStream extends InputStream {
if (buffer == null || isClosed) {
throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$
}
- if (lastReader != null && !lastReader.isAlive()) {
- throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
- }
+ // BEGIN android-removed
+ // eagerly throwing causes us to fail even if the buffer's not full
+ // if (lastReader != null && !lastReader.isAlive()) {
+ // throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
+ // }
+ // END android-removed
/**
* Set the last thread to be writing on this PipedInputStream. If
* lastWriter dies while someone is waiting to read an IOException of
@@ -362,11 +396,14 @@ public class PipedInputStream extends InputStream {
lastWriter = Thread.currentThread();
try {
while (buffer != null && out == in) {
- notifyAll();
- wait(1000);
+ // BEGIN android-changed
+ // moved has-last-reader-died check to be before wait()
if (lastReader != null && !lastReader.isAlive()) {
throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$
}
+ notifyAll();
+ wait(1000);
+ // END android-changed
}
} catch (InterruptedException e) {
throw new InterruptedIOException();
@@ -379,7 +416,11 @@ public class PipedInputStream extends InputStream {
if (in == buffer.length) {
in = 0;
}
- return;
+
+ // BEGIN android-added
+ // let blocked readers read the newly available data
+ notifyAll();
+ // END android-added
}
}
diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java
index 33c25f3..0fa4796 100644
--- a/luni/src/main/java/java/net/InetAddress.java
+++ b/luni/src/main/java/java/net/InetAddress.java
@@ -272,7 +272,7 @@ public class InetAddress extends Object implements Serializable {
}
}
- byte[] hBytes = NETIMPL.ipStringToByteArray(host);
+ byte[] hBytes = Inet6Util.createByteArrayFromIPAddressString(host);
if (hBytes.length == 4) {
return (new InetAddress[] { new Inet4Address(hBytes) });
} else if (hBytes.length == 16) {
@@ -301,8 +301,7 @@ public class InetAddress extends Object implements Serializable {
return getAllByNameImpl(host, false)[0];
}
- // BEGIN android-added
- /**
+ /*
* Convenience method to convert a byte array to a string, converting
* exceptions to runtime exceptions. This is used when passing in byte
* arrays that have been verified to be correct and is necessary because
@@ -311,8 +310,6 @@ public class InetAddress extends Object implements Serializable {
* UnknownHostException. Exceptions should never occur when the address is
* valid, but they cannot be simply ignored because they could be due to
* runtime errors such as out-of-memory conditions.
- *
- * @param ipaddress the byte array to convert
*/
private static String ipAddressToString(byte[] ipaddress) {
try {
@@ -321,7 +318,6 @@ public class InetAddress extends Object implements Serializable {
throw new RuntimeException(e);
}
}
- // END android-added
/**
* Gets the textual representation of this IP address.
@@ -739,12 +735,8 @@ public class InetAddress extends Object implements Serializable {
* Returns true if the string is a host name, false if it is an IP Address.
*/
private static boolean isHostName(String value) {
- try {
- NETIMPL.ipStringToByteArray(value);
- return false;
- } catch (UnknownHostException e) {
- return true;
- }
+ return !(Inet6Util.isValidIPV4Address(value) || Inet6Util
+ .isValidIP6Address(value));
}
/**
@@ -1286,6 +1278,173 @@ public class InetAddress extends Object implements Serializable {
return value;
}
+ /**
+ * Creates an InetAddress based on the {@code ipAddressString}. No error
+ * handling is performed here.
+ */
+ static InetAddress createHostNameFromIPAddress(String ipAddressString)
+ throws UnknownHostException {
+
+ InetAddress address = null;
+
+ if (Inet6Util.isValidIPV4Address(ipAddressString)) {
+ byte[] byteAddress = new byte[4];
+ String[] parts = ipAddressString.split("\\."); //$NON-NLS-1$
+ int length = parts.length;
+ if (length == 1) {
+ long value = Long.parseLong(parts[0]);
+ for (int i = 0; i < 4; i++) {
+ byteAddress[i] = (byte) (value >> ((3 - i) * 8));
+ }
+ } else {
+ for (int i = 0; i < length; i++) {
+ byteAddress[i] = (byte) Integer.parseInt(parts[i]);
+ }
+ }
+
+ // adjust for 2/3 parts address
+ if (length == 2) {
+ byteAddress[3] = byteAddress[1];
+ byteAddress[1] = 0;
+ }
+ if (length == 3) {
+ byteAddress[3] = byteAddress[2];
+ byteAddress[2] = 0;
+ }
+
+ address = new Inet4Address(byteAddress);
+ } else { // otherwise it must be ipv6
+
+ if (ipAddressString.charAt(0) == '[') {
+ ipAddressString = ipAddressString.substring(1, ipAddressString
+ .length() - 1);
+ }
+
+ StringTokenizer tokenizer = new StringTokenizer(ipAddressString,
+ ":.%", true); //$NON-NLS-1$
+ ArrayList<String> hexStrings = new ArrayList<String>();
+ ArrayList<String> decStrings = new ArrayList<String>();
+ String scopeString = null;
+ String token = ""; //$NON-NLS-1$
+ String prevToken = ""; //$NON-NLS-1$
+ String prevPrevToken = ""; //$NON-NLS-1$
+ int doubleColonIndex = -1; // If a double colon exists, we need to
+ // insert 0s.
+
+ // Go through the tokens, including the separators ':' and '.'
+ // When we hit a : or . the previous token will be added to either
+ // the hex list or decimal list. In the case where we hit a ::
+ // we will save the index of the hexStrings so we can add zeros
+ // in to fill out the string
+ while (tokenizer.hasMoreTokens()) {
+ prevPrevToken = prevToken;
+ prevToken = token;
+ token = tokenizer.nextToken();
+
+ if (token.equals(":")) { //$NON-NLS-1$
+ if (prevToken.equals(":")) { //$NON-NLS-1$
+ doubleColonIndex = hexStrings.size();
+ } else if (!prevToken.equals("")) { //$NON-NLS-1$
+ hexStrings.add(prevToken);
+ }
+ } else if (token.equals(".")) { //$NON-NLS-1$
+ decStrings.add(prevToken);
+ } else if (token.equals("%")) { //$NON-NLS-1$
+ // add the last word before the % properly
+ if (!prevToken.equals(":") && !prevToken.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$
+ if (prevPrevToken.equals(":")) { //$NON-NLS-1$
+ hexStrings.add(prevToken);
+ } else if (prevPrevToken.equals(".")) { //$NON-NLS-1$
+ decStrings.add(prevToken);
+ }
+ }
+
+ // the rest should be the scope string
+ scopeString = tokenizer.nextToken();
+ while (tokenizer.hasMoreTokens()) {
+ scopeString = scopeString + tokenizer.nextToken();
+ }
+ }
+ }
+
+ if (prevToken.equals(":")) { //$NON-NLS-1$
+ if (token.equals(":")) { //$NON-NLS-1$
+ doubleColonIndex = hexStrings.size();
+ } else {
+ hexStrings.add(token);
+ }
+ } else if (prevToken.equals(".")) { //$NON-NLS-1$
+ decStrings.add(token);
+ }
+
+ // figure out how many hexStrings we should have
+ // also check if it is a IPv4 address
+ int hexStringsLength = 8;
+
+ // If we have an IPv4 address tagged on at the end, subtract
+ // 4 bytes, or 2 hex words from the total
+ if (decStrings.size() > 0) {
+ hexStringsLength -= 2;
+ }
+
+ // if we hit a double Colon add the appropriate hex strings
+ if (doubleColonIndex != -1) {
+ int numberToInsert = hexStringsLength - hexStrings.size();
+ for (int i = 0; i < numberToInsert; i++) {
+ hexStrings.add(doubleColonIndex, "0"); //$NON-NLS-1$
+ }
+ }
+
+ byte ipByteArray[] = new byte[16];
+
+ // Finally convert these strings to bytes...
+ for (int i = 0; i < hexStrings.size(); i++) {
+ Inet6Util.convertToBytes(hexStrings.get(i), ipByteArray, i * 2);
+ }
+
+ // Now if there are any decimal values, we know where they go...
+ for (int i = 0; i < decStrings.size(); i++) {
+ ipByteArray[i + 12] = (byte) (Integer.parseInt(decStrings
+ .get(i)) & 255);
+ }
+
+ // now check to see if this guy is actually and IPv4 address
+ // an ipV4 address is ::FFFF:d.d.d.d
+ boolean ipV4 = true;
+ for (int i = 0; i < 10; i++) {
+ if (ipByteArray[i] != 0) {
+ ipV4 = false;
+ break;
+ }
+ }
+
+ if (ipByteArray[10] != -1 || ipByteArray[11] != -1) {
+ ipV4 = false;
+ }
+
+ if (ipV4) {
+ byte ipv4ByteArray[] = new byte[4];
+ for (int i = 0; i < 4; i++) {
+ ipv4ByteArray[i] = ipByteArray[i + 12];
+ }
+ address = InetAddress.getByAddress(ipv4ByteArray);
+ } else {
+ int scopeId = 0;
+ if (scopeString != null) {
+ try {
+ scopeId = Integer.parseInt(scopeString);
+ } catch (Exception e) {
+ // this should not occur as we should not get into this
+ // function unless the address is in a valid format
+ }
+ }
+ address = InetAddress.getByAddress(ipByteArray, scopeId);
+ }
+ }
+
+ return address;
+ }
+
static boolean preferIPv6Addresses() {
String result = AccessController.doPrivileged(new PriviAction<String>(
"java.net.preferIPv6Addresses")); //$NON-NLS-1$
diff --git a/luni/src/main/java/java/util/Formatter.java b/luni/src/main/java/java/util/Formatter.java
index d1dd417..3d8b94e 100644
--- a/luni/src/main/java/java/util/Formatter.java
+++ b/luni/src/main/java/java/util/Formatter.java
@@ -885,7 +885,6 @@ public final class Formatter implements Closeable, Flushable {
if (transformer == null || ! transformer.locale.equals(l)) {
transformer = new Transformer(this, l);
}
- // END android-changed
int currentObjectIndex = 0;
Object lastArgument = null;
@@ -893,12 +892,13 @@ public final class Formatter implements Closeable, Flushable {
while (formatBuffer.hasRemaining()) {
parser.reset();
FormatToken token = parser.getNextFormatToken();
- String result;
String plainText = token.getPlainText();
if (token.getConversionType() == (char) FormatToken.UNSET) {
- result = plainText;
+ outputCharSequence(plainText);
} else {
plainText = plainText.substring(0, plainText.indexOf('%'));
+ outputCharSequence(plainText);
+
Object argument = null;
if (token.requireArgument()) {
int index = token.getArgIndex() == FormatToken.UNSET ? currentObjectIndex++
@@ -908,21 +908,26 @@ public final class Formatter implements Closeable, Flushable {
lastArgument = argument;
hasLastArgumentSet = true;
}
- result = transformer.transform(token, argument);
- result = (null == result ? plainText : plainText + result);
- }
- // if output is made by formattable callback
- if (null != result) {
- try {
- out.append(result);
- } catch (IOException e) {
- lastIOException = e;
- }
+ outputCharSequence(transformer.transform(token, argument));
}
}
+ // END android-changed
return this;
}
+ // BEGIN android-added
+ // Fixes http://code.google.com/p/android/issues/detail?id=1767.
+ private void outputCharSequence(CharSequence cs) {
+ if (cs != null) {
+ try {
+ out.append(cs);
+ } catch (IOException e) {
+ lastIOException = e;
+ }
+ }
+ }
+ // END android-added
+
private Object getArgument(Object[] args, int index, FormatToken token,
Object lastArgument, boolean hasLastArgumentSet) {
if (index == FormatToken.LAST_ARGUMENT_INDEX && !hasLastArgumentSet) {
@@ -1184,13 +1189,13 @@ public final class Formatter implements Closeable, Flushable {
* Gets the formatted string according to the format token and the
* argument.
*/
- String transform(FormatToken token, Object argument) {
+ CharSequence transform(FormatToken token, Object argument) {
/* init data member to print */
this.formatToken = token;
this.arg = argument;
- String result;
+ CharSequence result;
switch (token.getConversionType()) {
case 'B':
case 'b': {
@@ -1254,7 +1259,7 @@ public final class Formatter implements Closeable, Flushable {
if (Character.isUpperCase(token.getConversionType())) {
if (null != result) {
- result = result.toUpperCase(Locale.US);
+ result = result.toString().toUpperCase(Locale.US);
}
}
return result;
@@ -1263,7 +1268,7 @@ public final class Formatter implements Closeable, Flushable {
/*
* Transforms the Boolean argument to a formatted string.
*/
- private String transformFromBoolean() {
+ private CharSequence transformFromBoolean() {
StringBuilder result = new StringBuilder();
int startIndex = 0;
int flags = formatToken.getFlags();
@@ -1294,7 +1299,7 @@ public final class Formatter implements Closeable, Flushable {
/*
* Transforms the hashcode of the argument to a formatted string.
*/
- private String transformFromHashCode() {
+ private CharSequence transformFromHashCode() {
StringBuilder result = new StringBuilder();
int startIndex = 0;
@@ -1324,7 +1329,7 @@ public final class Formatter implements Closeable, Flushable {
/*
* Transforms the String to a formatted string.
*/
- private String transformFromString() {
+ private CharSequence transformFromString() {
StringBuilder result = new StringBuilder();
int startIndex = 0;
int flags = formatToken.getFlags();
@@ -1374,7 +1379,7 @@ public final class Formatter implements Closeable, Flushable {
/*
* Transforms the Character to a formatted string.
*/
- private String transformFromCharacter() {
+ private CharSequence transformFromCharacter() {
StringBuilder result = new StringBuilder();
int startIndex = 0;
@@ -1434,7 +1439,7 @@ public final class Formatter implements Closeable, Flushable {
* Transforms percent to a formatted string. Only '-' is legal flag.
* Precision is illegal.
*/
- private String transformFromPercent() {
+ private CharSequence transformFromPercent() {
StringBuilder result = new StringBuilder("%"); //$NON-NLS-1$
int startIndex = 0;
@@ -1462,7 +1467,7 @@ public final class Formatter implements Closeable, Flushable {
* Transforms line separator to a formatted string. Any flag, the width
* or the precision is illegal.
*/
- private String transformFromLineSeparator() {
+ private CharSequence transformFromLineSeparator() {
if (formatToken.isPrecisionSet()) {
throw new IllegalFormatPrecisionException(formatToken
.getPrecision());
@@ -1492,7 +1497,7 @@ public final class Formatter implements Closeable, Flushable {
/*
* Pads characters to the formatted string.
*/
- private String padding(StringBuilder source, int startIndex) {
+ private CharSequence padding(StringBuilder source, int startIndex) {
int start = startIndex;
boolean paddingRight = formatToken
.isFlagSet(FormatToken.FLAG_MINUS);
@@ -1520,7 +1525,7 @@ public final class Formatter implements Closeable, Flushable {
width = Math.max(source.length(), width);
}
if (length >= width) {
- return source.toString();
+ return source;
}
char[] paddings = new char[width - length];
@@ -1532,13 +1537,13 @@ public final class Formatter implements Closeable, Flushable {
} else {
source.insert(start, insertString);
}
- return source.toString();
+ return source;
}
/*
* Transforms the Integer to a formatted string.
*/
- private String transformFromInteger() {
+ private CharSequence transformFromInteger() {
int startIndex = 0;
boolean isNegative = false;
StringBuilder result = new StringBuilder();
@@ -1651,7 +1656,7 @@ public final class Formatter implements Closeable, Flushable {
if (isNegative
&& formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) {
result = wrapParentheses(result);
- return result.toString();
+ return result;
}
if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_ZERO)) {
@@ -1680,7 +1685,7 @@ public final class Formatter implements Closeable, Flushable {
return result;
}
- private String transformFromSpecialNumber() {
+ private CharSequence transformFromSpecialNumber() {
String source = null;
if (!(arg instanceof Number) || arg instanceof BigDecimal) {
@@ -1713,12 +1718,12 @@ public final class Formatter implements Closeable, Flushable {
formatToken.setPrecision(FormatToken.UNSET);
formatToken.setFlags(formatToken.getFlags()
& (~FormatToken.FLAG_ZERO));
- source = padding(new StringBuilder(source), 0);
+ return padding(new StringBuilder(source), 0);
}
return source;
}
- private String transformFromNull() {
+ private CharSequence transformFromNull() {
formatToken.setFlags(formatToken.getFlags()
& (~FormatToken.FLAG_ZERO));
return padding(new StringBuilder("null"), 0); //$NON-NLS-1$
@@ -1727,7 +1732,7 @@ public final class Formatter implements Closeable, Flushable {
/*
* Transforms a BigInteger to a formatted string.
*/
- private String transformFromBigInteger() {
+ private CharSequence transformFromBigInteger() {
int startIndex = 0;
boolean isNegative = false;
StringBuilder result = new StringBuilder();
@@ -1817,7 +1822,7 @@ public final class Formatter implements Closeable, Flushable {
if (isNegative
&& formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) {
result = wrapParentheses(result);
- return result.toString();
+ return result;
}
if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_ZERO)) {
@@ -1829,7 +1834,7 @@ public final class Formatter implements Closeable, Flushable {
/*
* Transforms a Float,Double or BigDecimal to a formatted string.
*/
- private String transformFromFloat() {
+ private CharSequence transformFromFloat() {
StringBuilder result = new StringBuilder();
int startIndex = 0;
char currentConversionType = formatToken.getConversionType();
@@ -1883,7 +1888,7 @@ public final class Formatter implements Closeable, Flushable {
currentConversionType, arg.getClass());
}
- String specialNumberResult = transformFromSpecialNumber();
+ CharSequence specialNumberResult = transformFromSpecialNumber();
if (null != specialNumberResult) {
return specialNumberResult;
}
@@ -1896,7 +1901,7 @@ public final class Formatter implements Closeable, Flushable {
}
// output result
FloatUtil floatUtil = new FloatUtil(result, formatToken,
- (DecimalFormat) NumberFormat.getInstance(locale), arg);
+ (DecimalFormat) getNumberFormat(), arg);
floatUtil.transform(formatToken, result);
formatToken.setPrecision(FormatToken.UNSET);
@@ -1904,7 +1909,7 @@ public final class Formatter implements Closeable, Flushable {
if (getDecimalFormatSymbols().getMinusSign() == result.charAt(0)) {
if (formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) {
result = wrapParentheses(result);
- return result.toString();
+ return result;
}
} else {
if (formatToken.isFlagSet(FormatToken.FLAG_SPACE)) {
@@ -1933,7 +1938,7 @@ public final class Formatter implements Closeable, Flushable {
/*
* Transforms a Date to a formatted string.
*/
- private String transformFromDateTime() {
+ private CharSequence transformFromDateTime() {
int startIndex = 0;
char currentConversionType = formatToken.getConversionType();
diff --git a/luni/src/main/java/java/util/HashMap.java b/luni/src/main/java/java/util/HashMap.java
index 28978d4..f79601f 100644
--- a/luni/src/main/java/java/util/HashMap.java
+++ b/luni/src/main/java/java/util/HashMap.java
@@ -36,7 +36,7 @@ import java.io.Serializable;
* @param <V> the type of mapped values
*/
public class HashMap<K, V> extends AbstractMap<K, V>
- implements Cloneable, Serializable, Map<K, V> {
+ implements Cloneable, Serializable {
/**
* Min capacity (other than zero) for a HashMap. Must be a power of two
* greater than 1 (and less than 1 << 30).
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 110a0fd..01319e1 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
@@ -111,12 +111,23 @@ public class ZoneInfo extends TimeZone {
// Subtract the raw offset from all offsets so it can be changed
// and affect them too.
- // Find whether there exist any observances of DST.
-
for (int i = 0; i < mGmtOffs.length; i++) {
mGmtOffs[i] -= mRawOffset;
+ }
- if (mIsDsts[i] != 0) {
+ // Is this zone still observing DST?
+ // We don't care if they've historically used it: most places have at least once.
+ // We want to know whether the last "schedule info" (the unix times in the mTransitions
+ // array) is in the future. If it is, DST is still relevant.
+ // See http://code.google.com/p/android/issues/detail?id=877.
+ // This test means that for somewhere like Morocco, which tried DST in 2009 but has
+ // no future plans (and thus no future schedule info) will report "true" from
+ // useDaylightTime at the start of 2009 but "false" at the end. This seems appropriate.
+ long currentUnixTime = System.currentTimeMillis() / 1000;
+ if (mTransitions.length > 0) {
+ // (We're really dealing with uint32_t values, so long is most convenient in Java.)
+ long latestScheduleTime = mTransitions[mTransitions.length - 1] & 0xffffffff;
+ if (currentUnixTime < latestScheduleTime) {
mUseDst = true;
}
}
diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
index fb47f0d..1345341 100644
--- a/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
+++ b/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
@@ -218,14 +218,9 @@ public interface INetworkSystem {
public void setInetAddress(InetAddress sender, byte[] address);
- // BEGIN android-added
public String byteArrayToIpString(byte[] address)
throws UnknownHostException;
- public byte[] ipStringToByteArray(String address)
- throws UnknownHostException;
- // END android-added
-
// BEGIN android-removed
// public boolean isReachableByICMP(InetAddress dest,InetAddress source,int ttl,int timeout);
// END android-removed
diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
index bd6a609..0fa25ea 100644
--- a/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
+++ b/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
@@ -242,9 +242,6 @@ final class OSNetworkSystem implements INetworkSystem {
public native String byteArrayToIpString(byte[] address)
throws UnknownHostException;
- public native byte[] ipStringToByteArray(String address)
- throws UnknownHostException;
-
static native int getSocketFlagsImpl();
public InetAddress getSocketLocalAddress(FileDescriptor fd,
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java b/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java
index 55914de..4dae1c3 100644
--- a/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java
+++ b/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java
@@ -24,6 +24,244 @@ import java.util.StringTokenizer;
*/
public class Inet6Util {
+ /**
+ * Creates an byte[] based on an ipAddressString. No error handling is
+ * performed here.
+ */
+ public static byte[] createByteArrayFromIPAddressString(
+ String ipAddressString) {
+
+ if (isValidIPV4Address(ipAddressString)) {
+ StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ".");
+ String token = "";
+ int tempInt = 0;
+ byte[] byteAddress = new byte[4];
+ for (int i = 0; i < 4; i++) {
+ token = tokenizer.nextToken();
+ tempInt = Integer.parseInt(token);
+ byteAddress[i] = (byte) tempInt;
+ }
+
+ return byteAddress;
+ }
+
+ if (ipAddressString.charAt(0) == '[') {
+ ipAddressString = ipAddressString.substring(1, ipAddressString.length() - 1);
+ }
+
+ StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ":.", true);
+ ArrayList<String> hexStrings = new ArrayList<String>();
+ ArrayList<String> decStrings = new ArrayList<String>();
+ String token = "";
+ String prevToken = "";
+ // If a double colon exists, we need to insert 0s.
+ int doubleColonIndex = -1;
+
+ /*
+ * Go through the tokens, including the separators ':' and '.' When we
+ * hit a : or . the previous token will be added to either the hex list
+ * or decimal list. In the case where we hit a :: we will save the index
+ * of the hexStrings so we can add zeros in to fill out the string
+ */
+ while (tokenizer.hasMoreTokens()) {
+ prevToken = token;
+ token = tokenizer.nextToken();
+
+ if (token.equals(":")) {
+ if (prevToken.equals(":")) {
+ doubleColonIndex = hexStrings.size();
+ } else if (!prevToken.equals("")) {
+ hexStrings.add(prevToken);
+ }
+ } else if (token.equals(".")) {
+ decStrings.add(prevToken);
+ }
+ }
+
+ if (prevToken.equals(":")) {
+ if (token.equals(":")) {
+ doubleColonIndex = hexStrings.size();
+ } else {
+ hexStrings.add(token);
+ }
+ } else if (prevToken.equals(".")) {
+ decStrings.add(token);
+ }
+
+ // figure out how many hexStrings we should have
+ // also check if it is a IPv4 address
+ int hexStringsLength = 8;
+
+ // If we have an IPv4 address tagged on at the end, subtract
+ // 4 bytes, or 2 hex words from the total
+ if (decStrings.size() > 0) {
+ hexStringsLength -= 2;
+ }
+
+ // if we hit a double Colon add the appropriate hex strings
+ if (doubleColonIndex != -1) {
+ int numberToInsert = hexStringsLength - hexStrings.size();
+ for (int i = 0; i < numberToInsert; i++) {
+ hexStrings.add(doubleColonIndex, "0");
+ }
+ }
+
+ byte ipByteArray[] = new byte[16];
+
+ // Finally convert these strings to bytes...
+ for (int i = 0; i < hexStrings.size(); i++) {
+ convertToBytes(hexStrings.get(i), ipByteArray, i * 2);
+ }
+
+ // Now if there are any decimal values, we know where they go...
+ for (int i = 0; i < decStrings.size(); i++) {
+ ipByteArray[i + 12] = (byte) (Integer.parseInt(decStrings.get(i)) & 255);
+ }
+
+ // now check to see if this guy is actually and IPv4 address
+ // an ipV4 address is ::FFFF:d.d.d.d
+ boolean ipV4 = true;
+ for (int i = 0; i < 10; i++) {
+ if (ipByteArray[i] != 0) {
+ ipV4 = false;
+ break;
+ }
+ }
+
+ if (ipByteArray[10] != -1 || ipByteArray[11] != -1) {
+ ipV4 = false;
+ }
+
+ if (ipV4) {
+ byte ipv4ByteArray[] = new byte[4];
+ for (int i = 0; i < 4; i++) {
+ ipv4ByteArray[i] = ipByteArray[i + 12];
+ }
+ return ipv4ByteArray;
+ }
+
+ return ipByteArray;
+
+ }
+
+ /** Converts a 4 character hex word into a 2 byte word equivalent */
+ public static void convertToBytes(String hexWord, byte ipByteArray[],
+ int byteIndex) {
+
+ int hexWordLength = hexWord.length();
+ int hexWordIndex = 0;
+ ipByteArray[byteIndex] = 0;
+ ipByteArray[byteIndex + 1] = 0;
+ int charValue;
+
+ // high order 4 bits of first byte
+ if (hexWordLength > 3) {
+ charValue = getIntValue(hexWord.charAt(hexWordIndex++));
+ ipByteArray[byteIndex] = (byte) (ipByteArray[byteIndex] | (charValue << 4));
+ }
+
+ // low order 4 bits of the first byte
+ if (hexWordLength > 2) {
+ charValue = getIntValue(hexWord.charAt(hexWordIndex++));
+ ipByteArray[byteIndex] = (byte) (ipByteArray[byteIndex] | charValue);
+ }
+
+ // high order 4 bits of second byte
+ if (hexWordLength > 1) {
+ charValue = getIntValue(hexWord.charAt(hexWordIndex++));
+ ipByteArray[byteIndex + 1] = (byte) (ipByteArray[byteIndex + 1] | (charValue << 4));
+ }
+
+ // low order 4 bits of the first byte
+ charValue = getIntValue(hexWord.charAt(hexWordIndex));
+ ipByteArray[byteIndex + 1] = (byte) (ipByteArray[byteIndex + 1] | charValue & 15);
+ }
+
+ static int getIntValue(char c) {
+
+ switch (c) {
+ case '0':
+ return 0;
+ case '1':
+ return 1;
+ case '2':
+ return 2;
+ case '3':
+ return 3;
+ case '4':
+ return 4;
+ case '5':
+ return 5;
+ case '6':
+ return 6;
+ case '7':
+ return 7;
+ case '8':
+ return 8;
+ case '9':
+ return 9;
+ }
+
+ c = Character.toLowerCase(c);
+ switch (c) {
+ case 'a':
+ return 10;
+ case 'b':
+ return 11;
+ case 'c':
+ return 12;
+ case 'd':
+ return 13;
+ case 'e':
+ return 14;
+ case 'f':
+ return 15;
+ }
+ return 0;
+ }
+
+ private static boolean isIPv4MappedAddress(byte ipAddress[]) {
+
+ // Check if the address matches ::FFFF:d.d.d.d
+ // The first 10 bytes are 0. The next to are -1 (FF).
+ // The last 4 bytes are varied.
+ for (int i = 0; i < 10; i++) {
+ if (ipAddress[i] != 0) {
+ return false;
+ }
+ }
+
+ if (ipAddress[10] != -1 || ipAddress[11] != -1) {
+ return false;
+ }
+
+ return true;
+
+ }
+
+ /**
+ * Takes the byte array and creates an integer out of four bytes starting at
+ * start as the high-order byte. This method makes no checks on the validity
+ * of the parameters.
+ */
+ public static int bytesToInt(byte bytes[], int start) {
+ // First mask the byte with 255, as when a negative
+ // signed byte converts to an integer, it has bits
+ // on in the first 3 bytes, we are only concerned
+ // about the right-most 8 bits.
+ // Then shift the rightmost byte to align with its
+ // position in the integer.
+ int value = ((bytes[start + 3] & 255))
+ | ((bytes[start + 2] & 255) << 8)
+ | ((bytes[start + 1] & 255) << 16)
+ | ((bytes[start] & 255) << 24);
+ return value;
+ }
+
+ public static String addressToString(int value) {
+ return ((value >> 24) & 0xff) + "." + ((value >> 16) & 0xff) + "."
+ + ((value >> 8) & 0xff) + "." + (value & 0xff);
+ }
public static boolean isIP6AddressInFullForm(String ipAddress) {
if (isValidIP6Address(ipAddress)) {
diff --git a/luni/src/main/native/java_net_InetAddress.cpp b/luni/src/main/native/java_net_InetAddress.cpp
index d7b4931..243b63d 100644
--- a/luni/src/main/native/java_net_InetAddress.cpp
+++ b/luni/src/main/native/java_net_InetAddress.cpp
@@ -24,7 +24,6 @@
#include <stdio.h>
#include <string.h>
-#include <assert.h>
#include <netdb.h>
#include <errno.h>
@@ -251,53 +250,40 @@ static jstring InetAddress_gethostbyaddr(JNIEnv* env, jobject obj,
return NULL;
}
- size_t addrlen = env->GetArrayLength(javaAddress);
- jbyte* rawAddress = env->GetByteArrayElements(javaAddress, NULL);
- if (rawAddress == NULL) {
- throwNullPointerException(env);
- return NULL;
- }
-
// Convert the raw address bytes into a socket address structure.
- int ret = 0;
struct sockaddr_storage ss;
- struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
- size_t socklen;
memset(&ss, 0, sizeof(ss));
- switch (addrlen) {
- case 4:
- socklen = sizeof(struct sockaddr_in);
- sin->sin_family = AF_INET;
- memcpy(&sin->sin_addr.s_addr, rawAddress, addrlen);
- env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT);
- break;
- case 16:
- socklen = sizeof(struct sockaddr_in6);
- sin6->sin6_family = AF_INET6;
- memcpy(&sin6->sin6_addr.s6_addr, rawAddress, addrlen);
- env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT);
- break;
- default:
- // The caller already throws an exception in this case. Don't worry
- // about it here.
- env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT);
- return NULL;
+
+ size_t socklen;
+ const size_t addressLength = env->GetArrayLength(javaAddress);
+ if (addressLength == 4) {
+ struct sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss);
+ sin->sin_family = AF_INET;
+ socklen = sizeof(struct sockaddr_in);
+ jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr);
+ env->GetByteArrayRegion(javaAddress, 0, 4, dst);
+ } else if (addressLength == 16) {
+ struct sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6*>(&ss);
+ sin6->sin6_family = AF_INET6;
+ socklen = sizeof(struct sockaddr_in6);
+ jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr);
+ env->GetByteArrayRegion(javaAddress, 0, 16, dst);
+ } else {
+ // The caller already throws an exception in this case. Don't worry
+ // about it here.
+ return NULL;
}
// Look up the host name from the IP address.
char name[NI_MAXHOST];
- if (ret == 0) {
- ret = getnameinfo((struct sockaddr *) &ss, socklen, name, sizeof(name),
- NULL, 0, NI_NAMEREQD);
- }
-
- if (ret == 0) {
- return env->NewStringUTF(name);
+ int ret = getnameinfo(reinterpret_cast<sockaddr*>(&ss), socklen,
+ name, sizeof(name), NULL, 0, NI_NAMEREQD);
+ if (ret != 0) {
+ jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret));
+ return NULL;
}
- jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret));
- return NULL;
+ return env->NewStringUTF(name);
}
/*
diff --git a/luni/src/main/native/java_net_NetworkInterface.c b/luni/src/main/native/java_net_NetworkInterface.c
index 379ccdf..c659ae1 100644
--- a/luni/src/main/native/java_net_NetworkInterface.c
+++ b/luni/src/main/native/java_net_NetworkInterface.c
@@ -241,24 +241,18 @@ static char * netLookupErrorString(int anErrorNum) {
static int structInToJavaAddress(
JNIEnv *env, struct in_addr *address, jbyteArray java_address) {
- if(java_address == NULL) {
+ if (java_address == NULL) {
throwNullPointerException(env);
return -1;
}
- if((*env)->GetArrayLength(env, java_address) != sizeof(address->s_addr)) {
+ if ((*env)->GetArrayLength(env, java_address) != sizeof(address->s_addr)) {
jniThrowIOException(env, errno);
return -1;
}
- jbyte *java_address_bytes;
-
- java_address_bytes = (*env)->GetByteArrayElements(env, java_address, NULL);
-
- memcpy(java_address_bytes, &(address->s_addr), sizeof(address->s_addr));
-
- (*env)->ReleaseByteArrayElements(env, java_address, java_address_bytes, 0);
-
+ jbyte* src = (jbyte*)(&(address->s_addr));
+ (*env)->SetByteArrayRegion(env, java_address, 0, sizeof(address->s_addr), src);
return 0;
}
diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp
index 38f3d36..5614214 100644
--- a/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp
+++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp
@@ -184,40 +184,24 @@ static jint harmony_io_getAllocGranularity(JNIEnv * env, jobject thiz) {
* Signature: (I[J[I[I)J
*/
static jlong harmony_io_readvImpl(JNIEnv *env, jobject thiz, jint fd,
- jintArray jbuffers, jintArray joffsets, jintArray jlengths, jint size) {
-
- jboolean bufsCopied = JNI_FALSE;
- jboolean offsetsCopied = JNI_FALSE;
- jboolean lengthsCopied = JNI_FALSE;
- jint *bufs;
- jint *offsets;
- jint *lengths;
- int i = 0;
- long totalRead = 0;
- struct iovec *vectors = (struct iovec *)malloc(size * sizeof(struct iovec));
- if(vectors == NULL) {
+ jintArray jBuffers, jintArray jOffsets, jintArray jLengths, jint size) {
+ iovec* vectors = new iovec[size];
+ if (vectors == NULL) {
return -1;
}
- bufs = env->GetIntArrayElements(jbuffers, &bufsCopied);
- offsets = env->GetIntArrayElements(joffsets, &offsetsCopied);
- lengths = env->GetIntArrayElements(jlengths, &lengthsCopied);
- while(i < size) {
- vectors[i].iov_base = (void *)((int)(bufs[i]+offsets[i]));
+ jint *buffers = env->GetIntArrayElements(jBuffers, NULL);
+ jint *offsets = env->GetIntArrayElements(jOffsets, NULL);
+ jint *lengths = env->GetIntArrayElements(jLengths, NULL);
+ for (int i = 0; i < size; ++i) {
+ vectors[i].iov_base = (void *)((int)(buffers[i]+offsets[i]));
vectors[i].iov_len = lengths[i];
- i++;
}
- totalRead = readv(fd, vectors, size);
- if(bufsCopied) {
- env->ReleaseIntArrayElements(jbuffers, bufs, JNI_ABORT);
- }
- if(offsetsCopied) {
- env->ReleaseIntArrayElements(joffsets, offsets, JNI_ABORT);
- }
- if(lengthsCopied) {
- env->ReleaseIntArrayElements(jlengths, lengths, JNI_ABORT);
- }
- free(vectors);
- return totalRead;
+ long result = readv(fd, vectors, size);
+ env->ReleaseIntArrayElements(jBuffers, buffers, JNI_ABORT);
+ env->ReleaseIntArrayElements(jOffsets, offsets, JNI_ABORT);
+ env->ReleaseIntArrayElements(jLengths, lengths, JNI_ABORT);
+ delete[] vectors;
+ return result;
}
/*
@@ -226,40 +210,24 @@ static jlong harmony_io_readvImpl(JNIEnv *env, jobject thiz, jint fd,
* Signature: (I[J[I[I)J
*/
static jlong harmony_io_writevImpl(JNIEnv *env, jobject thiz, jint fd,
- jintArray jbuffers, jintArray joffsets, jintArray jlengths, jint size) {
-
- jboolean bufsCopied = JNI_FALSE;
- jboolean offsetsCopied = JNI_FALSE;
- jboolean lengthsCopied = JNI_FALSE;
- jint *bufs;
- jint *offsets;
- jint *lengths;
- int i = 0;
- long totalRead = 0;
- struct iovec *vectors = (struct iovec *)malloc(size * sizeof(struct iovec));
- if(vectors == NULL) {
+ jintArray jBuffers, jintArray jOffsets, jintArray jLengths, jint size) {
+ iovec* vectors = new iovec[size];
+ if (vectors == NULL) {
return -1;
}
- bufs = env->GetIntArrayElements(jbuffers, &bufsCopied);
- offsets = env->GetIntArrayElements(joffsets, &offsetsCopied);
- lengths = env->GetIntArrayElements(jlengths, &lengthsCopied);
- while(i < size) {
- vectors[i].iov_base = (void *)((int)(bufs[i]+offsets[i]));
+ jint *buffers = env->GetIntArrayElements(jBuffers, NULL);
+ jint *offsets = env->GetIntArrayElements(jOffsets, NULL);
+ jint *lengths = env->GetIntArrayElements(jLengths, NULL);
+ for (int i = 0; i < size; ++i) {
+ vectors[i].iov_base = (void *)((int)(buffers[i]+offsets[i]));
vectors[i].iov_len = lengths[i];
- i++;
}
- totalRead = writev(fd, vectors, size);
- if(bufsCopied) {
- env->ReleaseIntArrayElements(jbuffers, bufs, JNI_ABORT);
- }
- if(offsetsCopied) {
- env->ReleaseIntArrayElements(joffsets, offsets, JNI_ABORT);
- }
- if(lengthsCopied) {
- env->ReleaseIntArrayElements(jlengths, lengths, JNI_ABORT);
- }
- free(vectors);
- return totalRead;
+ long result = writev(fd, vectors, size);
+ env->ReleaseIntArrayElements(jBuffers, buffers, JNI_ABORT);
+ env->ReleaseIntArrayElements(jOffsets, offsets, JNI_ABORT);
+ env->ReleaseIntArrayElements(jLengths, lengths, JNI_ABORT);
+ delete[] vectors;
+ return result;
}
/*
@@ -337,16 +305,12 @@ static jlong harmony_io_writeDirectImpl(JNIEnv * env, jobject thiz, jint fd,
static jlong harmony_io_readImpl(JNIEnv * env, jobject thiz, jint fd,
jbyteArray byteArray, jint offset, jint nbytes) {
- jboolean isCopy;
- jbyte *bytes;
- jlong result;
-
if (nbytes == 0) {
return 0;
}
- bytes = env->GetByteArrayElements(byteArray, &isCopy);
-
+ jbyte* bytes = env->GetByteArrayElements(byteArray, NULL);
+ jlong result;
for (;;) {
result = read(fd, (void *) (bytes + offset), (int) nbytes);
@@ -374,7 +338,7 @@ static jlong harmony_io_readImpl(JNIEnv * env, jobject thiz, jint fd,
jniThrowException(env, "java/io/InterruptedIOException",
"Read timed out");
} else {
- jniThrowException(env, "java/io/IOException", strerror(errno));
+ jniThrowException(env, "java/io/IOException", strerror(errno));
}
}
@@ -389,10 +353,8 @@ static jlong harmony_io_readImpl(JNIEnv * env, jobject thiz, jint fd,
static jlong harmony_io_writeImpl(JNIEnv * env, jobject thiz, jint fd,
jbyteArray byteArray, jint offset, jint nbytes) {
- jboolean isCopy;
- jbyte *bytes = env->GetByteArrayElements(byteArray, &isCopy);
+ jbyte* bytes = env->GetByteArrayElements(byteArray, NULL);
jlong result;
-
for (;;) {
result = write(fd, (const char *) bytes + offset, (int) nbytes);
@@ -416,7 +378,7 @@ static jlong harmony_io_writeImpl(JNIEnv * env, jobject thiz, jint fd,
jniThrowException(env, "java/io/InterruptedIOException",
"Write timed out");
} else {
- jniThrowException(env, "java/io/IOException", strerror(errno));
+ jniThrowException(env, "java/io/IOException", strerror(errno));
}
}
@@ -562,11 +524,13 @@ static jint harmony_io_truncateImpl(JNIEnv * env, jobject thiz, jint fd,
static jint harmony_io_openImpl(JNIEnv * env, jobject obj, jbyteArray path,
jint jflags) {
+ if (path == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return -1;
+ }
+
int flags = 0;
int mode = 0;
- jint * portFD;
- jsize length;
- char pathCopy[HyMaxPath];
// BEGIN android-changed
// don't want default permissions to allow global access.
@@ -596,19 +560,14 @@ static jint harmony_io_openImpl(JNIEnv * env, jobject obj, jbyteArray path,
flags = EsTranslateOpenFlags(flags);
- length = env->GetArrayLength (path);
+ jsize length = env->GetArrayLength (path);
length = length < HyMaxPath - 1 ? length : HyMaxPath - 1;
+ char pathCopy[HyMaxPath];
env->GetByteArrayRegion (path, 0, length, (jbyte *)pathCopy);
pathCopy[length] = '\0';
convertToPlatform (pathCopy);
int cc;
-
- if(pathCopy == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- return -1;
- }
-
do {
cc = open(pathCopy, flags, mode);
} while(cc < 0 && errno == EINTR);
@@ -731,44 +690,7 @@ static jint harmony_io_ioctlAvailable(JNIEnv *env, jobject thiz, jint fd) {
*/
static jlong harmony_io_ttyReadImpl(JNIEnv *env, jobject thiz,
jbyteArray byteArray, jint offset, jint nbytes) {
-
- jboolean isCopy;
- jbyte *bytes = env->GetByteArrayElements(byteArray, &isCopy);
- jlong result;
-
- for(;;) {
-
- result = (jlong) read(STDIN_FILENO, (char *)(bytes + offset), (int) nbytes);
-
- if ((result != -1) || (errno != EINTR)) {
- break;
- }
-
- /*
- * If we didn't break above, that means that the read() call
- * returned due to EINTR. We shield Java code from this
- * possibility by trying again. Note that this is different
- * from EAGAIN, which should result in this code throwing
- * an InterruptedIOException.
- */
- }
-
- env->ReleaseByteArrayElements(byteArray, bytes, 0);
-
- if (result == 0) {
- return -1;
- }
-
- if (result == -1) {
- if (errno == EAGAIN) {
- jniThrowException(env, "java/io/InterruptedIOException",
- "Read timed out");
- } else {
- jniThrowException(env, "java/io/IOException", strerror(errno));
- }
- }
-
- return result;
+ return harmony_io_readImpl(env, thiz, STDIN_FILENO, byteArray, offset, nbytes);
}
/*
diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp
index 2e814cc..b1493f8 100644
--- a/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp
+++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp
@@ -144,9 +144,8 @@ static jbyte harmony_nio_getByteImpl(JNIEnv *_env, jobject _this,
*/
static void harmony_nio_getBytesImpl(JNIEnv *_env, jobject _this, jint pointer,
jbyteArray dst, jint offset, jint length) {
- jbyte *dst_ = (jbyte *)_env->GetPrimitiveArrayCritical(dst, (jboolean *)0);
- memcpy(dst_ + offset, (jbyte *)pointer, length);
- _env->ReleasePrimitiveArrayCritical(dst, dst_, 0);
+ jbyte* src = reinterpret_cast<jbyte*>(static_cast<uintptr_t>(pointer));
+ _env->SetByteArrayRegion(dst, offset, length, src);
}
/*
@@ -166,9 +165,8 @@ static void harmony_nio_putByteImpl(JNIEnv *_env, jobject _this, jint pointer,
*/
static void harmony_nio_putBytesImpl(JNIEnv *_env, jobject _this,
jint pointer, jbyteArray src, jint offset, jint length) {
- jbyte *src_ = (jbyte *)_env->GetPrimitiveArrayCritical(src, (jboolean *)0);
- memcpy((jbyte *)pointer, src_ + offset, length);
- _env->ReleasePrimitiveArrayCritical(src, src_, JNI_ABORT);
+ jbyte* dst = reinterpret_cast<jbyte*>(static_cast<uintptr_t>(pointer));
+ _env->GetByteArrayRegion(src, offset, length, dst);
}
static void
diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
index ac2b4e2..6d258e0 100644
--- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
+++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
@@ -14,13 +14,6 @@
* limitations under the License.
*/
-// BEGIN android-changed
-//
-// This file has been substantially reworked in order to provide more IPv6
-// support and to move functionality from Java to native code where it made
-// sense (e.g. when converting between IP addresses, socket structures, and
-// strings, for which there exist fast and robust native implementations).
-
#define LOG_TAG "OSNetworkSystem"
#include "JNIHelp.h"
@@ -33,7 +26,6 @@
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
-#include <arpa/inet.h>
#include <sys/time.h>
#include <stdlib.h>
#include <sys/ioctl.h>
@@ -178,8 +170,6 @@ struct CachedFields {
jclass byte_class;
jmethodID byte_class_init;
jfieldID byte_class_value;
- jclass string_class;
- jmethodID string_class_init;
jclass socketimpl_class;
jfieldID socketimpl_address;
jfieldID socketimpl_port;
@@ -225,6 +215,28 @@ static void throwNullPointerException(JNIEnv *env) {
}
/**
+ * Converts a 4-byte array to a native address structure. Throws a
+ * NullPointerException or an IOException in case of error. This is
+ * signaled by a return value of -1. The normal return value is 0.
+ */
+static int javaAddressToStructIn(
+ JNIEnv *env, jbyteArray java_address, struct in_addr *address) {
+ if (java_address == NULL) {
+ return -1;
+ }
+
+ if (env->GetArrayLength(java_address) != sizeof(address->s_addr)) {
+ return -1;
+ }
+
+ memset(address, 0, sizeof(address));
+
+ jbyte* dst = reinterpret_cast<jbyte*>(&(address->s_addr));
+ env->GetByteArrayRegion(java_address, 0, sizeof(address->s_addr), dst);
+ return 0;
+}
+
+/**
* Converts a native address structure to a Java byte array. Throws a
* NullPointerException or an IOException in case of error. This is
* signaled by a return value of -1. The normal return value is 0.
@@ -234,7 +246,7 @@ static void throwNullPointerException(JNIEnv *env) {
* @exception SocketException the address family is unknown, or out of memory
*
*/
-static jbyteArray socketAddressToByteArray(JNIEnv *env,
+static jbyteArray socketAddressToAddressBytes(JNIEnv *env,
struct sockaddr_storage *address) {
void *rawAddress;
@@ -281,38 +293,9 @@ static int getSocketAddressPort(struct sockaddr_storage *address) {
}
/**
- * Checks whether a socket address structure contains an IPv4-mapped address.
- *
- * @param address the socket address structure to check
- * @return true if address contains an IPv4-mapped address, false otherwise.
- */
-static bool isMappedAddress(sockaddr *address) {
- if (! address || address->sa_family != AF_INET6) {
- return false;
- }
- in6_addr addr = ((sockaddr_in6 *) address)->sin6_addr;
- return (addr.s6_addr32[0] == 0 &&
- addr.s6_addr32[1] == 0 &&
- addr.s6_addr32[2] == htonl(0xffff));
-}
-
-/**
- * Checks whether a 16-byte array represents an IPv4-mapped IPv6 address.
- *
- * @param addressBytes the address to check. Must be 16 bytes long.
- * @return true if address contains an IPv4-mapped address, false otherwise.
- */
-static bool isJavaMappedAddress(jbyte *addressBytes) {
- static const unsigned char mappedBytes[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
- return !memcmp(mappedBytes, addressBytes, sizeof(mappedBytes));
-}
-
-/**
* Converts a native address structure to an InetAddress object.
* Throws a NullPointerException or an IOException in case of
- * error. This is signaled by a return value of -1. The normal
- * return value is 0.
+ * error.
*
* @param sockaddress the sockaddr_storage structure to convert
*
@@ -321,7 +304,7 @@ static bool isJavaMappedAddress(jbyte *addressBytes) {
static jobject socketAddressToInetAddress(JNIEnv *env,
struct sockaddr_storage *sockaddress) {
- jbyteArray byteArray = socketAddressToByteArray(env, sockaddress);
+ jbyteArray byteArray = socketAddressToAddressBytes(env, sockaddress);
if (byteArray == NULL) // Exception has already been thrown.
return NULL;
@@ -330,39 +313,6 @@ static jobject socketAddressToInetAddress(JNIEnv *env,
}
/**
- * Converts an IPv4-mapped IPv6 address to an IPv4 address. Performs no error
- * checking.
- *
- * @param address the address to convert. Must contain an IPv4-mapped address.
- * @param outputAddress the converted address. Will contain an IPv4 address.
- */
-static void convertMappedToIpv4(sockaddr_in6 *sin6, sockaddr_in *sin) {
- memset(sin, 0, sizeof(*sin));
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3];
- sin->sin_port = sin6->sin6_port;
-}
-
-/**
- * Converts an IPv4 address to an IPv4-mapped IPv6 address. Performs no error
- * checking.
- *
- * @param address the address to convert. Must contain an IPv4 address.
- * @param outputAddress the converted address. Will contain an IPv6 address.
- * @param mapUnspecified if true, convert 0.0.0.0 to ::ffff:0:0; if false, to ::
- */
-static void convertIpv4ToMapped(struct sockaddr_in *sin,
- struct sockaddr_in6 *sin6, bool mapUnspecified) {
- memset(sin6, 0, sizeof(*sin6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
- if (sin->sin_addr.s_addr != 0 || mapUnspecified) {
- sin6->sin6_addr.s6_addr32[2] = htonl(0xffff);
- }
- sin6->sin6_port = sin->sin_port;
-}
-
-/**
* Converts an InetAddress object and port number to a native address structure.
* Throws a NullPointerException or a SocketException in case of
* error. This is signaled by a return value of -1. The normal
@@ -377,30 +327,29 @@ static void convertIpv4ToMapped(struct sockaddr_in *sin,
* @exception SocketError if the address family is unknown
*/
static int byteArrayToSocketAddress(JNIEnv *env,
- jbyteArray addressByteArray, int port, sockaddr_storage *sockaddress) {
- if (addressByteArray == NULL) {
- throwNullPointerException(env);
- return EFAULT;
+ jbyteArray addressBytes, int port, sockaddr_storage *sockaddress) {
+ if (addressBytes == NULL) {
+ throwNullPointerException(env);
+ return EFAULT;
}
- size_t addressLength = env->GetArrayLength(addressByteArray);
-
// Convert the IP address bytes to the proper IP address type.
+ size_t addressLength = env->GetArrayLength(addressBytes);
if (addressLength == 4) {
// IPv4 address.
- sockaddr_in *sin = (sockaddr_in *) sockaddress;
+ sockaddr_in *sin = reinterpret_cast<sockaddr_in*>(sockaddress);
memset(sin, 0, sizeof(sockaddr_in));
sin->sin_family = AF_INET;
sin->sin_port = htons(port);
- jbyte *rawBytes = (jbyte *) &sin->sin_addr.s_addr;
- env->GetByteArrayRegion(addressByteArray, 0, 4, rawBytes);
+ jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr);
+ env->GetByteArrayRegion(addressBytes, 0, 4, dst);
} else if (addressLength == 16) {
// IPv6 address.
- sockaddr_in6 *sin6 = (sockaddr_in6 *) sockaddress;
+ sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6*>(sockaddress);
memset(sin6, 0, sizeof(sockaddr_in6));
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(port);
- jbyte *rawBytes = (jbyte *) &sin6->sin6_addr.s6_addr;
- env->GetByteArrayRegion(addressByteArray, 0, 16, rawBytes);
+ jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr);
+ env->GetByteArrayRegion(addressBytes, 0, 16, dst);
} else {
// Unknown address family.
throwSocketException(env, SOCKERR_BADAF);
@@ -419,20 +368,23 @@ static int byteArrayToSocketAddress(JNIEnv *env,
* @param port the port number
* @param sockaddress the sockaddr_storage structure to write to
*
- * @return 0 on success, -1 on failure
+ * @return 0 on success, a system error code on failure
* @throw UnknownHostException if any error occurs
*
* @exception SocketError if the address family is unknown
*/
-static int inetAddressToSocketAddress(JNIEnv *env,
- jobject inetaddress, int port, sockaddr_storage *sockaddress) {
-
+static int inetAddressToSocketAddress(JNIEnv *env, jobject inetaddress,
+ int port, sockaddr_storage *sockaddress) {
// Get the byte array that stores the IP address bytes in the InetAddress.
- jbyteArray addressByteArray;
- addressByteArray = (jbyteArray)env->GetObjectField(inetaddress,
- gCachedFields.iaddr_ipaddress);
+ if (inetaddress == NULL) {
+ throwNullPointerException(env);
+ return EFAULT;
+ }
+ jbyteArray addressBytes =
+ reinterpret_cast<jbyteArray>(env->GetObjectField(inetaddress,
+ gCachedFields.iaddr_ipaddress));
- return byteArrayToSocketAddress(env, addressByteArray, port, sockaddress);
+ return byteArrayToSocketAddress(env, addressBytes, port, sockaddress);
}
/**
@@ -521,84 +473,6 @@ static jstring osNetworkSystem_byteArrayToIpString(JNIEnv *env, jclass clazz,
}
/**
- * Convert a Java string representing an IP address to a Java byte array.
- * The formats accepted are:
- * - IPv4:
- * - 1.2.3.4
- * - 1.2.4
- * - 1.4
- * - 4
- * - IPv6
- * - Compressed form (2001:db8::1)
- * - Uncompressed form (2001:db8:0:0:0:0:0:1)
- * - IPv4-compatible (::192.0.2.0)
- * - With an embedded IPv4 address (2001:db8::192.0.2.0).
- * IPv6 addresses may appear in square brackets.
- *
- * @param addressByteArray the byte array to convert.
- *
- * @return a string with the textual representation of the address.
- *
- * @throws UnknownHostException the IP address was invalid.
- */
-static jbyteArray osNetworkSystem_ipStringToByteArray(JNIEnv *env, jclass clazz,
- jstring javaString) {
- if (javaString == NULL) {
- throwNullPointerException(env);
- }
-
- char ipString[INET6_ADDRSTRLEN];
- int stringLength = env->GetStringUTFLength(javaString);
- env->GetStringUTFRegion(javaString, 0, stringLength, ipString);
-
- // Accept IPv6 addresses (only) in square brackets for compatibility.
- if (ipString[0] == '[' && ipString[stringLength - 1] == ']' &&
- index(ipString, ':') != NULL) {
- memmove(ipString, ipString + 1, stringLength - 2);
- ipString[stringLength - 2] = '\0';
- }
-
- jbyteArray result = NULL;
- sockaddr_in sin;
- addrinfo hints, *res;
- memset(&hints, 0, sizeof(hints));
- hints.ai_flags = AI_NUMERICHOST;
- int ret = getaddrinfo(ipString, NULL, &hints, &res);
- if (ret == 0 && res) {
- // Convert mapped addresses to IPv4 addresses if necessary.
- if (res->ai_family == AF_INET6 && isMappedAddress(res->ai_addr)) {
- convertMappedToIpv4((sockaddr_in6 *) res->ai_addr, &sin);
- result = socketAddressToByteArray(env, (sockaddr_storage *) &sin);
- } else {
- result = socketAddressToByteArray(env,
- (sockaddr_storage *) res->ai_addr);
- }
- } else {
- // For backwards compatibility, deal with address formats that
- // getaddrinfo does not support. For example, 1.2.3, 1.3, and even 3 are
- // valid IPv4 addresses according to the Java API. If getaddrinfo fails,
- // try to use inet_aton.
- if (inet_aton(ipString, &sin.sin_addr)) {
- sin.sin_port = 0;
- sin.sin_family = AF_INET;
- result = socketAddressToByteArray(env, (sockaddr_storage *) &sin);
- }
- }
-
- if (res) {
- freeaddrinfo(res);
- }
-
- if (! result) {
- env->ExceptionClear();
- jniThrowException(env, "java/net/UnknownHostException",
- gai_strerror(ret));
- }
-
- return result;
-}
-
-/**
* Answer a new java.lang.Boolean object.
*
* @param env pointer to the JNI library
@@ -652,22 +526,17 @@ jobject newJavaLangInteger(JNIEnv * env, jint anInt) {
return env->NewObject(tempClass, tempMethod, anInt);
}
-/**
- * Answer a new java.lang.String object.
- *
- * @param env pointer to the JNI library
- * @param anInt the byte[] constructor argument
- *
- * @return the new String
- */
-
-jobject newJavaLangString(JNIEnv * env, jbyteArray bytes) {
- jclass tempClass;
- jmethodID tempMethod;
+// Converts a number of milliseconds to a timeval.
+static timeval toTimeval(long ms) {
+ timeval tv;
+ tv.tv_sec = ms / 1000;
+ tv.tv_usec = (ms - tv.tv_sec*1000) * 1000;
+ return tv;
+}
- tempClass = gCachedFields.string_class;
- tempMethod = gCachedFields.string_class_init;
- return env->NewObject(tempClass, tempMethod, (jbyteArray) bytes);
+// Converts a timeval to a number of milliseconds.
+static long toMs(const timeval& tv) {
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
/**
@@ -684,11 +553,10 @@ jobject newJavaLangString(JNIEnv * env, jbyteArray bytes) {
*/
static int time_msec_clock() {
- struct timeval tp;
+ timeval tp;
struct timezone tzp;
-
gettimeofday(&tp, &tzp);
- return (tp.tv_sec * 1000) + (tp.tv_usec / 1000);
+ return toMs(tp);
}
/**
@@ -1056,53 +924,43 @@ static int getSocketAddressFamily(int socket) {
}
/**
- * A helper method, to set the connect context to a Long object.
+ * Converts an IPv4-mapped IPv6 address to an IPv4 address. Performs no error
+ * checking.
*
- * @param env pointer to the JNI library
- * @param longclass Java Long Object
+ * @param address the address to convert. Must contain an IPv4-mapped address.
+ * @param outputAddress the converted address. Will contain an IPv4 address.
*/
-void setConnectContext(JNIEnv *env,jobject longclass,jbyte * context) {
- jclass descriptorCLS;
- jfieldID descriptorFID;
- descriptorCLS = env->FindClass("java/lang/Long");
- descriptorFID = env->GetFieldID(descriptorCLS, "value", "J");
- env->SetLongField(longclass, descriptorFID, (jlong)((jint)context));
-};
+static void convertMappedToIpv4(sockaddr_storage *address,
+ sockaddr_storage *outputAddress) {
+ memset(outputAddress, 0, sizeof(sockaddr_in));
+ const sockaddr_in6 *sin6 = ((sockaddr_in6 *) address);
+ sockaddr_in *sin = ((sockaddr_in *) outputAddress);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3];
+ sin->sin_port = sin6->sin6_port;
+}
/**
- * A helper method, to get the connect context.
+ * Converts an IPv4 address to an IPv4-mapped IPv6 address. Performs no error
+ * checking.
*
- * @param env pointer to the JNI library
- * @param longclass Java Long Object
+ * @param address the address to convert. Must contain an IPv4 address.
+ * @param outputAddress the converted address. Will contain an IPv6 address.
+ * @param mapUnspecified if true, convert 0.0.0.0 to ::ffff:0:0; if false, to ::
*/
-jbyte *getConnectContext(JNIEnv *env, jobject longclass) {
- jclass descriptorCLS;
- jfieldID descriptorFID;
- descriptorCLS = env->FindClass("java/lang/Long");
- descriptorFID = env->GetFieldID(descriptorCLS, "value", "J");
- return (jbyte*) ((jint)env->GetLongField(longclass, descriptorFID));
-};
-
-// typical ip checksum
-unsigned short ip_checksum(unsigned short* buffer, int size) {
- register unsigned short * buf = buffer;
- register int bufleft = size;
- register unsigned long sum = 0;
-
- while (bufleft > 1) {
- sum = sum + (*buf++);
- bufleft = bufleft - sizeof(unsigned short );
- }
- if (bufleft) {
- sum = sum + (*(unsigned char*)buf);
- }
- sum = (sum >> 16) + (sum & 0xffff);
- sum += (sum >> 16);
-
- return (unsigned short )(~sum);
+static void convertIpv4ToMapped(struct sockaddr_storage *address,
+ struct sockaddr_storage *outputAddress, bool mapUnspecified) {
+ memset(outputAddress, 0, sizeof(struct sockaddr_in6));
+ const struct sockaddr_in *sin = ((struct sockaddr_in *) address);
+ struct sockaddr_in6 *sin6 = ((struct sockaddr_in6 *) outputAddress);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
+ if (sin->sin_addr.s_addr != 0 || mapUnspecified) {
+ sin6->sin6_addr.s6_addr32[2] = htonl(0xffff);
+ }
+ sin6->sin6_port = sin->sin_port;
}
-
/**
* Wrapper for connect() that converts IPv4 addresses to IPv4-mapped IPv6
* addresses if necessary.
@@ -1111,12 +969,11 @@ unsigned short ip_checksum(unsigned short* buffer, int size) {
* @param socketAddress the address to connect to
*/
static int doConnect(int socket, struct sockaddr_storage *socketAddress) {
- sockaddr_storage mappedAddress;
- sockaddr_storage *realAddress;
+ struct sockaddr_storage mappedAddress;
+ struct sockaddr_storage *realAddress;
if (socketAddress->ss_family == AF_INET &&
getSocketAddressFamily(socket) == AF_INET6) {
- convertIpv4ToMapped((sockaddr_in *) socketAddress,
- (sockaddr_in6 *) &mappedAddress, true);
+ convertIpv4ToMapped(socketAddress, &mappedAddress, true);
realAddress = &mappedAddress;
} else {
realAddress = socketAddress;
@@ -1141,8 +998,7 @@ static int doBind(int socket, struct sockaddr_storage *socketAddress) {
struct sockaddr_storage *realAddress;
if (socketAddress->ss_family == AF_INET &&
getSocketAddressFamily(socket) == AF_INET6) {
- convertIpv4ToMapped((sockaddr_in *) socketAddress,
- (sockaddr_in6 *) &mappedAddress, false);
+ convertIpv4ToMapped(socketAddress, &mappedAddress, false);
realAddress = &mappedAddress;
} else {
realAddress = socketAddress;
@@ -1177,9 +1033,8 @@ static int doBind(int socket, struct sockaddr_storage *socketAddress) {
* @return 0, if no errors occurred, otherwise the (negative) error code.
*/
static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr,
- unsigned int timeout, unsigned int step, jbyte *ctxt) {
+ int timeout, unsigned int step, jbyte *ctxt) {
int rc = 0;
- struct timeval passedTimeout;
int errorVal;
socklen_t errorValLen = sizeof(int);
struct selectFDSet *context = NULL;
@@ -1239,13 +1094,13 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr,
* set the timeout value to be used. Because on some unix platforms we
* don't get notified when a socket is closed we only sleep for 100ms
* at a time
+ *
+ * TODO: is this relevant for Android?
*/
- passedTimeout.tv_sec = 0;
if (timeout > 100) {
- passedTimeout.tv_usec = 100 * 1000;
- } else if ((int)timeout >= 0) {
- passedTimeout.tv_usec = timeout * 1000;
+ timeout = 100;
}
+ timeval passedTimeout(toTimeval(timeout));
/* initialize the FD sets for the select */
FD_ZERO(&(context->exceptionSet));
@@ -1259,7 +1114,7 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr,
&(context->readSet),
&(context->writeSet),
&(context->exceptionSet),
- (int)timeout >= 0 ? &passedTimeout : NULL);
+ timeout >= 0 ? &passedTimeout : NULL);
/* if there is at least one descriptor ready to be checked */
if (0 < rc) {
@@ -1401,6 +1256,7 @@ static int interfaceIndexFromMulticastSocket(int socket) {
return -1;
}
+
/**
* Join/Leave the nominated multicast group on the specified socket.
* Implemented by setting the multicast 'add membership'/'drop membership'
@@ -1565,7 +1421,6 @@ static void osNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj,
{&c->integer_class, "java/lang/Integer"},
{&c->boolean_class, "java/lang/Boolean"},
{&c->byte_class, "java/lang/Byte"},
- {&c->string_class, "java/lang/String"},
{&c->socketimpl_class, "java/net/SocketImpl"},
{&c->dpack_class, "java/net/DatagramPacket"}
};
@@ -1587,7 +1442,6 @@ static void osNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj,
{&c->integer_class_init, c->integer_class, "<init>", "(I)V", false},
{&c->boolean_class_init, c->boolean_class, "<init>", "(Z)V", false},
{&c->byte_class_init, c->byte_class, "<init>", "(B)V", false},
- {&c->string_class_init, c->string_class, "<init>", "([B)V", false},
{&c->iaddr_getbyaddress, c->iaddr_class, "getByAddress",
"([B)Ljava/net/InetAddress;", true}
};
@@ -1660,6 +1514,7 @@ static int createSocketFileDescriptor(JNIEnv* env, jobject fileDescriptor,
return sock;
}
+
static void osNetworkSystem_createStreamSocketImpl(JNIEnv* env, jclass clazz,
jobject fileDescriptor, jboolean preferIPv4Stack) {
// LOGD("ENTER createSocketImpl");
@@ -2417,7 +2272,7 @@ static jint osNetworkSystem_receiveDatagramDirectImpl(JNIEnv* env, jclass clazz,
}
if (packet != NULL) {
- jbyteArray addr = socketAddressToByteArray(env, &sockAddr);
+ jbyteArray addr = socketAddressToAddressBytes(env, &sockAddr);
if (addr == NULL) // Exception has already been thrown.
return 0;
int port = getSocketAddressPort(&sockAddr);
@@ -2636,13 +2491,10 @@ static jint osNetworkSystem_receiveStreamImpl(JNIEnv* env, jclass clazz,
int spaceAvailable = env->GetArrayLength(data) - offset;
int localCount = count < spaceAvailable? count : spaceAvailable;
- jboolean isCopy;
- jbyte *body = env->GetByteArrayElements(data, &isCopy);
+ jbyte* body = env->GetByteArrayElements(data, NULL);
// set timeout
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
+ timeval tv(toTimeval(timeout));
setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv,
sizeof(struct timeval));
@@ -2680,8 +2532,7 @@ static jint osNetworkSystem_sendStreamImpl(JNIEnv* env, jclass clazz,
int handle = 0;
int result = 0, sent = 0;
- jboolean isCopy;
- jbyte *message = env->GetByteArrayElements(data, &isCopy);
+ jbyte *message = env->GetByteArrayElements(data, NULL);
// Cap write length to available buf size
int spaceAvailable = env->GetArrayLength(data) - offset;
@@ -2827,111 +2678,87 @@ static jint osNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz,
return sent;
}
-static jint osNetworkSystem_selectImpl(JNIEnv* env, jclass clazz,
- jobjectArray readFDArray, jobjectArray writeFDArray, jint countReadC,
- jint countWriteC, jintArray outFlags, jlong timeout) {
- // LOGD("ENTER selectImpl");
-
- struct timeval timeP;
- int result = 0;
- int size = 0;
- jobject gotFD;
- fd_set *fdset_read,*fdset_write;
- int handle;
- jboolean isCopy ;
- jint *flagArray;
- int val;
- unsigned int time_sec = (unsigned int)timeout/1000;
- unsigned int time_msec = (unsigned int)(timeout%1000);
-
- fdset_read = (fd_set *)malloc(sizeof(fd_set));
- fdset_write = (fd_set *)malloc(sizeof(fd_set));
-
- FD_ZERO(fdset_read);
- FD_ZERO(fdset_write);
-
- for (val = 0; val<countReadC; val++) {
-
- gotFD = env->GetObjectArrayElement(readFDArray,val);
-
- handle = jniGetFDFromFileDescriptor(env, gotFD);
-
- FD_SET(handle, fdset_read);
-
- if (0 > (size - handle)) {
- size = handle;
+static bool initFdSet(JNIEnv* env, jobjectArray fdArray, jint count, fd_set* fdSet, int* maxFd) {
+ for (int i = 0; i < count; ++i) {
+ jobject fileDescriptor = env->GetObjectArrayElement(fdArray, i);
+ if (fileDescriptor == NULL) {
+ return false;
}
- }
-
- for (val = 0; val<countWriteC; val++) {
-
- gotFD = env->GetObjectArrayElement(writeFDArray,val);
-
- handle = jniGetFDFromFileDescriptor(env, gotFD);
-
- FD_SET(handle, fdset_write);
-
- if (0 > (size - handle)) {
- size = handle;
+
+ const int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+ if (fd < 0 || fd > 1024) {
+ LOGE("selectImpl: invalid fd %i", fd);
+ continue;
}
- }
-
- /* the size is the max_fd + 1 */
- size =size + 1;
-
- if (0 > size) {
- result = SOCKERR_FDSET_SIZEBAD;
- } else {
- /* only set when timeout >= 0 (non-block)*/
- if (0 <= timeout) {
-
- timeP.tv_sec = time_sec;
- timeP.tv_usec = time_msec*1000;
-
- result = sockSelect(size, fdset_read, fdset_write, NULL, &timeP);
-
- } else {
- result = sockSelect(size, fdset_read, fdset_write, NULL, NULL);
+
+ FD_SET(fd, fdSet);
+
+ if (fd > *maxFd) {
+ *maxFd = fd;
}
}
+ return true;
+}
- if (0 < result) {
- /*output the result to a int array*/
- flagArray = env->GetIntArrayElements(outFlags, &isCopy);
-
- for (val=0; val<countReadC; val++) {
- gotFD = env->GetObjectArrayElement(readFDArray,val);
-
- handle = jniGetFDFromFileDescriptor(env, gotFD);
-
- if (FD_ISSET(handle,fdset_read)) {
- flagArray[val] = SOCKET_OP_READ;
- } else {
- flagArray[val] = SOCKET_OP_NONE;
- }
+static bool translateFdSet(JNIEnv* env, jobjectArray fdArray, jint count, const fd_set& fdSet, jint* flagArray, size_t offset, jint op) {
+ for (int i = 0; i < count; ++i) {
+ jobject fileDescriptor = env->GetObjectArrayElement(fdArray, i);
+ if (fileDescriptor == NULL) {
+ return false;
}
-
- for (val=0; val<countWriteC; val++) {
-
- gotFD = env->GetObjectArrayElement(writeFDArray,val);
-
- handle = jniGetFDFromFileDescriptor(env, gotFD);
-
- if (FD_ISSET(handle,fdset_write)) {
- flagArray[val+countReadC] = SOCKET_OP_WRITE;
- } else {
- flagArray[val+countReadC] = SOCKET_OP_NONE;
- }
+
+ const int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+ const bool valid = fd >= 0 && fd < 1024;
+
+ if (valid && FD_ISSET(fd, &fdSet)) {
+ flagArray[i + offset] = op;
+ } else {
+ flagArray[i + offset] = SOCKET_OP_NONE;
}
-
- env->ReleaseIntArrayElements(outFlags, flagArray, 0);
}
+ return true;
+}
- free(fdset_write);
- free(fdset_read);
-
- /* return both correct and error result, let java handle the exception*/
- return result;
+static jint osNetworkSystem_selectImpl(JNIEnv* env, jclass clazz,
+ jobjectArray readFDArray, jobjectArray writeFDArray, jint countReadC,
+ jint countWriteC, jintArray outFlags, jlong timeoutMs) {
+ // LOGD("ENTER selectImpl");
+
+ // Initialize the fd_sets.
+ int maxFd = -1;
+ fd_set readFds;
+ fd_set writeFds;
+ FD_ZERO(&readFds);
+ FD_ZERO(&writeFds);
+ bool initialized = initFdSet(env, readFDArray, countReadC, &readFds, &maxFd) &&
+ initFdSet(env, writeFDArray, countWriteC, &writeFds, &maxFd);
+ if (!initialized) {
+ return -1;
+ }
+
+ // Initialize the timeout, if any.
+ timeval tv;
+ timeval* tvp = NULL;
+ if (timeoutMs >= 0) {
+ tv = toTimeval(timeoutMs);
+ tvp = &tv;
+ }
+
+ // Perform the select.
+ int result = sockSelect(maxFd + 1, &readFds, &writeFds, NULL, tvp);
+ if (result < 0) {
+ return result;
+ }
+
+ // Translate the result into the int[] we're supposed to fill in.
+ jint* flagArray = env->GetIntArrayElements(outFlags, NULL);
+ if (flagArray == NULL) {
+ return -1;
+ }
+ bool okay = translateFdSet(env, readFDArray, countReadC, readFds, flagArray, 0, SOCKET_OP_READ) &&
+ translateFdSet(env, writeFDArray, countWriteC, writeFds, flagArray, countReadC, SOCKET_OP_WRITE);
+ env->ReleaseIntArrayElements(outFlags, flagArray, 0);
+ return okay ? 0 : -1;
}
static jobject osNetworkSystem_getSocketLocalAddressImpl(JNIEnv* env,
@@ -3163,7 +2990,7 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz,
throwSocketException(env, convertError(errno));
return NULL;
}
- return newJavaLangInteger(env, timeout.tv_sec * 1000 + timeout.tv_usec/1000);
+ return newJavaLangInteger(env, toMs(timeout));
}
default: {
throwSocketException(env, SOCKERR_OPTUNSUPP);
@@ -3405,9 +3232,7 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz,
}
case JAVASOCKOPT_SO_RCVTIMEOUT: {
- struct timeval timeout;
- timeout.tv_sec = intVal / 1000;
- timeout.tv_usec = (intVal % 1000) * 1000;
+ timeval timeout(toTimeval(intVal));
result = setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof(struct timeval));
if (0 != result) {
@@ -3764,7 +3589,6 @@ static JNINativeMethod gMethods[] = {
{ "setInetAddressImpl", "(Ljava/net/InetAddress;[B)V", (void*) osNetworkSystem_setInetAddressImpl },
{ "inheritedChannelImpl", "()Ljava/nio/channels/Channel;", (void*) osNetworkSystem_inheritedChannelImpl },
{ "byteArrayToIpString", "([B)Ljava/lang/String;", (void*) osNetworkSystem_byteArrayToIpString },
- { "ipStringToByteArray", "(Ljava/lang/String;)[B", (void*) osNetworkSystem_ipStringToByteArray },
};
int register_org_apache_harmony_luni_platform_OSNetworkSystem(JNIEnv* env) {
@@ -3773,4 +3597,3 @@ int register_org_apache_harmony_luni_platform_OSNetworkSystem(JNIEnv* env) {
gMethods,
NELEM(gMethods));
}
-// END android-changed
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java
index cb35324..283c1db 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java
@@ -312,7 +312,7 @@ public class PackageTest extends junit.framework.TestCase {
method = "isCompatibleWith",
args = {java.lang.String.class}
)
- @KnownFailure("isCompatibleWith returns incorrect value.")
+ @KnownFailure("Dalvik packages are always version '0.0'.")
public void test_isCompatibleWithLjava_lang_String() throws Exception {
Package p = getTestPackage("hyts_c.jar", "p.C");
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringBuffer2Test.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringBuffer2Test.java
index 8b03ced..6bc1939 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringBuffer2Test.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringBuffer2Test.java
@@ -414,7 +414,6 @@ public class StringBuffer2Test extends junit.framework.TestCase {
method = "ensureCapacity",
args = {int.class}
)
- @KnownFailure("Google TODO 1481226")
public void test_ensureCapacityI() {
// Test for method void java.lang.StringBuffer.ensureCapacity(int)
StringBuffer sb = new StringBuffer(10);
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet4AddressTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet4AddressTest.java
index 163db31..920f137 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet4AddressTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet4AddressTest.java
@@ -17,6 +17,7 @@
package org.apache.harmony.luni.tests.java.net;
+import dalvik.annotation.KnownFailure;
import dalvik.annotation.TestTargetClass;
import dalvik.annotation.TestTargets;
import dalvik.annotation.TestLevel;
@@ -483,6 +484,12 @@ public class Inet4AddressTest extends junit.framework.TestCase {
method = "equals",
args = {java.lang.Object.class}
)
+ @KnownFailure("127.0.0 is not recognized as a valid IP address. " +
+ "Unfortunately, despite the fact that these IP address formats " +
+ "have been the cause of numerous phishing and security " +
+ "vulnerabilities in the past, and most other languages refuse " +
+ "them, the RI documentation explicitly specifies that they are " +
+ "supported. Fix the code to support these.")
public void test_equals() throws Exception {
InetAddress addr = Inet4Address.getByName("239.191.255.255");
assertTrue(addr.equals(addr));
@@ -501,6 +508,7 @@ public class Inet4AddressTest extends junit.framework.TestCase {
method = "getHostAddress",
args = {}
)
+ @KnownFailure("1, 1.1 and 1.1.1 are not recognized as valid IP addresses.")
public void test_getHostAddress() throws Exception {
InetAddress addr = Inet4Address.getByName("localhost");
assertEquals("127.0.0.1", addr.getHostAddress());
@@ -527,6 +535,7 @@ public class Inet4AddressTest extends junit.framework.TestCase {
method = "hashCode",
args = {}
)
+ @KnownFailure("1.1 and 1.1.1 are not recognized as valid IP addresses.")
public void test_hashCode() throws Exception {
InetAddress addr1 = Inet4Address.getByName("1.1");
InetAddress addr2 = Inet4Address.getByName("1.1.1");
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet6AddressTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet6AddressTest.java
index 98ae45e..62952d9 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet6AddressTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet6AddressTest.java
@@ -17,6 +17,7 @@
package org.apache.harmony.luni.tests.java.net;
+import dalvik.annotation.KnownFailure;
import dalvik.annotation.TestTargetClass;
import dalvik.annotation.TestLevel;
import dalvik.annotation.TestTargetNew;
@@ -1152,6 +1153,12 @@ public class Inet6AddressTest extends junit.framework.TestCase {
method = "equals",
args = {java.lang.Object.class}
)
+ @KnownFailure("127.0.0 is not recognized as a valid IP address. " +
+ "Unfortunately, despite the fact that these IP address formats " +
+ "have been the cause of numerous phishing and security " +
+ "vulnerabilities in the past, and most other languages refuse " +
+ "them, the RI documentation explicitly specifies that they are " +
+ "supported. Fix the code to support these.")
public void test_equals() throws Exception {
InetAddress addr = Inet6Address.getByName("239.191.255.255");
assertTrue(addr.equals(addr));
@@ -1170,6 +1177,7 @@ public class Inet6AddressTest extends junit.framework.TestCase {
method = "getHostAddress",
args = {}
)
+ @KnownFailure("1, 1.1 and 1.1.1 are not recognized as valid IP addresses.")
public void test_getHostAddress() throws Exception {
InetAddress aAddr = Inet6Address.getByName("localhost");
assertEquals("127.0.0.1", aAddr.getHostAddress());
@@ -1193,9 +1201,7 @@ public class Inet6AddressTest extends junit.framework.TestCase {
0x25, (byte) 0xFF, (byte) 0xFE, (byte) 0xF8, (byte) 0x7C,
(byte) 0xB2 };
aAddr = Inet6Address.getByAddress(bAddr);
- String aString = aAddr.getHostAddress();
- assertTrue(aString.equals("fe80:0:0:0:211:25ff:fef8:7cb2") ||
- aString.equals("fe80::211:25ff:fef8:7cb2"));
+ assertEquals("fe80:0:0:0:211:25ff:fef8:7cb2", aAddr.getHostAddress());
byte[] cAddr = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
@@ -1209,9 +1215,7 @@ public class Inet6AddressTest extends junit.framework.TestCase {
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
aAddr = Inet6Address.getByAddress(dAddr);
- aString = aAddr.getHostAddress();
- assertTrue(aString.equals("0:0:0:0:0:0:0:0") ||
- aString.equals("::"));
+ assertEquals("0:0:0:0:0:0:0:0", aAddr.getHostAddress());
byte[] eAddr = { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
(byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
@@ -1234,6 +1238,7 @@ public class Inet6AddressTest extends junit.framework.TestCase {
method = "hashCode",
args = {}
)
+ @KnownFailure("1.1 and 1.1.1 are not recognized as valid IP addresses.")
public void test_hashCode() throws Exception {
InetAddress addr1 = Inet6Address.getByName("1.1");
InetAddress addr2 = Inet6Address.getByName("1.1.1");
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java
index 5635ecb..6087a46 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java
@@ -18,6 +18,7 @@
package org.apache.harmony.luni.tests.java.net;
import dalvik.annotation.BrokenTest;
+import dalvik.annotation.KnownFailure;
import dalvik.annotation.TestTargetClass;
import dalvik.annotation.TestTargets;
import dalvik.annotation.TestLevel;
@@ -254,6 +255,12 @@ public class InetAddressTest extends junit.framework.TestCase {
method = "getByName",
args = {java.lang.String.class}
)
+ @KnownFailure("1.2.3 and 1.2 are not recognized as valid IPv4 addresses. " +
+ "Unfortunately, despite the fact that these IP address formats " +
+ "have been the cause of numerous phishing and security " +
+ "vulnerabilities in the past, and most other languages refuse " +
+ "them, the RI documentation explicitly specifies that they are " +
+ "supported. Fix the code to support these.")
public void test_getByNameLjava_lang_String() throws Exception {
// Test for method java.net.InetAddress
// java.net.InetAddress.getByName(java.lang.String)
diff --git a/luni/src/test/java/tests/api/java/util/FormatterTest.java b/luni/src/test/java/tests/api/java/util/FormatterTest.java
index b2030c9..6f86818 100644
--- a/luni/src/test/java/tests/api/java/util/FormatterTest.java
+++ b/luni/src/test/java/tests/api/java/util/FormatterTest.java
@@ -800,6 +800,36 @@ public class FormatterTest extends TestCase {
}
}
+ @TestTargetNew(
+ level = TestLevel.COMPLETE,
+ notes = "Tests that supplying a Formattable works. See http://code.google.com/p/android/issues/detail?id=1767.",
+ method = "format",
+ args = {}
+ )
+ public void test_Formattable() {
+ Formattable ones = new Formattable() {
+ public void formatTo(Formatter formatter, int flags, int width, int precision) throws IllegalFormatException {
+ try {
+ formatter.out().append("111");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ Formattable twos = new Formattable() {
+ public void formatTo(Formatter formatter, int flags, int width, int precision) throws IllegalFormatException {
+ try {
+ formatter.out().append("222");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ assertEquals("aaa 111?", new Formatter().format("aaa %s?", ones).toString());
+ assertEquals("aaa 111 bbb 222?", new Formatter().format("aaa %s bbb %s?", ones, twos).toString());
+ }
+
/**
* @tests java.util.Formatter#out()
*/
diff --git a/luni/src/test/java/tests/api/java/util/TimeZoneTest.java b/luni/src/test/java/tests/api/java/util/TimeZoneTest.java
index efdb8a1..75e9ae3 100644
--- a/luni/src/test/java/tests/api/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/tests/api/java/util/TimeZoneTest.java
@@ -383,6 +383,19 @@ public class TimeZoneTest extends junit.framework.TestCase {
@TestTargetNew(
level = TestLevel.COMPLETE,
notes = "",
+ method = "useDaylightTime",
+ args = {}
+ )
+ public void test_useDaylightTime() {
+ // http://code.google.com/p/android/issues/detail?id=877
+
+ TimeZone asiaTaipei = TimeZone.getTimeZone("Asia/Taipei");
+ assertFalse("Taiwan doesn't use DST", asiaTaipei.useDaylightTime());
+ }
+
+ @TestTargetNew(
+ level = TestLevel.COMPLETE,
+ notes = "",
method = "setID",
args = {java.lang.String.class}
)
diff --git a/math/src/main/java/java/math/BigInt.java b/math/src/main/java/java/math/BigInt.java
index cb7990a..581c22f 100644
--- a/math/src/main/java/java/math/BigInt.java
+++ b/math/src/main/java/java/math/BigInt.java
@@ -225,12 +225,6 @@ class BigInt
return a;
}
- public byte[] bigEndianTwosComplement() {
- byte[] a = NativeBN.bn2twosComp(this.bignum, null);
- return a;
- }
-
-
public int sign() {
return NativeBN.sign(this.bignum);
}
diff --git a/openssl/src/main/java/org/openssl/NativeBN.java b/openssl/src/main/java/org/openssl/NativeBN.java
index 44464e0..3597e3c 100644
--- a/openssl/src/main/java/org/openssl/NativeBN.java
+++ b/openssl/src/main/java/org/openssl/NativeBN.java
@@ -79,9 +79,6 @@ public class NativeBN {
public static native int[] bn2litEndInts(int a, int[] to);
- public static native byte[] bn2twosComp(int a, byte[] to);
-
-
public static native int sign(int a);
// Returns -1, 0, 1 AND NOT boolean.
// #define BN_is_negative(a) ((a)->neg != 0)
diff --git a/openssl/src/main/native/BNInterface.c b/openssl/src/main/native/BNInterface.c
index 4132e4f..1a3eb16 100644
--- a/openssl/src/main/native/BNInterface.c
+++ b/openssl/src/main/native/BNInterface.c
@@ -33,12 +33,6 @@
static void
-throwOutOfMemoryException(JNIEnv* env, const char* message)
-{
- jniThrowException(env, "java/lang/OutOfMemoryError", message);
-}
-
-static void
throwNewNullPointerException (JNIEnv* env, const char* message)
{
jniThrowException(env, "java/lang/NullPointerException", message);
@@ -456,27 +450,6 @@ static jintArray NativeBN_bn2litEndInts(JNIEnv* env, jclass cls, BIGNUM* a, jint
}
}
-/**
- * public static native byte[] bn2twosComp(int, byte[])
- */
-static jbyteArray NativeBN_bn2twosComp(JNIEnv* env, jclass cls, BIGNUM* a, jbyteArray to) {
- if (!oneValidHandle(env, a)) return NULL;
- jbyteArray returnJBytes = to;
- unsigned char * tmpBytes;
- int len, byteCnt;
- byteCnt = BN_num_bytes(a);
-// FIXME: Currently ignoring array passed in to:
- returnJBytes = (*env)->NewByteArray(env, byteCnt);
-// FIXME: is it neccessary to check for returnJBytes != NULL?
- tmpBytes = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, returnJBytes, NULL));
- if (tmpBytes != NULL) {
- len = BN_bn2bin(a, tmpBytes);
- (*env)->ReleasePrimitiveArrayCritical(env, returnJBytes, tmpBytes, 0);
- return returnJBytes;
- }
- else return NULL;
-}
-
/**
* public static native int sign(int)
@@ -833,7 +806,6 @@ static JNINativeMethod METHODS[] = {
{ "BN_bn2hex", "(I)Ljava/lang/String;", (void*)NativeBN_BN_bn2hex },
{ "BN_bn2bin", "(I[B)[B", (void*)NativeBN_BN_bn2bin },
{ "bn2litEndInts", "(I[I)[I", (void*)NativeBN_bn2litEndInts },
- { "bn2twosComp", "(I[B)[B", (void*)NativeBN_bn2twosComp },
{ "sign", "(I)I", (void*)NativeBN_sign },
{ "BN_set_negative", "(II)V", (void*)NativeBN_BN_set_negative },
{ "twosCompFitsIntoBytes", "(II)Z", (void*)NativeBN_twosCompFitsIntoBytes },
diff --git a/regex/src/main/java/java/util/regex/MatchResult.java b/regex/src/main/java/java/util/regex/MatchResult.java
index fa67ba6..76c17a8 100644
--- a/regex/src/main/java/java/util/regex/MatchResult.java
+++ b/regex/src/main/java/java/util/regex/MatchResult.java
@@ -1,17 +1,18 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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
*
- * 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
*
- * 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.
+ * 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 java.util.regex;
@@ -22,91 +23,75 @@ package java.util.regex;
* pair of parentheses in the regular expression and an additional group for
* the whole regular expression. The start, end, and contents of each group
* can be queried.
- *
+ *
* @see Matcher
* @see Matcher#toMatchResult()
- *
- * @since Android 1.0
*/
public interface MatchResult {
/**
* Returns the index of the first character following the text that matched
- * the whole regular expression.
- *
+ * the whole regular expression.
+ *
* @return the character index.
- *
- * @since Android 1.0
*/
int end();
/**
* Returns the index of the first character following the text that matched
* a given group.
- *
+ *
* @param group
* the group, ranging from 0 to groupCount() - 1, with 0
* representing the whole pattern.
- *
+ *
* @return the character index.
- *
- * @since Android 1.0
*/
int end(int group);
/**
- * Returns the text that matched the whole regular expression.
- *
+ * Returns the text that matched the whole regular expression.
+ *
* @return the text.
- *
- * @since Android 1.0
*/
String group();
/**
* Returns the text that matched a given group of the regular expression.
- *
+ *
* @param group
* the group, ranging from 0 to groupCount() - 1, with 0
* representing the whole pattern.
- *
+ *
* @return the text that matched the group.
- *
- * @since Android 1.0
*/
String group(int group);
/**
* Returns the number of groups in the result, which is always equal to
* the number of groups in the original regular expression.
- *
+ *
* @return the number of groups.
- *
- * @since Android 1.0
*/
int groupCount();
/**
* Returns the index of the first character of the text that matched
- * the whole regular expression.
- *
+ * the whole regular expression.
+ *
* @return the character index.
- *
- * @since Android 1.0
*/
int start();
/**
* Returns the index of the first character of the text that matched a given
* group.
- *
+ *
* @param group
* the group, ranging from 0 to groupCount() - 1, with 0
* representing the whole pattern.
- *
+ *
* @return the character index.
- *
- * @since Android 1.0
*/
int start(int group);
}
diff --git a/regex/src/main/java/java/util/regex/Matcher.java b/regex/src/main/java/java/util/regex/Matcher.java
index e3e4874..be5c782 100644
--- a/regex/src/main/java/java/util/regex/Matcher.java
+++ b/regex/src/main/java/java/util/regex/Matcher.java
@@ -44,8 +44,6 @@ import com.ibm.icu4jni.regex.NativeRegEx;
* {@code Pattern} was successful and at which position the next attempt would
* resume the search. Depending on the application's needs, it may become
* necessary to explicitly {@link #reset()} this state from time to time.
- *
- * @since Android 1.0
*/
public final class Matcher implements MatchResult {
@@ -128,39 +126,99 @@ public final class Matcher implements MatchResult {
}
/**
+ * Appends a literal part of the input plus a replacement for the current
+ * match to a given {@link StringBuffer}. The literal part is exactly the
+ * part of the input between the previous match and the current match. The
+ * method can be used in conjunction with {@link #find()} and
+ * {@link #appendTail(StringBuffer)} to walk through the input and replace
+ * all occurrences of the {@code Pattern} with something else.
+ *
+ * @param buffer
+ * the {@code StringBuffer} to append to.
+ * @param replacement
+ * the replacement text.
+ * @return the {@code Matcher} itself.
+ * @throws IllegalStateException
+ * if no successful match has been made.
+ */
+ public Matcher appendReplacement(StringBuffer buffer, String replacement) {
+ buffer.append(input.substring(appendPos, start()));
+ appendEvaluated(buffer, replacement);
+ appendPos = end();
+
+ return this;
+ }
+
+ /**
+ * Internal helper method to append a given string to a given string buffer.
+ * If the string contains any references to groups, these are replaced by
+ * the corresponding group's contents.
+ *
+ * @param buffer
+ * the string buffer.
+ * @param s
+ * the string to append.
+ */
+ private void appendEvaluated(StringBuffer buffer, String s) {
+ boolean escape = false;
+ boolean dollar = false;
+
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == '\\' && !escape) {
+ escape = true;
+ } else if (c == '$' && !escape) {
+ dollar = true;
+ } else if (c >= '0' && c <= '9' && dollar) {
+ buffer.append(group(c - '0'));
+ dollar = false;
+ } else {
+ buffer.append(c);
+ dollar = false;
+ escape = false;
+ }
+ }
+
+ // This seemingly stupid piece of code reproduces a JDK bug.
+ if (escape) {
+ throw new ArrayIndexOutOfBoundsException(s.length());
+ }
+ }
+
+ /**
* Resets the Matcher. A new input sequence and a new region can be
* specified. Results of a previous find get lost. The next attempt to find
* an occurrence of the Pattern in the string will start at the beginning of
* the region. This is the internal version of reset() to which the several
* public versions delegate.
- *
+ *
* @param input
* the input sequence.
* @param start
* the start of the region.
* @param end
* the end of the region.
- *
+ *
* @return the matcher itself.
*/
private Matcher reset(CharSequence input, int start, int end) {
if (input == null) {
throw new IllegalArgumentException();
}
-
- if (start < 0 || end < 0 || start > input.length() ||
+
+ if (start < 0 || end < 0 || start > input.length() ||
end > input.length() || start > end) {
throw new IllegalArgumentException();
}
// Maybe should have a reset() here, but it makes thing worse...
// NativeRegEx.reset(nativePattern, 0);
-
+
if (!input.equals(this.input)) {
this.input = input.toString();
-
+
NativeRegEx.setText(nativePattern, this.input);
-
+
regionStart = 0;
regionEnd = input.length();
}
@@ -176,22 +234,8 @@ public final class Matcher implements MatchResult {
matchFound = false;
findPos = regionStart;
appendPos = 0;
-
- return this;
- }
- /**
- * Resets the {@code Matcher}. This results in the region being set to the
- * whole input. Results of a previous find get lost. The next attempt to
- * find an occurrence of the {@link Pattern} in the string will start at the
- * beginning of the input.
- *
- * @return the {@code Matcher} itself.
- *
- * @since Android 1.0
- */
- public Matcher reset() {
- return reset(input, 0, input.length());
+ return this;
}
/**
@@ -204,73 +248,24 @@ public final class Matcher implements MatchResult {
* the new input sequence.
*
* @return the {@code Matcher} itself.
- *
- * @since Android 1.0
*/
public Matcher reset(CharSequence input) {
return reset(input, 0, input.length());
}
/**
- * Sets a new pattern for the {@code Matcher}. Results of a previous find
- * get lost. The next attempt to find an occurrence of the {@link Pattern}
- * in the string will start at the beginning of the input.
- *
- * @param pattern
- * the new {@code Pattern}.
- *
+ * Resets the {@code Matcher}. This results in the region being set to the
+ * whole input. Results of a previous find get lost. The next attempt to
+ * find an occurrence of the {@link Pattern} in the string will start at the
+ * beginning of the input.
+ *
* @return the {@code Matcher} itself.
- *
- * @since Android 1.0
*/
- public Matcher usePattern(Pattern pattern) {
- if (pattern == null) {
- throw new IllegalArgumentException();
- }
-
- this.pattern = pattern;
-
- if (nativePattern != 0) {
- NativeRegEx.close(nativePattern);
- }
- nativePattern = NativeRegEx.clone(pattern.mNativePattern);
-
- if (input != null) {
- NativeRegEx.setText(nativePattern, input);
- NativeRegEx.setRegion(nativePattern, regionStart, regionEnd);
- NativeRegEx.useAnchoringBounds(nativePattern, anchoringBounds);
- NativeRegEx.useTransparentBounds(nativePattern, transparentBounds);
- }
-
- matchOffsets = new int[(this.pattern.mGroupCount + 1) * 2];
- matchFound = false;
- return this;
- }
-
- /**
- * Returns the {@link Pattern} instance used inside this matcher.
- *
- * @return the {@code Pattern} instance.
- *
- * @since Android 1.0
- */
- public Pattern pattern() {
- return pattern;
+ public Matcher reset() {
+ return reset(input, 0, input.length());
}
/**
- * Returns the number of groups in the results, which is always equal to
- * the number of groups in the original regular expression.
- *
- * @return the number of groups.
- *
- * @since Android 1.0
- */
- public int groupCount() {
- return pattern.mGroupCount;
- }
-
- /**
* Resets this matcher and sets a region. Only characters inside the region
* are considered for a match.
*
@@ -279,109 +274,150 @@ public final class Matcher implements MatchResult {
* @param end
* the first character after the end of the region.
* @return the {@code Matcher} itself.
- * @since Android 1.0
*/
public Matcher region(int start, int end) {
return reset(input, start, end);
}
+
/**
- * Returns this matcher's region start, that is, the first character that is
- * considered for a match.
- *
- * @return the start of the region.
- * @since Android 1.0
+ * Appends the (unmatched) remainder of the input to the given
+ * {@link StringBuffer}. The method can be used in conjunction with
+ * {@link #find()} and {@link #appendReplacement(StringBuffer, String)} to
+ * walk through the input and replace all matches of the {@code Pattern}
+ * with something else.
+ *
+ * @param buffer
+ * the {@code StringBuffer} to append to.
+ * @return the {@code StringBuffer}.
+ * @throws IllegalStateException
+ * if no successful match has been made.
*/
- public int regionStart() {
- return regionStart;
+ public StringBuffer appendTail(StringBuffer buffer) {
+ if (appendPos < regionEnd) {
+ buffer.append(input.substring(appendPos, regionEnd));
+ }
+
+ return buffer;
}
/**
- * Returns this matcher's region end, that is, the first character that is
- * not considered for a match.
- *
- * @return the end of the region.
- * @since Android 1.0
+ * Replaces the first occurrence of this matcher's pattern in the input with
+ * a given string.
+ *
+ * @param replacement
+ * the replacement text.
+ * @return the modified input string.
*/
- public int regionEnd() {
- return regionEnd;
+ public String replaceFirst(String replacement) {
+ StringBuffer buffer = new StringBuffer(input.length());
+
+ findPos = 0;
+ appendPos = 0;
+ matchFound = false;
+ searching = false;
+
+ if (find()) {
+ appendReplacement(buffer, replacement);
+ }
+
+ return appendTail(buffer).toString();
}
/**
- * Determines whether this matcher has anchoring bounds enabled or not. When
- * anchoring bounds are enabled, the start and end of the input match the
- * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled
- * by default.
- *
- * @param value
- * the new value for anchoring bounds.
- * @return the {@code Matcher} itself.
- * @since Android 1.0
+ * Replaces all occurrences of this matcher's pattern in the input with a
+ * given string.
+ *
+ * @param replacement
+ * the replacement text.
+ * @return the modified input string.
*/
- public Matcher useAnchoringBounds(boolean value) {
- anchoringBounds = value;
- NativeRegEx.useAnchoringBounds(nativePattern, value);
- return this;
+ public String replaceAll(String replacement) {
+ StringBuffer buffer = new StringBuffer(input.length());
+
+ findPos = 0;
+ appendPos = 0;
+ matchFound = false;
+ searching = false;
+
+ while (find()) {
+ appendReplacement(buffer, replacement);
+ }
+
+ return appendTail(buffer).toString();
}
-
+
/**
- * Indicates whether this matcher has anchoring bounds enabled. When
- * anchoring bounds are enabled, the start and end of the input match the
- * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled
- * by default.
- *
- * @return true if (and only if) the {@code Matcher} uses anchoring bounds.
- * @since Android 1.0
+ * Returns the {@link Pattern} instance used inside this matcher.
+ *
+ * @return the {@code Pattern} instance.
*/
- public boolean hasAnchoringBounds() {
- return anchoringBounds;
+ public Pattern pattern() {
+ return pattern;
}
/**
- * Determines whether this matcher has transparent bounds enabled or not.
- * When transparent bounds are enabled, the parts of the input outside the
- * region are subject to lookahead and lookbehind, otherwise they are not.
- * Transparent bounds are disabled by default.
- *
- * @param value
- * the new value for transparent bounds.
- * @return the {@code Matcher} itself.
- * @since Android 1.0
+ * Returns the text that matched a given group of the regular expression.
+ *
+ * @param group
+ * the group, ranging from 0 to groupCount() - 1, with 0
+ * representing the whole pattern.
+ * @return the text that matched the group.
+ * @throws IllegalStateException
+ * if no successful match has been made.
*/
- public Matcher useTransparentBounds(boolean value) {
- transparentBounds = value;
- NativeRegEx.useTransparentBounds(nativePattern, value);
- return this;
+ public String group(int group) {
+ ensureMatch();
+ int from = matchOffsets[group * 2];
+ int to = matchOffsets[(group * 2) + 1];
+ if (from == -1 || to == -1) {
+ return null;
+ } else {
+ return input.substring(from, to);
+ }
}
-
+
/**
- * Indicates whether this matcher has transparent bounds enabled. When
- * transparent bounds are enabled, the parts of the input outside the region
- * are subject to lookahead and lookbehind, otherwise they are not.
- * Transparent bounds are disabled by default.
- *
- * @return true if (and only if) the {@code Matcher} uses anchoring bounds.
- * @since Android 1.0
+ * Returns the text that matched the whole regular expression.
+ *
+ * @return the text.
+ * @throws IllegalStateException
+ * if no successful match has been made.
*/
- public boolean hasTransparentBounds() {
- return transparentBounds;
+ public String group() {
+ return group(0);
}
-
+
/**
- * Makes sure that a successful match has been made. Is invoked internally
- * from various places in the class.
- *
- * @throws IllegalStateException
- * if no successful match has been made.
- *
- * @since Android 1.0
+ * Returns the next occurrence of the {@link Pattern} in the input. The
+ * method starts the search from the given character in the input.
+ *
+ * @param start
+ * The index in the input at which the find operation is to
+ * begin. If this is less than the start of the region, it is
+ * automatically adjusted to that value. If it is beyond the end
+ * of the region, the method will fail.
+ * @return true if (and only if) a match has been found.
*/
- private void ensureMatch() throws IllegalStateException {
- if (!matchFound) {
- throw new IllegalStateException("No successful match so far");
+ public boolean find(int start) {
+ findPos = start;
+
+ if (findPos < regionStart) {
+ findPos = regionStart;
+ } else if (findPos >= regionEnd) {
+ matchFound = false;
+ return false;
}
+
+ matchFound = NativeRegEx.find(nativePattern, findPos);
+ if (matchFound) {
+ NativeRegEx.startEnd(nativePattern, matchOffsets);
+ findPos = matchOffsets[1];
+ }
+
+ return matchFound;
}
-
+
/**
* Returns the next occurrence of the {@link Pattern} in the input. If a
* previous match was successful, the method continues the search from the
@@ -389,7 +425,6 @@ public final class Matcher implements MatchResult {
* either from the region start (if one has been set), or from position 0.
*
* @return true if (and only if) a match has been found.
- * @since Android 1.0
*/
public boolean find() {
if (!searching) {
@@ -408,34 +443,35 @@ public final class Matcher implements MatchResult {
}
/**
- * Returns the next occurrence of the {@link Pattern} in the input. The
- * method starts the search from the given character in the input.
- *
- * @param start
- * The index in the input at which the find operation is to
- * begin. If this is less than the start of the region, it is
- * automatically adjusted to that value. If it is beyond the end
- * of the region, the method will fail.
- * @return true if (and only if) a match has been found.
- * @since Android 1.0
+ * Returns the index of the first character of the text that matched a given
+ * group.
+ *
+ * @param group
+ * the group, ranging from 0 to groupCount() - 1, with 0
+ * representing the whole pattern.
+ * @return the character index.
+ * @throws IllegalStateException
+ * if no successful match has been made.
*/
- public boolean find(int start) {
- findPos = start;
-
- if (findPos < regionStart) {
- findPos = regionStart;
- } else if (findPos >= regionEnd) {
- matchFound = false;
- return false;
- }
-
- matchFound = NativeRegEx.find(nativePattern, findPos);
- if (matchFound) {
- NativeRegEx.startEnd(nativePattern, matchOffsets);
- findPos = matchOffsets[1];
- }
-
- return matchFound;
+ public int start(int group) throws IllegalStateException {
+ ensureMatch();
+ return matchOffsets[group * 2];
+ }
+
+ /**
+ * Returns the index of the first character following the text that matched
+ * a given group.
+ *
+ * @param group
+ * the group, ranging from 0 to groupCount() - 1, with 0
+ * representing the whole pattern.
+ * @return the character index.
+ * @throws IllegalStateException
+ * if no successful match has been made.
+ */
+ public int end(int group) {
+ ensureMatch();
+ return matchOffsets[(group * 2) + 1];
}
/**
@@ -444,8 +480,6 @@ public final class Matcher implements MatchResult {
*
* @return true if (and only if) the {@code Pattern} matches the entire
* region.
- *
- * @since Android 1.0
*/
public boolean matches() {
matchFound = NativeRegEx.matches(nativePattern, -1);
@@ -458,13 +492,34 @@ public final class Matcher implements MatchResult {
}
/**
+ * Returns a replacement string for the given one that has all backslashes
+ * and dollar signs escaped.
+ *
+ * @param s
+ * the input string.
+ * @return the input string, with all backslashes and dollar signs having
+ * been escaped.
+ */
+ public static String quoteReplacement(String s) {
+ StringBuffer buffer = new StringBuffer(s.length());
+
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == '\\' || c == '$') {
+ buffer.append('\\');
+ }
+ buffer.append(c);
+ }
+
+ return buffer.toString();
+ }
+
+ /**
* Tries to match the {@link Pattern}, starting from the beginning of the
* region (or the beginning of the input, if no region has been set).
* Doesn't require the {@code Pattern} to match against the whole region.
*
* @return true if (and only if) the {@code Pattern} matches.
- *
- * @since Android 1.0
*/
public boolean lookingAt() {
matchFound = NativeRegEx.lookingAt(nativePattern, -1);
@@ -483,27 +538,19 @@ public final class Matcher implements MatchResult {
* @return the character index.
* @throws IllegalStateException
* if no successful match has been made.
- * @since Android 1.0
*/
- public int start() throws IllegalStateException {
+ public int start() {
return start(0);
}
/**
- * Returns the index of the first character of the text that matched a given
- * group.
- *
- * @param group
- * the group, ranging from 0 to groupCount() - 1, with 0
- * representing the whole pattern.
- * @return the character index.
- * @throws IllegalStateException
- * if no successful match has been made.
- * @since Android 1.0
+ * Returns the number of groups in the results, which is always equal to
+ * the number of groups in the original regular expression.
+ *
+ * @return the number of groups.
*/
- public int start(int group) throws IllegalStateException {
- ensureMatch();
- return matchOffsets[group * 2];
+ public int groupCount() {
+ return pattern.mGroupCount;
}
/**
@@ -513,255 +560,166 @@ public final class Matcher implements MatchResult {
* @return the character index.
* @throws IllegalStateException
* if no successful match has been made.
- * @since Android 1.0
*/
public int end() {
return end(0);
}
/**
- * Returns the index of the first character following the text that matched
- * a given group.
- *
- * @param group
- * the group, ranging from 0 to groupCount() - 1, with 0
- * representing the whole pattern.
- * @return the character index.
+ * Converts the current match into a separate {@link MatchResult} instance
+ * that is independent from this matcher. The new object is unaffected when
+ * the state of this matcher changes.
+ *
+ * @return the new {@code MatchResult}.
* @throws IllegalStateException
* if no successful match has been made.
- * @since Android 1.0
*/
- public int end(int group) {
+ public MatchResult toMatchResult() {
ensureMatch();
- return matchOffsets[(group * 2) + 1];
+ return new MatchResultImpl(input, matchOffsets);
}
/**
- * Returns the text that matched the whole regular expression.
- *
- * @return the text.
- * @throws IllegalStateException
- * if no successful match has been made.
- * @since Android 1.0
+ * Determines whether this matcher has anchoring bounds enabled or not. When
+ * anchoring bounds are enabled, the start and end of the input match the
+ * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled
+ * by default.
+ *
+ * @param value
+ * the new value for anchoring bounds.
+ * @return the {@code Matcher} itself.
*/
- public String group() {
- return group(0);
+ public Matcher useAnchoringBounds(boolean value) {
+ anchoringBounds = value;
+ NativeRegEx.useAnchoringBounds(nativePattern, value);
+ return this;
}
/**
- * Returns the text that matched a given group of the regular expression.
- *
- * @param group
- * the group, ranging from 0 to groupCount() - 1, with 0
- * representing the whole pattern.
- * @return the text that matched the group.
- * @throws IllegalStateException
- * if no successful match has been made.
- * @since Android 1.0
+ * Indicates whether this matcher has anchoring bounds enabled. When
+ * anchoring bounds are enabled, the start and end of the input match the
+ * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled
+ * by default.
+ *
+ * @return true if (and only if) the {@code Matcher} uses anchoring bounds.
*/
- public String group(int group) {
- ensureMatch();
- int from = matchOffsets[group * 2];
- int to = matchOffsets[(group * 2) + 1];
- if (from == -1 || to == -1) {
- return null;
- } else {
- return input.substring(from, to);
- }
+ public boolean hasAnchoringBounds() {
+ return anchoringBounds;
}
/**
- * Indicates whether the last match hit the end of the input.
- *
- * @return true if (and only if) the last match hit the end of the input.
- * @since Android 1.0
- */
- public boolean hitEnd() {
- return NativeRegEx.hitEnd(nativePattern);
- }
-
- /**
- * Indicates whether more input might change a successful match into an
- * unsuccessful one.
- *
- * @return true if (and only if) more input might change a successful match
- * into an unsuccessful one.
- * @since Android 1.0
+ * Determines whether this matcher has transparent bounds enabled or not.
+ * When transparent bounds are enabled, the parts of the input outside the
+ * region are subject to lookahead and lookbehind, otherwise they are not.
+ * Transparent bounds are disabled by default.
+ *
+ * @param value
+ * the new value for transparent bounds.
+ * @return the {@code Matcher} itself.
*/
- public boolean requireEnd() {
- return NativeRegEx.requireEnd(nativePattern);
+ public Matcher useTransparentBounds(boolean value) {
+ transparentBounds = value;
+ NativeRegEx.useTransparentBounds(nativePattern, value);
+ return this;
}
-
+
/**
- * Converts the current match into a separate {@link MatchResult} instance
- * that is independent from this matcher. The new object is unaffected when
- * the state of this matcher changes.
- *
- * @return the new {@code MatchResult}.
+ * Makes sure that a successful match has been made. Is invoked internally
+ * from various places in the class.
+ *
* @throws IllegalStateException
* if no successful match has been made.
- * @since Android 1.0
*/
- public MatchResult toMatchResult() {
- ensureMatch();
- return new MatchResultImpl(input, matchOffsets);
+ private void ensureMatch() {
+ if (!matchFound) {
+ throw new IllegalStateException("No successful match so far");
+ }
}
/**
- * Appends a literal part of the input plus a replacement for the current
- * match to a given {@link StringBuffer}. The literal part is exactly the
- * part of the input between the previous match and the current match. The
- * method can be used in conjunction with {@link #find()} and
- * {@link #appendTail(StringBuffer)} to walk through the input and replace
- * all occurrences of the {@code Pattern} with something else.
- *
- * @param buffer
- * the {@code StringBuffer} to append to.
- * @param replacement
- * the replacement text.
- * @return the {@code Matcher} itself.
- * @throws IllegalStateException
- * if no successful match has been made.
- * @since Android 1.0
+ * Indicates whether this matcher has transparent bounds enabled. When
+ * transparent bounds are enabled, the parts of the input outside the region
+ * are subject to lookahead and lookbehind, otherwise they are not.
+ * Transparent bounds are disabled by default.
+ *
+ * @return true if (and only if) the {@code Matcher} uses anchoring bounds.
*/
- public Matcher appendReplacement(StringBuffer buffer, String replacement)
- throws IllegalStateException {
-
- buffer.append(input.substring(appendPos, start()));
- appendEvaluated(buffer, replacement);
- appendPos = end();
-
- return this;
+ public boolean hasTransparentBounds() {
+ return transparentBounds;
}
/**
- * Appends the (unmatched) remainder of the input to the given
- * {@link StringBuffer}. The method can be used in conjunction with
- * {@link #find()} and {@link #appendReplacement(StringBuffer, String)} to
- * walk through the input and replace all matches of the {@code Pattern}
- * with something else.
- *
- * @param buffer
- * the {@code StringBuffer} to append to.
- * @return the {@code StringBuffer}.
- * @throws IllegalStateException
- * if no successful match has been made.
- * @since Android 1.0
+ * Returns this matcher's region start, that is, the first character that is
+ * considered for a match.
+ *
+ * @return the start of the region.
*/
- public StringBuffer appendTail(StringBuffer buffer) {
- if (appendPos < regionEnd) {
- buffer.append(input.substring(appendPos, regionEnd));
- }
-
- return buffer;
+ public int regionStart() {
+ return regionStart;
}
/**
- * Internal helper method to append a given string to a given string buffer.
- * If the string contains any references to groups, these are replaced by
- * the corresponding group's contents.
- *
- * @param buffer
- * the string buffer.
- * @param s
- * the string to append.
+ * Returns this matcher's region end, that is, the first character that is
+ * not considered for a match.
+ *
+ * @return the end of the region.
*/
- private void appendEvaluated(StringBuffer buffer, String s) {
- boolean escape = false;
- boolean dollar = false;
-
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- if (c == '\\' && !escape) {
- escape = true;
- } else if (c == '$' && !escape) {
- dollar = true;
- } else if (c >= '0' && c <= '9' && dollar) {
- buffer.append(group(c - '0'));
- dollar = false;
- } else {
- buffer.append(c);
- dollar = false;
- escape = false;
- }
- }
-
- // This seemingly stupid piece of code reproduces a JDK bug.
- if (escape) {
- throw new ArrayIndexOutOfBoundsException(s.length());
- }
+ public int regionEnd() {
+ return regionEnd;
}
-
+
/**
- * Replaces all occurrences of this matcher's pattern in the input with a
- * given string.
+ * Indicates whether more input might change a successful match into an
+ * unsuccessful one.
*
- * @param replacement
- * the replacement text.
- * @return the modified input string.
- * @since Android 1.0
+ * @return true if (and only if) more input might change a successful match
+ * into an unsuccessful one.
*/
- public String replaceAll(String replacement) {
- StringBuffer buffer = new StringBuffer(input.length());
-
- findPos = 0;
- appendPos = 0;
- matchFound = false;
- searching = false;
-
- while (find()) {
- appendReplacement(buffer, replacement);
- }
-
- return appendTail(buffer).toString();
+ public boolean requireEnd() {
+ return NativeRegEx.requireEnd(nativePattern);
}
/**
- * Replaces the first occurrence of this matcher's pattern in the input with
- * a given string.
- *
- * @param replacement
- * the replacement text.
- * @return the modified input string.
- * @since Android 1.0
+ * Indicates whether the last match hit the end of the input.
+ *
+ * @return true if (and only if) the last match hit the end of the input.
*/
- public String replaceFirst(String replacement) {
- StringBuffer buffer = new StringBuffer(input.length());
-
- findPos = 0;
- appendPos = 0;
- matchFound = false;
- searching = false;
-
- if (find()) {
- appendReplacement(buffer, replacement);
- }
-
- return appendTail(buffer).toString();
+ public boolean hitEnd() {
+ return NativeRegEx.hitEnd(nativePattern);
}
/**
- * Returns a replacement string for the given one that has all backslashes
- * and dollar signs escaped.
- *
- * @param s
- * the input string.
- * @return the input string, with all backslashes and dollar signs having
- * been escaped.
- * @since Android 1.0
+ * Sets a new pattern for the {@code Matcher}. Results of a previous find
+ * get lost. The next attempt to find an occurrence of the {@link Pattern}
+ * in the string will start at the beginning of the input.
+ *
+ * @param pattern
+ * the new {@code Pattern}.
+ *
+ * @return the {@code Matcher} itself.
*/
- public static String quoteReplacement(String s) {
- StringBuffer buffer = new StringBuffer(s.length());
-
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- if (c == '\\' || c == '$') {
- buffer.append('\\');
- }
- buffer.append(c);
+ public Matcher usePattern(Pattern pattern) {
+ if (pattern == null) {
+ throw new IllegalArgumentException();
}
-
- return buffer.toString();
+
+ this.pattern = pattern;
+
+ if (nativePattern != 0) {
+ NativeRegEx.close(nativePattern);
+ }
+ nativePattern = NativeRegEx.clone(pattern.mNativePattern);
+
+ if (input != null) {
+ NativeRegEx.setText(nativePattern, input);
+ NativeRegEx.setRegion(nativePattern, regionStart, regionEnd);
+ NativeRegEx.useAnchoringBounds(nativePattern, anchoringBounds);
+ NativeRegEx.useTransparentBounds(nativePattern, transparentBounds);
+ }
+
+ matchOffsets = new int[(this.pattern.mGroupCount + 1) * 2];
+ matchFound = false;
+ return this;
}
@Override
diff --git a/regex/src/main/java/java/util/regex/Pattern.java b/regex/src/main/java/java/util/regex/Pattern.java
index 2c71de1..db3bc21 100644
--- a/regex/src/main/java/java/util/regex/Pattern.java
+++ b/regex/src/main/java/java/util/regex/Pattern.java
@@ -47,7 +47,7 @@ import com.ibm.icu4jni.regex.NativeRegEx;
* boolean b2 = Pattern.matches("Hello, A[a-z]*!", "Hello, Robot!"); // false
* </pre>
* <p/>
- * Please consult the <a href="package-summary.html">package documentation</a> for an
+ * Please consult the <a href="package.html">package documentation</a> for an
* overview of the regular expression syntax used in this class as well as
* Android-specific implementation details.
*
@@ -61,8 +61,6 @@ public final class Pattern implements Serializable {
/**
* This constant specifies that a pattern matches Unix line endings ('\n')
* only against the '.', '^', and '$' meta characters.
- *
- * @since Android 1.0
*/
public static final int UNIX_LINES = 0x01;
@@ -76,8 +74,6 @@ public final class Pattern implements Serializable {
* constant. So if case insensitivity is enabled, this automatically extends
* to all Unicode characters. The {@code UNICODE_CASE} constant itself has
* no special consequences.
- *
- * @since Android 1.0
*/
public static final int CASE_INSENSITIVE = 0x02;
@@ -85,8 +81,6 @@ public final class Pattern implements Serializable {
* This constant specifies that a {@code Pattern} may contain whitespace or
* comments. Otherwise comments and whitespace are taken as literal
* characters.
- *
- * @since Android 1.0
*/
public static final int COMMENTS = 0x04;
@@ -94,24 +88,18 @@ public final class Pattern implements Serializable {
* This constant specifies that the meta characters '^' and '$' match only
* the beginning and end end of an input line, respectively. Normally, they
* match the beginning and the end of the complete input.
- *
- * @since Android 1.0
*/
public static final int MULTILINE = 0x08;
/**
* This constant specifies that the whole {@code Pattern} is to be taken
* literally, that is, all meta characters lose their meanings.
- *
- * @since Android 1.0
*/
public static final int LITERAL = 0x10;
/**
* This constant specifies that the '.' meta character matches arbitrary
* characters, including line endings, which is normally not the case.
- *
- * @since Android 1.0
*/
public static final int DOTALL = 0x20;
@@ -126,8 +114,6 @@ public final class Pattern implements Serializable {
* constant. So if case insensitivity is enabled, this automatically extends
* to all Unicode characters. The {@code UNICODE_CASE} constant then has no
* special consequences.
- *
- * @since Android 1.0
*/
public static final int UNICODE_CASE = 0x40;
@@ -135,8 +121,6 @@ public final class Pattern implements Serializable {
* This constant specifies that a character in a {@code Pattern} and a
* character in the input string only match if they are canonically
* equivalent. It is (currently) not supported in Android.
- *
- * @since Android 1.0
*/
public static final int CANON_EQ = 0x80;
@@ -159,24 +143,144 @@ public final class Pattern implements Serializable {
* Holds the number of groups in the pattern.
*/
transient int mGroupCount;
-
+
+
/**
- * Compiles a regular expression, creating a new Pattern instance in the
- * process. This is actually a convenience method that calls {@link
- * #compile(String, int)} with a {@code flags} value of zero.
- *
- * @param pattern
- * the regular expression.
- *
- * @return the new {@code Pattern} instance.
- *
- * @throws PatternSyntaxException
- * if the regular expression is syntactically incorrect.
- *
- * @since Android 1.0
+ * Returns a {@link Matcher} for the {@code Pattern} and a given input. The
+ * {@code Matcher} can be used to match the {@code Pattern} against the
+ * whole input, find occurrences of the {@code Pattern} in the input, or
+ * replace parts of the input.
+ *
+ * @param input
+ * the input to process.
+ *
+ * @return the resulting {@code Matcher}.
*/
- public static Pattern compile(String pattern) throws PatternSyntaxException {
- return new Pattern(pattern, 0);
+ public Matcher matcher(CharSequence input) {
+ return new Matcher(this, input);
+ }
+
+ /**
+ * Splits the given input sequence at occurrences of this {@code Pattern}.
+ *
+ * <p>If this {@code Pattern} does not occur in the input, the result is an
+ * array containing the input (converted from a {@code CharSequence} to
+ * a {@code String}).
+ *
+ * <p>Otherwise, the {@code limit} parameter controls the contents of the
+ * returned array as described below.
+ *
+ * @param inputSeq
+ * the input sequence.
+ * @param limit
+ * Determines the maximum number of entries in the resulting
+ * array, and the treatment of trailing empty strings.
+ * <ul>
+ * <li>For n &gt; 0, the resulting array contains at most n
+ * entries. If this is fewer than the number of matches, the
+ * final entry will contain all remaining input.
+ * <li>For n &lt; 0, the length of the resulting array is
+ * exactly the number of occurrences of the {@code Pattern}
+ * plus one for the text after the final separator.
+ * All entries are included.
+ * <li>For n == 0, the result is as for n &lt; 0, except
+ * trailing empty strings will not be returned. (Note that
+ * the case where the input is itself an empty string is
+ * special, as described above, and the limit parameter does
+ * not apply there.)
+ * </ul>
+ *
+ * @return the resulting array.
+ */
+ public String[] split(CharSequence inputSeq, int limit) {
+ if (inputSeq.length() == 0) {
+ // Unlike Perl, which considers the result of splitting the empty
+ // string to be the empty array, Java returns an array containing
+ // the empty string.
+ return new String[] { "" };
+ }
+
+ int maxLength = limit <= 0 ? Integer.MAX_VALUE : limit;
+
+ String input = inputSeq.toString();
+ ArrayList<String> list = new ArrayList<String>();
+
+ Matcher matcher = new Matcher(this, inputSeq);
+ int savedPos = 0;
+
+ // Add text preceding each occurrence, if enough space.
+ while(matcher.find() && list.size() + 1 < maxLength) {
+ list.add(input.substring(savedPos, matcher.start()));
+ savedPos = matcher.end();
+ }
+
+ // Add trailing text if enough space.
+ if (list.size() < maxLength) {
+ if (savedPos < input.length()) {
+ list.add(input.substring(savedPos));
+ } else {
+ list.add("");
+ }
+ }
+
+ // Remove trailing empty matches in the limit == 0 case.
+ if (limit == 0) {
+ int i = list.size() - 1;
+ while (i >= 0 && "".equals(list.get(i))) {
+ list.remove(i);
+ i--;
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Splits a given input around occurrences of a regular expression. This is
+ * a convenience method that is equivalent to calling the method
+ * {@link #split(java.lang.CharSequence, int)} with a limit of 0.
+ *
+ * @param input
+ * the input sequence.
+ *
+ * @return the resulting array.
+ */
+ public String[] split(CharSequence input) {
+ return split(input, 0);
+ }
+
+ /**
+ * Returns the regular expression that was compiled into this
+ * {@code Pattern}.
+ *
+ * @return the regular expression.
+ */
+ public String pattern() {
+ return pattern;
+ }
+
+ @Override
+ public String toString() {
+ return pattern;
+ }
+
+ /**
+ * Returns the flags that have been set for this {@code Pattern}.
+ *
+ * @return the flags that have been set. A combination of the constants
+ * defined in this class.
+ *
+ * @see #CANON_EQ
+ * @see #CASE_INSENSITIVE
+ * @see #COMMENTS
+ * @see #DOTALL
+ * @see #LITERAL
+ * @see #MULTILINE
+ * @see #UNICODE_CASE
+ * @see #UNIX_LINES
+ */
+ public int flags() {
+ return flags;
}
/**
@@ -208,8 +312,6 @@ public final class Pattern implements Serializable {
* @see #MULTILINE
* @see #UNICODE_CASE
* @see #UNIX_LINES
- *
- * @since Android 1.0
*/
public static Pattern compile(String pattern, int flags) throws PatternSyntaxException {
return new Pattern(pattern, flags);
@@ -238,7 +340,24 @@ public final class Pattern implements Serializable {
compileImpl(pattern, flags);
}
-
+
+ /**
+ * Compiles a regular expression, creating a new Pattern instance in the
+ * process. This is actually a convenience method that calls {@link
+ * #compile(String, int)} with a {@code flags} value of zero.
+ *
+ * @param pattern
+ * the regular expression.
+ *
+ * @return the new {@code Pattern} instance.
+ *
+ * @throws PatternSyntaxException
+ * if the regular expression is syntactically incorrect.
+ */
+ public static Pattern compile(String pattern) {
+ return new Pattern(pattern, 0);
+ }
+
/**
* Compiles the given regular expression using the given flags. Used
* internally only.
@@ -266,56 +385,6 @@ public final class Pattern implements Serializable {
}
/**
- * Returns the regular expression that was compiled into this
- * {@code Pattern}.
- *
- * @return the regular expression.
- *
- * @since Android 1.0
- */
- public String pattern() {
- return pattern;
- }
-
- /**
- * Returns the flags that have been set for this {@code Pattern}.
- *
- * @return the flags that have been set. A combination of the constants
- * defined in this class.
- *
- * @see #CANON_EQ
- * @see #CASE_INSENSITIVE
- * @see #COMMENTS
- * @see #DOTALL
- * @see #LITERAL
- * @see #MULTILINE
- * @see #UNICODE_CASE
- * @see #UNIX_LINES
- *
- * @since Android 1.0
- */
- public int flags() {
- return flags;
- }
-
- /**
- * Returns a {@link Matcher} for the {@code Pattern} and a given input. The
- * {@code Matcher} can be used to match the {@code Pattern} against the
- * whole input, find occurrences of the {@code Pattern} in the input, or
- * replace parts of the input.
- *
- * @param input
- * the input to process.
- *
- * @return the resulting {@code Matcher}.
- *
- * @since Android 1.0
- */
- public Matcher matcher(CharSequence input) {
- return new Matcher(this, input);
- }
-
- /**
* Tries to match a given regular expression against a given input. This is
* actually nothing but a convenience method that compiles the regular
* expression into a {@code Pattern}, builds a {@link Matcher} for it, and
@@ -332,107 +401,12 @@ public final class Pattern implements Serializable {
*
* @see Pattern#compile(java.lang.String, int)
* @see Matcher#matches()
- *
- * @since Android 1.0
*/
- static public boolean matches(String regex, CharSequence input) {
+ public static boolean matches(String regex, CharSequence input) {
return new Matcher(new Pattern(regex, 0), input).matches();
}
/**
- * Splits a given input around occurrences of a regular expression. This is
- * a convenience method that is equivalent to calling the method
- * {@link #split(java.lang.CharSequence, int)} with a limit of 0.
- *
- * @param input
- * the input sequence.
- *
- * @return the resulting array.
- *
- * @since Android 1.0
- */
- public String[] split(CharSequence input) {
- return split(input, 0);
- }
-
- /**
- * Splits the given input sequence at occurrences of this {@code Pattern}.
- *
- * If this {@code Pattern} does not occur in the input, the result is an
- * array containing the input (converted from a {@code CharSequence} to
- * a {@code String}).
- *
- * Otherwise, the {@code limit} parameter controls the contents of the
- * returned array as described below.
- *
- * @param inputSeq
- * the input sequence.
- * @param limit
- * Determines the maximum number of entries in the resulting
- * array, and the treatment of trailing empty strings.
- * <ul>
- * <li>For n &gt; 0, the resulting array contains at most n
- * entries. If this is fewer than the number of matches, the
- * final entry will contain all remaining input.
- * <li>For n &lt; 0, the length of the resulting array is
- * exactly the number of occurrences of the {@code Pattern}
- * plus one for the text after the final separator.
- * All entries are included.
- * <li>For n == 0, the result is as for n &lt; 0, except
- * trailing empty strings will not be returned. (Note that
- * the case where the input is itself an empty string is
- * special, as described above, and the limit parameter does
- * not apply there.)
- * </ul>
- *
- * @return the resulting array.
- *
- * @since Android 1.0
- */
- public String[] split(CharSequence inputSeq, int limit) {
- if (inputSeq.length() == 0) {
- // Unlike Perl, which considers the result of splitting the empty
- // string to be the empty array, Java returns an array containing
- // the empty string.
- return new String[] { "" };
- }
-
- int maxLength = limit <= 0 ? Integer.MAX_VALUE : limit;
-
- String input = inputSeq.toString();
- ArrayList<String> list = new ArrayList<String>();
-
- Matcher matcher = new Matcher(this, inputSeq);
- int savedPos = 0;
-
- // Add text preceding each occurrence, if enough space.
- while(matcher.find() && list.size() + 1 < maxLength) {
- list.add(input.substring(savedPos, matcher.start()));
- savedPos = matcher.end();
- }
-
- // Add trailing text if enough space.
- if (list.size() < maxLength) {
- if (savedPos < input.length()) {
- list.add(input.substring(savedPos));
- } else {
- list.add("");
- }
- }
-
- // Remove trailing empty matches in the limit == 0 case.
- if (limit == 0) {
- int i = list.size() - 1;
- while (i >= 0 && "".equals(list.get(i))) {
- list.remove(i);
- i--;
- }
- }
-
- return list.toArray(new String[list.size()]);
- }
-
- /**
* Quotes a given string using "\Q" and "\E", so that all other
* meta-characters lose their special meaning. If the string is used for a
* {@code Pattern} afterwards, it can only be matched literally.
@@ -441,27 +415,20 @@ public final class Pattern implements Serializable {
* the string to quote.
*
* @return the quoted string.
- *
- * @since Android 1.0
*/
public static String quote(String s) {
- StringBuffer sb = new StringBuffer().append("\\Q");
+ StringBuilder sb = new StringBuilder().append("\\Q"); //$NON-NLS-1$
int apos = 0;
int k;
- while ((k = s.indexOf("\\E", apos)) >= 0) {
- sb.append(s.substring(apos, k + 2)).append("\\\\E\\Q");
+ while ((k = s.indexOf("\\E", apos)) >= 0) { //$NON-NLS-1$
+ sb.append(s.substring(apos, k + 2)).append("\\\\E\\Q"); //$NON-NLS-1$
apos = k + 2;
}
- return sb.append(s.substring(apos)).append("\\E").toString();
+ return sb.append(s.substring(apos)).append("\\E").toString(); //$NON-NLS-1$
}
@Override
- public String toString() {
- return pattern;
- }
-
- @Override
protected void finalize() throws Throwable {
try {
if (mNativePattern != 0) {
@@ -474,7 +441,7 @@ public final class Pattern implements Serializable {
}
/**
- * Provides serialization support
+ * Serialization support
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
diff --git a/regex/src/main/java/java/util/regex/PatternSyntaxException.java b/regex/src/main/java/java/util/regex/PatternSyntaxException.java
index e4d5abd..d59bdd4 100644
--- a/regex/src/main/java/java/util/regex/PatternSyntaxException.java
+++ b/regex/src/main/java/java/util/regex/PatternSyntaxException.java
@@ -1,17 +1,18 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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
*
- * 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
*
- * 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.
+ * 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 java.util.regex;
@@ -25,24 +26,22 @@ import java.util.Arrays;
*
* @see Pattern#compile(String)
* @see Pattern#compile(java.lang.String,int)
- *
- * @since Android 1.0
*/
public class PatternSyntaxException extends IllegalArgumentException {
private static final long serialVersionUID = -3864639126226059218L;
-
- /**
- * Holds the syntactically incorrect regular expression, or null if the
- * regular expression is not known.
- */
- private String pattern;
/**
* Holds the description of the syntax error, or null if the description is
* not known.
*/
- private String description;
+ private String desc;
+
+ /**
+ * Holds the syntactically incorrect regular expression, or null if the
+ * regular expression is not known.
+ */
+ private String pattern;
/**
* Holds the index around which the error occured, or -1, in case it is
@@ -63,11 +62,10 @@ public class PatternSyntaxException extends IllegalArgumentException {
* @param index
* the character index around which the error occurred, or -1 if
* the index is not known.
- * @since Android 1.0
*/
public PatternSyntaxException(String description, String pattern, int index) {
+ this.desc = description;
this.pattern = pattern;
- this.description = description;
this.index = index;
}
@@ -76,7 +74,6 @@ public class PatternSyntaxException extends IllegalArgumentException {
*
* @return the regular expression.
*
- * @since Android 1.0
*/
public String getPattern() {
return pattern;
@@ -88,16 +85,15 @@ public class PatternSyntaxException extends IllegalArgumentException {
* original regular expression, and the index at which the error occured.
*
* @return the error message.
- *
- * @since Android 1.0
*/
@Override
public String getMessage() {
+ // BEGIN android-changed
StringBuilder builder = new StringBuilder("Syntax error");
- if (description != null) {
+ if (desc != null) {
builder.append(' ');
- builder.append(description);
+ builder.append(desc);
}
if (index >= 0) {
@@ -118,6 +114,7 @@ public class PatternSyntaxException extends IllegalArgumentException {
}
return builder.toString();
+ // END android-changed
}
/**
@@ -125,10 +122,9 @@ public class PatternSyntaxException extends IllegalArgumentException {
* description is not known.
*
* @return the description.
- * @since Android 1.0
*/
public String getDescription() {
- return description;
+ return desc;
}
/**
@@ -137,10 +133,8 @@ public class PatternSyntaxException extends IllegalArgumentException {
*
* @return the index.
*
- * @since Android 1.0
*/
public int getIndex() {
return index;
}
-
}
diff --git a/sql/src/main/native/sqlite_jni.c b/sql/src/main/native/sqlite_jni.c
index 4923869..82aa8a0 100644
--- a/sql/src/main/native/sqlite_jni.c
+++ b/sql/src/main/native/sqlite_jni.c
@@ -331,16 +331,10 @@ trans2iso(JNIEnv *env, int haveutf, jstring enc, jstring src,
dest->result = 0;
dest->tofree = 0;
if (haveutf) {
- const char *utf = (*env)->GetStringUTFChars(env, src, 0);
-
- if (!utf) {
- return dest->result;
- }
- dest->tofree = malloc(strlen(utf) + 1);
- dest->result = dest->tofree;
- strcpy(dest->result, utf);
- (*env)->ReleaseStringUTFChars(env, src, utf);
- return dest->result;
+ const jsize utfLength = (*env)->GetStringUTFLength(env, src);
+ dest->result = dest->tofree = malloc(utfLength + 1);
+ (*env)->GetStringUTFRegion(env, src, 0, utfLength, dest->result);
+ return dest->result;
}
if (enc) {
bytes = (*env)->CallObjectMethod(env, src,
@@ -1331,11 +1325,10 @@ Java_SQLite_Database__1exec__Ljava_lang_String_2LSQLite_Callback_2_3Ljava_lang_S
}
if (h) {
if (h->sqlite) {
- jboolean b;
jthrowable exc;
int rc = SQLITE_ERROR, nargs, i;
char *err = 0, *p;
- const char *str = (*env)->GetStringUTFChars(env, sql, &b);
+ const char *str = (*env)->GetStringUTFChars(env, sql, NULL);
transstr sqlstr;
struct args {
char *arg;
@@ -3009,10 +3002,9 @@ Java_SQLite_Database_vm_1compile_1args(JNIEnv *env,
hvm *v;
jvalue vv;
jthrowable exc;
- jboolean b;
int rc = SQLITE_ERROR, nargs, i;
char *p;
- const char *str = (*env)->GetStringUTFChars(env, sql, &b);
+ const char *str = (*env)->GetStringUTFChars(env, sql, NULL);
const char *tail;
transstr sqlstr;
struct args {
@@ -3401,7 +3393,7 @@ Java_SQLite_Database_stmt_1prepare(JNIEnv *env, jobject obj, jstring sql,
return;
}
len16 = len16 + sizeof (jchar) - ((char *) tail - (char *) sql16);
- if (len16 < sizeof (jchar)) {
+ if (len16 < (jsize) sizeof (jchar)) {
len16 = sizeof (jchar);
}
v = malloc(sizeof (hvm) + len16);
@@ -3674,19 +3666,16 @@ Java_SQLite_Stmt_bind__ILjava_lang_String_2(JNIEnv *env, jobject obj,
return;
}
if (val) {
- len = (*env)->GetStringLength(env, val);
+ const jsize charCount = (*env)->GetStringLength(env, val);
+ len = charCount * sizeof(jchar);
if (len > 0) {
- const jchar *ch;
-
- len *= sizeof (jchar);
data = sqlite3_malloc(len);
if (!data) {
throwoom(env, "unable to get blob parameter");
return;
}
- ch = (*env)->GetStringChars(env, val, 0);
- memcpy(data, ch, len);
- (*env)->ReleaseStringChars(env, val, ch);
+
+ (*env)->GetStringRegion(env, val, 0, charCount, (jchar*) data);
ret = sqlite3_bind_text16((sqlite3_stmt *) v->vm,
pos, data, len, sqlite3_free);
} else {
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java
index 6c23a91..507e14f 100644
--- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java
@@ -94,10 +94,11 @@ public abstract class SSLInputStream extends InputStream {
* Reads and returns uint64 value.
*/
public long readUint64() throws IOException {
- return (read() << 56) | (read() << 48)
- | (read() << 40) | (read() << 32)
- | (read() << 24) | (read() << 16)
- | (read() << 8) | (read() & 0x00FF);
+ // BEGIN android-changed
+ long hi = readUint32();
+ long lo = readUint32();
+ return (hi << 32) | lo;
+ // END android-changed
}
/**
@@ -131,4 +132,3 @@ public abstract class SSLInputStream extends InputStream {
return i;
}
}
-
diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp
index 13a1e61..bb5e3b7 100644
--- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp
@@ -87,9 +87,9 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init(J
// 'seed == null' when no SecureRandom Object is set
// in the SSLContext.
if (seed != NULL) {
- jboolean iscopy = JNI_FALSE;
- jbyte* randseed = env->GetByteArrayElements(seed, &iscopy);
+ jbyte* randseed = env->GetByteArrayElements(seed, NULL);
RAND_seed((unsigned char*) randseed, 1024);
+ env->ReleaseByteArrayElements(seed, randseed, 0);
} else {
RAND_load_file("/dev/urandom", 1024);
}
diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp
index feae690..324dacf 100644
--- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp
@@ -47,20 +47,6 @@ static SSL_SESSION *getSslSessionPointer(JNIEnv* env, jobject object) {
}
/**
- * Throws java.io.IOexception with the provided message.
- */
-static void throwIOExceptionStr(JNIEnv* env, const char* message)
-{
- jclass exClass = env->FindClass("java/io/IOException");
-
- if (exClass == NULL) {
- LOGE("Unable to find class java/io/IOException");
- } else {
- env->ThrowNew(exClass, message);
- }
-}
-
-/**
* Gets the peer certificate in the chain and fills a byte array with the
* information therein.
*/
@@ -150,20 +136,15 @@ static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_deserialize
*/
static jbyteArray org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getid(JNIEnv* env, jobject object)
{
- SSL_SESSION * ssl_session;
- jbyteArray bytes;
- jbyte *tmp;
-
- ssl_session = getSslSessionPointer(env, object);
+ SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
- bytes = env->NewByteArray(ssl_session->session_id_length);
- if (bytes != NULL) {
- tmp = env->GetByteArrayElements(bytes, NULL);
- memcpy(tmp, ssl_session->session_id, ssl_session->session_id_length);
- env->ReleaseByteArrayElements(bytes, tmp, 0);
+ jbyteArray result = env->NewByteArray(ssl_session->session_id_length);
+ if (result != NULL) {
+ jbyte* src = reinterpret_cast<jbyte*>(ssl_session->session_id);
+ env->SetByteArrayRegion(result, 0, ssl_session->session_id_length, src);
}
- return bytes;
+ return result;
}
/**
diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
index 87f2af3..1b0feeb 100644
--- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
@@ -989,9 +989,9 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init(JNIEnv*
// 'seed == null' when no SecureRandom Object is set
// in the SSLContext.
if (seed != NULL) {
- jboolean iscopy = JNI_FALSE;
- jbyte* randseed = env->GetByteArrayElements(seed, &iscopy);
+ jbyte* randseed = env->GetByteArrayElements(seed, NULL);
RAND_seed((unsigned char*) randseed, 1024);
+ env->ReleaseByteArrayElements(seed, randseed, 0);
} else {
RAND_load_file("/dev/urandom", 1024);
}
diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h
index 9ed75be..409b4ec 100644
--- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h
@@ -76,9 +76,8 @@ static jobjectArray getcertificatebytes(JNIEnv* env,
joa = NULL;
break;
} else {
- jbyte *tmp = env->GetByteArrayElements(bytes, NULL);
- memcpy(tmp, bptr->data, bptr->length);
- env->ReleaseByteArrayElements(bytes, tmp, 0);
+ jbyte* src = reinterpret_cast<jbyte*>(bptr->data);
+ env->SetByteArrayRegion(bytes, 0, bptr->length, src);
env->SetObjectArrayElement(joa, i, bytes);
}
}
diff --git a/x-net/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java b/x-net/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java
index 3c1fb2e..c4bae0a 100644
--- a/x-net/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java
+++ b/x-net/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java
@@ -32,6 +32,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.security.KeyStore;
+import java.security.SecureRandom;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
@@ -585,4 +586,26 @@ public class SSLServerSocketTest extends TestCase {
.createServerSocket();
return sss;
}
+
+ @TestTargetNew(
+ level = TestLevel.COMPLETE,
+ notes = "Guard against native resource leakage.",
+ method = "SSLSocket",
+ args = {}
+ )
+ public void test_creationStressTest() throws Exception {
+ KeyManager[] keyManagers = getKeyManagers();
+ // Test the default codepath, which uses /dev/urandom.
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(keyManagers, null, null);
+ for (int i = 0; i < 2048; ++i) {
+ sslContext.getServerSocketFactory().createServerSocket();
+ }
+
+ // Test the other codepath, which copies a seed from a byte[].
+ sslContext.init(keyManagers, null, new SecureRandom());
+ for (int i = 0; i < 2048; ++i) {
+ sslContext.getServerSocketFactory().createServerSocket();
+ }
+ }
}
diff --git a/x-net/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java b/x-net/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java
index 5e39cb1..13a0e59 100644
--- a/x-net/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java
+++ b/x-net/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java
@@ -26,6 +26,7 @@ import javax.security.cert.X509Certificate;
import java.net.*;
import java.security.KeyStore;
+import java.security.SecureRandom;
import java.lang.String;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -336,6 +337,27 @@ public class SSLSocketTest extends TestCase {
}
}
+ @TestTargetNew(
+ level = TestLevel.COMPLETE,
+ notes = "Guard against native resource leakage.",
+ method = "SSLSocket",
+ args = {}
+ )
+ public void test_creationStressTest() throws Exception {
+ // Test the default codepath, which uses /dev/urandom.
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, null, null);
+ for (int i = 0; i < 2048; ++i) {
+ sslContext.getSocketFactory().createSocket();
+ }
+
+ // Test the other codepath, which copies a seed from a byte[].
+ sslContext.init(null, null, new SecureRandom());
+ for (int i = 0; i < 2048; ++i) {
+ sslContext.getSocketFactory().createSocket();
+ }
+ }
+
/**
* @throws IOException
* @tests javax.net.ssl.SSLSocket#addHandshakeCompletedListener(HandshakeCompletedListener listener)
diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java
index f71d289..2fd16d0 100644
--- a/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java
+++ b/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java
@@ -69,7 +69,7 @@ public abstract class InnerNodeImpl extends LeafNodeImpl {
}
public Node getNextSibling() {
- if (parent == null || index >= parent.children.size()) {
+ if (parent == null || index + 1 >= parent.children.size()) {
return null;
}
diff --git a/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
index 40a4116..6132107 100644
--- a/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -105,16 +105,7 @@ static jclass stringClass;
static jstring emptyString;
/**
- * Throws a NullPointerException.
- *
- * @param msg exception message
- */
-static void throw_NullPointerException(JNIEnv* env, const char* msg) {
- jniThrowException(env, "java/lang/NullPointerException", msg);
-}
-
-/**
- * Throw a NullPointerException.
+ * Throws OutOfMemoryError.
*/
static void throw_OutOfMemoryError(JNIEnv* env) {
jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory.");
@@ -1308,26 +1299,21 @@ static jint getAttributeIndex(JNIEnv* env, jobject clazz,
return getAttributeIndexForQName(
env, clazz, attributePointer, localName);
}
-
int localNameLength = env->GetStringUTFLength(localName);
// Create string in the same format used by Expat: "uri|localName"
+ // TODO: do we have a guarantee that uriLength and localNameLength are small?
char concatenated[uriLength + localNameLength + 2];
// Append uri.
- const char* uriBytes = env->GetStringUTFChars(uri, NULL);
- if (uriBytes == NULL) return -1;
- strcpy(concatenated, uriBytes);
- env->ReleaseStringUTFChars(uri, uriBytes);
+ env->GetStringUTFRegion(uri, 0, uriLength, concatenated);
- // Separarator.
+ // Separator.
concatenated[uriLength] = '|';
// Append local name.
- const char* localNameBytes = env->GetStringUTFChars(localName, NULL);
- if (localNameBytes == NULL) return -1;
- strcpy(concatenated + uriLength + 1, localNameBytes);
- env->ReleaseStringUTFChars(localName, localNameBytes);
+ env->GetStringUTFRegion(localName, 0, localNameLength,
+ concatenated + uriLength + 1);
return findAttributeByName(attributes, concatenated);
}
diff --git a/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java b/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java
index c515d20..292c2f1 100644
--- a/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java
+++ b/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java
@@ -278,7 +278,6 @@ public class DocumentBuilderTest extends TestCase {
method = "parse",
args = {java.io.File.class}
)
- @KnownFailure("d.getChildNodes returns an unexpected/strange #Text node")
public void test_parseLjava_io_File() throws IOException {
File f = resourceToTmpFile("/simple.xml");
diff --git a/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTestSupport.java b/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTestSupport.java
index bc5e6a1..a1627ba 100644
--- a/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTestSupport.java
+++ b/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTestSupport.java
@@ -54,7 +54,7 @@ class SAXParserTestSupport {
public static final String KEY_ERROR = "error";
public static final String KEY_FATAL_ERROR = "fatalError";
public static final String KEY_WARNING = "warning";
- public static final String KEY_END_ELEMENT = "endEement";
+ public static final String KEY_END_ELEMENT = "endElement";
public static final String KEY_END_PREFIX_MAPPING = "endPrefixMapping";
public static final String KEY_IGNORABLE_WHITE_SPACE =
"ignorableWhitespace";
diff --git a/xml/src/test/java/tests/xml/AllTests.java b/xml/src/test/java/tests/xml/AllTests.java
index eefae50..8c089e3 100644
--- a/xml/src/test/java/tests/xml/AllTests.java
+++ b/xml/src/test/java/tests/xml/AllTests.java
@@ -26,6 +26,7 @@ public class AllTests {
suite.addTestSuite(SimpleParserTest.class);
suite.addTestSuite(SimpleBuilderTest.class);
+ suite.addTestSuite(NodeTests.class);
//suite.addTest(tests.org.w3c.dom.AllTests.suite());
suite.addTest(tests.api.javax.xml.parsers.AllTests.suite());
diff --git a/xml/src/test/java/tests/xml/NodeTests.java b/xml/src/test/java/tests/xml/NodeTests.java
new file mode 100644
index 0000000..e46e216
--- /dev/null
+++ b/xml/src/test/java/tests/xml/NodeTests.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2009 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.xml;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargetClass;
+
+import junit.framework.TestCase;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import java.io.ByteArrayInputStream;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+@TestTargetClass(Node.class)
+public class NodeTests extends TestCase {
+ @TestTargetNew(
+ level = TestLevel.PARTIAL,
+ notes = "Issue #779: org.w3c.dom.Node#getNextSibling throws IndexOutOfBoundsException.",
+ method = "getNextSibling",
+ args = {}
+ )
+ public void test_getNextSibling() throws Exception {
+ // Calling getNextSibling when there is no next sibling should return null.
+ // From http://code.google.com/p/android/issues/detail?id=779.
+ ByteArrayInputStream bis = new ByteArrayInputStream("<root/>".getBytes());
+ Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(bis);
+ Node root = document.getDocumentElement();
+ assertNull(root.getNextSibling());
+ }
+}