summaryrefslogtreecommitdiffstats
path: root/luni/src
diff options
context:
space:
mode:
Diffstat (limited to 'luni/src')
-rw-r--r--luni/src/main/java/libcore/icu/NativeBreakIterator.java10
-rw-r--r--luni/src/main/native/libcore_icu_NativeBreakIterator.cpp132
-rw-r--r--luni/src/test/java/libcore/java/text/BreakIteratorTest.java42
3 files changed, 156 insertions, 28 deletions
diff --git a/luni/src/main/java/libcore/icu/NativeBreakIterator.java b/luni/src/main/java/libcore/icu/NativeBreakIterator.java
index 9c10461..88ceb71 100644
--- a/luni/src/main/java/libcore/icu/NativeBreakIterator.java
+++ b/luni/src/main/java/libcore/icu/NativeBreakIterator.java
@@ -107,16 +107,20 @@ public final class NativeBreakIterator implements Cloneable {
}
public void setText(CharacterIterator newText) {
- this.charIter = newText;
StringBuilder sb = new StringBuilder();
for (char c = newText.first(); c != CharacterIterator.DONE; c = newText.next()) {
sb.append(c);
}
- setTextImpl(this.address, sb.toString());
+ setText(sb.toString(), newText);
}
public void setText(String newText) {
- setText(new StringCharacterIterator(newText));
+ setText(newText, new StringCharacterIterator(newText));
+ }
+
+ private void setText(String s, CharacterIterator it) {
+ this.charIter = it;
+ setTextImpl(this.address, s);
}
public boolean isBoundary(int offset) {
diff --git a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp b/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
index b3eab45..d430ede 100644
--- a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
+++ b/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
@@ -19,61 +19,143 @@
#include "JNIHelp.h"
#include "JniConstants.h"
#include "ErrorCode.h"
-#include "ScopedJavaUnicodeString.h"
+#include "JniException.h"
#include "ScopedUtfChars.h"
#include "unicode/ubrk.h"
#include "unicode/putil.h"
#include <stdlib.h>
-static jint getIterator(JNIEnv* env, jstring locale, UBreakIteratorType type) {
+/**
+ * ICU4C 4.6 doesn't let us update the pointers inside a UBreakIterator to track our char[] as it
+ * moves around the heap. This class pins the char[] for the lifetime of the
+ * java.text.BreakIterator. It also holds a global reference to the java.lang.String that owns the
+ * char[] so that the char[] can't be GCed.
+ */
+class BreakIteratorPeer {
+public:
+ static BreakIteratorPeer* fromAddress(jint address) {
+ return reinterpret_cast<BreakIteratorPeer*>(static_cast<uintptr_t>(address));
+ }
+
+ uintptr_t toAddress() {
+ return reinterpret_cast<uintptr_t>(this);
+ }
+
+ BreakIteratorPeer(UBreakIterator* it) : mIt(it), mString(NULL), mChars(NULL) {
+ }
+
+ void setText(JNIEnv* env, jstring s) {
+ releaseString(env);
+
+ mString = reinterpret_cast<jstring>(env->NewGlobalRef(s));
+ mChars = env->GetStringChars(mString, NULL);
+ if (mChars == NULL) {
+ return;
+ }
+
+ size_t charCount = env->GetStringLength(mString);
+ UErrorCode status = U_ZERO_ERROR;
+ ubrk_setText(mIt, mChars, charCount, &status);
+ icu4jni_error(env, status);
+ }
+
+ BreakIteratorPeer* clone(JNIEnv* env) {
+ UErrorCode status = U_ZERO_ERROR;
+ jint bufferSize = U_BRK_SAFECLONE_BUFFERSIZE;
+ UBreakIterator* it = ubrk_safeClone(mIt, NULL, &bufferSize, &status);
+ if (icu4jni_error(env, status)) {
+ return NULL;
+ }
+ BreakIteratorPeer* result = new BreakIteratorPeer(it);
+ if (mString != NULL) {
+ result->setText(env, mString);
+ }
+ return result;
+ }
+
+ void close(JNIEnv* env) {
+ if (mIt != NULL) {
+ ubrk_close(mIt);
+ mIt = NULL;
+ }
+ releaseString(env);
+ }
+
+ ~BreakIteratorPeer() {
+ if (mIt != NULL || mString != NULL) {
+ LOG_ALWAYS_FATAL("BreakIteratorPeer deleted but not closed");
+ }
+ }
+
+ UBreakIterator* breakIterator() {
+ return mIt;
+ }
+
+private:
+ UBreakIterator* mIt;
+
+ jstring mString;
+ const jchar* mChars;
+
+ void releaseString(JNIEnv* env) {
+ if (mString != NULL) {
+ env->ReleaseStringChars(mString, mChars);
+ env->DeleteGlobalRef(mString);
+ mString = NULL;
+ }
+ }
+
+ // Disallow copy and assignment.
+ BreakIteratorPeer(const BreakIteratorPeer&);
+ void operator=(const BreakIteratorPeer&);
+};
+
+static UBreakIterator* breakIterator(jint address) {
+ return BreakIteratorPeer::fromAddress(address)->breakIterator();
+}
+
+static jint makeIterator(JNIEnv* env, jstring locale, UBreakIteratorType type) {
UErrorCode status = U_ZERO_ERROR;
- ScopedUtfChars localeChars(env, locale);
+ const ScopedUtfChars localeChars(env, locale);
if (localeChars.c_str() == NULL) {
return 0;
}
UBreakIterator* it = ubrk_open(type, localeChars.c_str(), NULL, 0, &status);
- icu4jni_error(env, status);
- return reinterpret_cast<uintptr_t>(it);
+ if (icu4jni_error(env, status)) {
+ return NULL;
+ }
+ return (new BreakIteratorPeer(it))->toAddress();
}
static jint NativeBreakIterator_getCharacterInstanceImpl(JNIEnv* env, jclass, jstring locale) {
- return getIterator(env, locale, UBRK_CHARACTER);
+ return makeIterator(env, locale, UBRK_CHARACTER);
}
static jint NativeBreakIterator_getLineInstanceImpl(JNIEnv* env, jclass, jstring locale) {
- return getIterator(env, locale, UBRK_LINE);
+ return makeIterator(env, locale, UBRK_LINE);
}
static jint NativeBreakIterator_getSentenceInstanceImpl(JNIEnv* env, jclass, jstring locale) {
- return getIterator(env, locale, UBRK_SENTENCE);
+ return makeIterator(env, locale, UBRK_SENTENCE);
}
static jint NativeBreakIterator_getWordInstanceImpl(JNIEnv* env, jclass, jstring locale) {
- return getIterator(env, locale, UBRK_WORD);
+ return makeIterator(env, locale, UBRK_WORD);
}
-static UBreakIterator* breakIterator(jint address) {
- return reinterpret_cast<UBreakIterator*>(static_cast<uintptr_t>(address));
-}
-
-static void NativeBreakIterator_closeBreakIteratorImpl(JNIEnv*, jclass, jint address) {
- ubrk_close(breakIterator(address));
+static void NativeBreakIterator_closeBreakIteratorImpl(JNIEnv* env, jclass, jint address) {
+ BreakIteratorPeer* peer = BreakIteratorPeer::fromAddress(address);
+ peer->close(env);
+ delete peer;
}
static jint NativeBreakIterator_cloneImpl(JNIEnv* env, jclass, jint address) {
- UErrorCode status = U_ZERO_ERROR;
- jint bufferSize = U_BRK_SAFECLONE_BUFFERSIZE;
- UBreakIterator* it = ubrk_safeClone(breakIterator(address), NULL, &bufferSize, &status);
- icu4jni_error(env, status);
- return reinterpret_cast<uintptr_t>(it);
+ return BreakIteratorPeer::fromAddress(address)->clone(env)->toAddress();
}
static void NativeBreakIterator_setTextImpl(JNIEnv* env, jclass, jint address, jstring javaText) {
- ScopedJavaUnicodeString text(env, javaText);
- UnicodeString& s(text.unicodeString());
- UErrorCode status = U_ZERO_ERROR;
- ubrk_setText(breakIterator(address), s.getBuffer(), s.length(), &status);
- icu4jni_error(env, status);
+ BreakIteratorPeer* peer = BreakIteratorPeer::fromAddress(address);
+ peer->setText(env, javaText);
}
static jboolean NativeBreakIterator_isBoundaryImpl(JNIEnv*, jclass, jint address, jint offset) {
diff --git a/luni/src/test/java/libcore/java/text/BreakIteratorTest.java b/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
index dbc9a1c..45d31b5 100644
--- a/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
+++ b/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
@@ -50,6 +50,48 @@ public class BreakIteratorTest extends junit.framework.TestCase {
assertTrue("Incorrect BreakIterator", it2 != BreakIterator.getWordInstance());
}
+ public void testWordBoundaries() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 1024; ++i) {
+ if (i > 0) {
+ sb.append(' ');
+ }
+ sb.append("12345");
+ }
+ String s = sb.toString();
+
+ BreakIterator it = BreakIterator.getWordInstance(Locale.US);
+ it.setText(s);
+
+ // Check we're not leaking global references. 2048 would bust the VM's hard-coded limit.
+ for (int i = 0; i < 2048; ++i) {
+ it.setText(s);
+ }
+
+ BreakIterator clone = (BreakIterator) it.clone();
+
+ assertExpectedWordBoundaries(it, s);
+ assertExpectedWordBoundaries(clone, s);
+ }
+
+ private void assertExpectedWordBoundaries(BreakIterator it, String s) {
+ int expectedPos = 0;
+ int pos = it.first();
+ assertEquals(expectedPos, pos);
+ while (pos != BreakIterator.DONE) {
+ expectedPos += 5; // The five characters until the end of this word.
+ pos = it.next();
+ assertEquals(expectedPos, pos);
+
+ expectedPos += 1; // The space before the start of the next word...
+ if (expectedPos > s.length()) {
+ expectedPos = BreakIterator.DONE; // ...unless we're done.
+ }
+ pos = it.next();
+ assertEquals(expectedPos, pos);
+ }
+ }
+
public void testIsBoundary() {
BreakIterator it = BreakIterator.getCharacterInstance(Locale.US);
it.setText("hello");