Loading core/java/android/text/SpannableString.java +17 −3 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package android.text; /** * This is the class for text whose content is immutable but to which * markup objects can be attached and detached. Loading @@ -26,12 +25,27 @@ public class SpannableString extends SpannableStringInternal implements CharSequence, GetChars, Spannable { /** * @param source source object to copy from * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source} * @hide */ public SpannableString(CharSequence source, boolean ignoreNoCopySpan) { super(source, 0, source.length(), ignoreNoCopySpan); } /** * For the backward compatibility reasons, this constructor copies all spans including {@link * android.text.NoCopySpan}. * @param source source text */ public SpannableString(CharSequence source) { super(source, 0, source.length()); this(source, false /* ignoreNoCopySpan */); // preserve existing NoCopySpan behavior } private SpannableString(CharSequence source, int start, int end) { super(source, start, end); // preserve existing NoCopySpan behavior super(source, start, end, false /* ignoreNoCopySpan */); } public static SpannableString valueOf(CharSequence source) { Loading core/java/android/text/SpannableStringInternal.java +61 −20 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ import java.lang.reflect.Array; /* package */ abstract class SpannableStringInternal { /* package */ SpannableStringInternal(CharSequence source, int start, int end) { int start, int end, boolean ignoreNoCopySpan) { if (start == 0 && end == source.length()) mText = source.toString(); else Loading @@ -38,24 +38,37 @@ import java.lang.reflect.Array; if (source instanceof Spanned) { if (source instanceof SpannableStringInternal) { copySpans((SpannableStringInternal) source, start, end); copySpans((SpannableStringInternal) source, start, end, ignoreNoCopySpan); } else { copySpans((Spanned) source, start, end); copySpans((Spanned) source, start, end, ignoreNoCopySpan); } } } /** * This unused method is left since this is listed in hidden api list. * * Due to backward compatibility reasons, we copy even NoCopySpan by default */ /* package */ SpannableStringInternal(CharSequence source, int start, int end) { this(source, start, end, false /* ignoreNoCopySpan */); } /** * Copies another {@link Spanned} object's spans between [start, end] into this object. * * @param src Source object to copy from. * @param start Start index in the source object. * @param end End index in the source object. * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source} */ private final void copySpans(Spanned src, int start, int end) { private void copySpans(Spanned src, int start, int end, boolean ignoreNoCopySpan) { Object[] spans = src.getSpans(start, end, Object.class); for (int i = 0; i < spans.length; i++) { if (ignoreNoCopySpan && spans[i] instanceof NoCopySpan) { continue; } int st = src.getSpanStart(spans[i]); int en = src.getSpanEnd(spans[i]); int fl = src.getSpanFlags(spans[i]); Loading @@ -76,35 +89,48 @@ import java.lang.reflect.Array; * @param src Source object to copy from. * @param start Start index in the source object. * @param end End index in the source object. * @param ignoreNoCopySpan copy NoCopySpan for backward compatible reasons. */ private final void copySpans(SpannableStringInternal src, int start, int end) { if (start == 0 && end == src.length()) { mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length); mSpanData = new int[src.mSpanData.length]; mSpanCount = src.mSpanCount; System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length); System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length); } else { private void copySpans(SpannableStringInternal src, int start, int end, boolean ignoreNoCopySpan) { int count = 0; int[] srcData = src.mSpanData; int limit = src.mSpanCount; final int[] srcData = src.mSpanData; final Object[] srcSpans = src.mSpans; final int limit = src.mSpanCount; boolean hasNoCopySpan = false; for (int i = 0; i < limit; i++) { int spanStart = srcData[i * COLUMNS + START]; int spanEnd = srcData[i * COLUMNS + END]; if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; if (srcSpans[i] instanceof NoCopySpan) { hasNoCopySpan = true; if (ignoreNoCopySpan) { continue; } } count++; } if (count == 0) return; Object[] srcSpans = src.mSpans; if (!hasNoCopySpan && start == 0 && end == src.length()) { mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length); mSpanData = new int[src.mSpanData.length]; mSpanCount = src.mSpanCount; System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length); System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length); } else { mSpanCount = count; mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount); mSpanData = new int[mSpans.length * COLUMNS]; for (int i = 0, j = 0; i < limit; i++) { int spanStart = srcData[i * COLUMNS + START]; int spanEnd = srcData[i * COLUMNS + END]; if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; if (isOutOfCopyRange(start, end, spanStart, spanEnd) || (ignoreNoCopySpan && srcSpans[i] instanceof NoCopySpan)) { continue; } if (spanStart < start) spanStart = start; if (spanEnd > end) spanEnd = end; Loading Loading @@ -494,6 +520,21 @@ import java.lang.reflect.Array; return hash; } /** * Following two unused methods are left since these are listed in hidden api list. * * Due to backward compatibility reasons, we copy even NoCopySpan by default */ private void copySpans(Spanned src, int start, int end) { copySpans(src, start, end, false); } private void copySpans(SpannableStringInternal src, int start, int end) { copySpans(src, start, end, false); } private String mText; private Object[] mSpans; private int[] mSpanData; Loading core/java/android/text/SpannedString.java +17 −2 Original line number Diff line number Diff line Loading @@ -26,12 +26,27 @@ public final class SpannedString extends SpannableStringInternal implements CharSequence, GetChars, Spanned { /** * @param source source object to copy from * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source} * @hide */ public SpannedString(CharSequence source, boolean ignoreNoCopySpan) { super(source, 0, source.length(), ignoreNoCopySpan); } /** * For the backward compatibility reasons, this constructor copies all spans including {@link * android.text.NoCopySpan}. * @param source source text */ public SpannedString(CharSequence source) { super(source, 0, source.length()); this(source, false /* ignoreNoCopySpan */); // preserve existing NoCopySpan behavior } private SpannedString(CharSequence source, int start, int end) { super(source, start, end); // preserve existing NoCopySpan behavior super(source, start, end, false /* ignoreNoCopySpan */); } public CharSequence subSequence(int start, int end) { Loading core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java 0 → 100644 +163 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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 android.text; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import android.annotation.NonNull; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.style.QuoteSpan; import android.text.style.UnderlineSpan; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class SpannableStringNoCopyTest { @Test public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // By default, copy NoCopySpans final SpannedString copied = new SpannedString(first); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(3, spans.length); } @Test public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // Do not copy NoCopySpan if specified so. final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(2, spans.length); for (int i = 0; i < spans.length; i++) { assertFalse(spans[i] instanceof NoCopySpan); } } @Test public void testCopyConstructor_copyNoCopySpans_OtherSpannableImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // By default, copy NoCopySpans final SpannedString copied = new SpannedString(new CustomSpannable(first)); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(3, spans.length); } @Test public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannableImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // Do not copy NoCopySpan if specified so. final SpannedString copied = new SpannedString( new CustomSpannable(first), false /* copyNoCopySpan */); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(2, spans.length); for (int i = 0; i < spans.length; i++) { assertFalse(spans[i] instanceof NoCopySpan); } } // A custom implementation of Spannable. private static class CustomSpannable implements Spannable { private final @NonNull Spannable mText; CustomSpannable(@NonNull Spannable text) { mText = text; } @Override public void setSpan(Object what, int start, int end, int flags) { mText.setSpan(what, start, end, flags); } @Override public void removeSpan(Object what) { mText.removeSpan(what); } @Override public <T> T[] getSpans(int start, int end, Class<T> type) { return mText.getSpans(start, end, type); } @Override public int getSpanStart(Object tag) { return mText.getSpanStart(tag); } @Override public int getSpanEnd(Object tag) { return mText.getSpanEnd(tag); } @Override public int getSpanFlags(Object tag) { return mText.getSpanFlags(tag); } @Override public int nextSpanTransition(int start, int limit, Class type) { return mText.nextSpanTransition(start, limit, type); } @Override public int length() { return mText.length(); } @Override public char charAt(int index) { return mText.charAt(index); } @Override public CharSequence subSequence(int start, int end) { return mText.subSequence(start, end); } @Override public String toString() { return mText.toString(); } }; } core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java 0 → 100644 +153 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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 android.text; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import android.annotation.NonNull; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.style.QuoteSpan; import android.text.style.UnderlineSpan; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class SpannedStringNoCopyTest { @Test public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // By default, copy NoCopySpans final SpannedString copied = new SpannedString(first); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(3, spans.length); } @Test public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // Do not copy NoCopySpan if specified so. final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(2, spans.length); for (int i = 0; i < spans.length; i++) { assertFalse(spans[i] instanceof NoCopySpan); } } @Test public void testCopyConstructor_copyNoCopySpans_OtherSpannedImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // By default, copy NoCopySpans final SpannedString copied = new SpannedString(new CustomSpanned(first)); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(3, spans.length); } @Test public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannedImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // Do not copy NoCopySpan if specified so. final SpannedString copied = new SpannedString( new CustomSpanned(first), false /* copyNoCopySpan */); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(2, spans.length); for (int i = 0; i < spans.length; i++) { assertFalse(spans[i] instanceof NoCopySpan); } } // A custom implementation of Spanned private static class CustomSpanned implements Spanned { private final @NonNull Spanned mText; CustomSpanned(@NonNull Spannable text) { mText = text; } @Override public <T> T[] getSpans(int start, int end, Class<T> type) { return mText.getSpans(start, end, type); } @Override public int getSpanStart(Object tag) { return mText.getSpanStart(tag); } @Override public int getSpanEnd(Object tag) { return mText.getSpanEnd(tag); } @Override public int getSpanFlags(Object tag) { return mText.getSpanFlags(tag); } @Override public int nextSpanTransition(int start, int limit, Class type) { return mText.nextSpanTransition(start, limit, type); } @Override public int length() { return mText.length(); } @Override public char charAt(int index) { return mText.charAt(index); } @Override public CharSequence subSequence(int start, int end) { return mText.subSequence(start, end); } @Override public String toString() { return mText.toString(); } }; } Loading
core/java/android/text/SpannableString.java +17 −3 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package android.text; /** * This is the class for text whose content is immutable but to which * markup objects can be attached and detached. Loading @@ -26,12 +25,27 @@ public class SpannableString extends SpannableStringInternal implements CharSequence, GetChars, Spannable { /** * @param source source object to copy from * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source} * @hide */ public SpannableString(CharSequence source, boolean ignoreNoCopySpan) { super(source, 0, source.length(), ignoreNoCopySpan); } /** * For the backward compatibility reasons, this constructor copies all spans including {@link * android.text.NoCopySpan}. * @param source source text */ public SpannableString(CharSequence source) { super(source, 0, source.length()); this(source, false /* ignoreNoCopySpan */); // preserve existing NoCopySpan behavior } private SpannableString(CharSequence source, int start, int end) { super(source, start, end); // preserve existing NoCopySpan behavior super(source, start, end, false /* ignoreNoCopySpan */); } public static SpannableString valueOf(CharSequence source) { Loading
core/java/android/text/SpannableStringInternal.java +61 −20 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ import java.lang.reflect.Array; /* package */ abstract class SpannableStringInternal { /* package */ SpannableStringInternal(CharSequence source, int start, int end) { int start, int end, boolean ignoreNoCopySpan) { if (start == 0 && end == source.length()) mText = source.toString(); else Loading @@ -38,24 +38,37 @@ import java.lang.reflect.Array; if (source instanceof Spanned) { if (source instanceof SpannableStringInternal) { copySpans((SpannableStringInternal) source, start, end); copySpans((SpannableStringInternal) source, start, end, ignoreNoCopySpan); } else { copySpans((Spanned) source, start, end); copySpans((Spanned) source, start, end, ignoreNoCopySpan); } } } /** * This unused method is left since this is listed in hidden api list. * * Due to backward compatibility reasons, we copy even NoCopySpan by default */ /* package */ SpannableStringInternal(CharSequence source, int start, int end) { this(source, start, end, false /* ignoreNoCopySpan */); } /** * Copies another {@link Spanned} object's spans between [start, end] into this object. * * @param src Source object to copy from. * @param start Start index in the source object. * @param end End index in the source object. * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source} */ private final void copySpans(Spanned src, int start, int end) { private void copySpans(Spanned src, int start, int end, boolean ignoreNoCopySpan) { Object[] spans = src.getSpans(start, end, Object.class); for (int i = 0; i < spans.length; i++) { if (ignoreNoCopySpan && spans[i] instanceof NoCopySpan) { continue; } int st = src.getSpanStart(spans[i]); int en = src.getSpanEnd(spans[i]); int fl = src.getSpanFlags(spans[i]); Loading @@ -76,35 +89,48 @@ import java.lang.reflect.Array; * @param src Source object to copy from. * @param start Start index in the source object. * @param end End index in the source object. * @param ignoreNoCopySpan copy NoCopySpan for backward compatible reasons. */ private final void copySpans(SpannableStringInternal src, int start, int end) { if (start == 0 && end == src.length()) { mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length); mSpanData = new int[src.mSpanData.length]; mSpanCount = src.mSpanCount; System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length); System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length); } else { private void copySpans(SpannableStringInternal src, int start, int end, boolean ignoreNoCopySpan) { int count = 0; int[] srcData = src.mSpanData; int limit = src.mSpanCount; final int[] srcData = src.mSpanData; final Object[] srcSpans = src.mSpans; final int limit = src.mSpanCount; boolean hasNoCopySpan = false; for (int i = 0; i < limit; i++) { int spanStart = srcData[i * COLUMNS + START]; int spanEnd = srcData[i * COLUMNS + END]; if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; if (srcSpans[i] instanceof NoCopySpan) { hasNoCopySpan = true; if (ignoreNoCopySpan) { continue; } } count++; } if (count == 0) return; Object[] srcSpans = src.mSpans; if (!hasNoCopySpan && start == 0 && end == src.length()) { mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length); mSpanData = new int[src.mSpanData.length]; mSpanCount = src.mSpanCount; System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length); System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length); } else { mSpanCount = count; mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount); mSpanData = new int[mSpans.length * COLUMNS]; for (int i = 0, j = 0; i < limit; i++) { int spanStart = srcData[i * COLUMNS + START]; int spanEnd = srcData[i * COLUMNS + END]; if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; if (isOutOfCopyRange(start, end, spanStart, spanEnd) || (ignoreNoCopySpan && srcSpans[i] instanceof NoCopySpan)) { continue; } if (spanStart < start) spanStart = start; if (spanEnd > end) spanEnd = end; Loading Loading @@ -494,6 +520,21 @@ import java.lang.reflect.Array; return hash; } /** * Following two unused methods are left since these are listed in hidden api list. * * Due to backward compatibility reasons, we copy even NoCopySpan by default */ private void copySpans(Spanned src, int start, int end) { copySpans(src, start, end, false); } private void copySpans(SpannableStringInternal src, int start, int end) { copySpans(src, start, end, false); } private String mText; private Object[] mSpans; private int[] mSpanData; Loading
core/java/android/text/SpannedString.java +17 −2 Original line number Diff line number Diff line Loading @@ -26,12 +26,27 @@ public final class SpannedString extends SpannableStringInternal implements CharSequence, GetChars, Spanned { /** * @param source source object to copy from * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source} * @hide */ public SpannedString(CharSequence source, boolean ignoreNoCopySpan) { super(source, 0, source.length(), ignoreNoCopySpan); } /** * For the backward compatibility reasons, this constructor copies all spans including {@link * android.text.NoCopySpan}. * @param source source text */ public SpannedString(CharSequence source) { super(source, 0, source.length()); this(source, false /* ignoreNoCopySpan */); // preserve existing NoCopySpan behavior } private SpannedString(CharSequence source, int start, int end) { super(source, start, end); // preserve existing NoCopySpan behavior super(source, start, end, false /* ignoreNoCopySpan */); } public CharSequence subSequence(int start, int end) { Loading
core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java 0 → 100644 +163 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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 android.text; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import android.annotation.NonNull; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.style.QuoteSpan; import android.text.style.UnderlineSpan; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class SpannableStringNoCopyTest { @Test public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // By default, copy NoCopySpans final SpannedString copied = new SpannedString(first); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(3, spans.length); } @Test public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // Do not copy NoCopySpan if specified so. final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(2, spans.length); for (int i = 0; i < spans.length; i++) { assertFalse(spans[i] instanceof NoCopySpan); } } @Test public void testCopyConstructor_copyNoCopySpans_OtherSpannableImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // By default, copy NoCopySpans final SpannedString copied = new SpannedString(new CustomSpannable(first)); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(3, spans.length); } @Test public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannableImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // Do not copy NoCopySpan if specified so. final SpannedString copied = new SpannedString( new CustomSpannable(first), false /* copyNoCopySpan */); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(2, spans.length); for (int i = 0; i < spans.length; i++) { assertFalse(spans[i] instanceof NoCopySpan); } } // A custom implementation of Spannable. private static class CustomSpannable implements Spannable { private final @NonNull Spannable mText; CustomSpannable(@NonNull Spannable text) { mText = text; } @Override public void setSpan(Object what, int start, int end, int flags) { mText.setSpan(what, start, end, flags); } @Override public void removeSpan(Object what) { mText.removeSpan(what); } @Override public <T> T[] getSpans(int start, int end, Class<T> type) { return mText.getSpans(start, end, type); } @Override public int getSpanStart(Object tag) { return mText.getSpanStart(tag); } @Override public int getSpanEnd(Object tag) { return mText.getSpanEnd(tag); } @Override public int getSpanFlags(Object tag) { return mText.getSpanFlags(tag); } @Override public int nextSpanTransition(int start, int limit, Class type) { return mText.nextSpanTransition(start, limit, type); } @Override public int length() { return mText.length(); } @Override public char charAt(int index) { return mText.charAt(index); } @Override public CharSequence subSequence(int start, int end) { return mText.subSequence(start, end); } @Override public String toString() { return mText.toString(); } }; }
core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java 0 → 100644 +153 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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 android.text; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import android.annotation.NonNull; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.style.QuoteSpan; import android.text.style.UnderlineSpan; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class SpannedStringNoCopyTest { @Test public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // By default, copy NoCopySpans final SpannedString copied = new SpannedString(first); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(3, spans.length); } @Test public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // Do not copy NoCopySpan if specified so. final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(2, spans.length); for (int i = 0; i < spans.length; i++) { assertFalse(spans[i] instanceof NoCopySpan); } } @Test public void testCopyConstructor_copyNoCopySpans_OtherSpannedImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // By default, copy NoCopySpans final SpannedString copied = new SpannedString(new CustomSpanned(first)); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(3, spans.length); } @Test public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannedImpl() { final SpannableString first = new SpannableString("t\nest data"); first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); // Do not copy NoCopySpan if specified so. final SpannedString copied = new SpannedString( new CustomSpanned(first), false /* copyNoCopySpan */); final Object[] spans = copied.getSpans(0, copied.length(), Object.class); assertNotNull(spans); assertEquals(2, spans.length); for (int i = 0; i < spans.length; i++) { assertFalse(spans[i] instanceof NoCopySpan); } } // A custom implementation of Spanned private static class CustomSpanned implements Spanned { private final @NonNull Spanned mText; CustomSpanned(@NonNull Spannable text) { mText = text; } @Override public <T> T[] getSpans(int start, int end, Class<T> type) { return mText.getSpans(start, end, type); } @Override public int getSpanStart(Object tag) { return mText.getSpanStart(tag); } @Override public int getSpanEnd(Object tag) { return mText.getSpanEnd(tag); } @Override public int getSpanFlags(Object tag) { return mText.getSpanFlags(tag); } @Override public int nextSpanTransition(int start, int limit, Class type) { return mText.nextSpanTransition(start, limit, type); } @Override public int length() { return mText.length(); } @Override public char charAt(int index) { return mText.charAt(index); } @Override public CharSequence subSequence(int start, int end) { return mText.subSequence(start, end); } @Override public String toString() { return mText.toString(); } }; }