diff options
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 > 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 < 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 < 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 > 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 < 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 < 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()); + } +} |