13483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka/*
23483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka * Copyright (C) 2018 The Android Open Source Project
33483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka *
43483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka * Licensed under the Apache License, Version 2.0 (the "License");
53483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka * you may not use this file except in compliance with the License.
63483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka * You may obtain a copy of the License at
73483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka *
83483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka *      http://www.apache.org/licenses/LICENSE-2.0
93483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka *
103483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka * Unless required by applicable law or agreed to in writing, software
113483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka * distributed under the License is distributed on an "AS IS" BASIS,
123483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka * See the License for the specific language governing permissions and
143483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka * limitations under the License.
153483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka */
163483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
173483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakapackage android.text;
183483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
193483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakaimport static org.junit.Assert.assertEquals;
203483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakaimport static org.junit.Assert.assertFalse;
213483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakaimport static org.junit.Assert.assertNotNull;
223483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
233483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakaimport android.annotation.NonNull;
243483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakaimport android.support.test.filters.SmallTest;
253483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakaimport android.support.test.runner.AndroidJUnit4;
263483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakaimport android.text.style.QuoteSpan;
273483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakaimport android.text.style.UnderlineSpan;
283483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
293483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakaimport org.junit.Test;
303483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakaimport org.junit.runner.RunWith;
313483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
323483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka@SmallTest
333483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka@RunWith(AndroidJUnit4.class)
343483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonakapublic class SpannedStringNoCopyTest {
353483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    @Test
363483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() {
373483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final SpannableString first = new SpannableString("t\nest data");
383483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
393483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
403483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
413483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
423483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        // By default, copy NoCopySpans
433483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final SpannedString copied = new SpannedString(first);
443483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
453483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        assertNotNull(spans);
463483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        assertEquals(3, spans.length);
473483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    }
483483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
493483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    @Test
503483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() {
513483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final SpannableString first = new SpannableString("t\nest data");
523483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
533483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
543483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
553483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
563483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        // Do not copy NoCopySpan if specified so.
57bd1632355ec72a7a893334a87d103d11ba4c2593Seigo Nonaka        final SpannedString copied = new SpannedString(first, true /* ignoreNoCopySpan */);
583483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
593483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        assertNotNull(spans);
603483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        assertEquals(2, spans.length);
613483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
623483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        for (int i = 0; i < spans.length; i++) {
633483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            assertFalse(spans[i] instanceof NoCopySpan);
643483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
653483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    }
663483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
673483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    @Test
683483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    public void testCopyConstructor_copyNoCopySpans_OtherSpannedImpl() {
693483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final SpannableString first = new SpannableString("t\nest data");
703483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
713483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
723483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
733483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
743483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        // By default, copy NoCopySpans
753483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final SpannedString copied = new SpannedString(new CustomSpanned(first));
763483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
773483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        assertNotNull(spans);
783483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        assertEquals(3, spans.length);
793483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    }
803483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
813483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    @Test
823483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannedImpl() {
833483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final SpannableString first = new SpannableString("t\nest data");
843483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
853483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
863483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
873483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
883483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        // Do not copy NoCopySpan if specified so.
893483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final SpannedString copied = new SpannedString(
90bd1632355ec72a7a893334a87d103d11ba4c2593Seigo Nonaka                new CustomSpanned(first), true /* ignoreNoCopySpan */);
913483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
923483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        assertNotNull(spans);
933483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        assertEquals(2, spans.length);
943483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
953483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        for (int i = 0; i < spans.length; i++) {
963483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            assertFalse(spans[i] instanceof NoCopySpan);
973483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
983483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    }
993483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1003483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    // A custom implementation of Spanned
1013483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    private static class CustomSpanned implements Spanned {
1023483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        private final @NonNull Spanned mText;
1033483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1043483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        CustomSpanned(@NonNull Spannable text) {
1053483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            mText = text;
1063483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
1073483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1083483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        @Override
1093483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        public <T> T[] getSpans(int start, int end, Class<T> type) {
1103483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            return mText.getSpans(start, end, type);
1113483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
1123483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1133483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        @Override
1143483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        public int getSpanStart(Object tag) {
1153483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            return mText.getSpanStart(tag);
1163483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
1173483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1183483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        @Override
1193483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        public int getSpanEnd(Object tag) {
1203483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            return mText.getSpanEnd(tag);
1213483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
1223483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1233483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        @Override
1243483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        public int getSpanFlags(Object tag) {
1253483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            return mText.getSpanFlags(tag);
1263483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
1273483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1283483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        @Override
1293483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        public int nextSpanTransition(int start, int limit, Class type) {
1303483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            return mText.nextSpanTransition(start, limit, type);
1313483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
1323483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1333483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        @Override
1343483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        public int length() {
1353483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            return mText.length();
1363483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
1373483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1383483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        @Override
1393483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        public char charAt(int index) {
1403483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            return mText.charAt(index);
1413483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
1423483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1433483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        @Override
1443483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        public CharSequence subSequence(int start, int end) {
1453483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            return mText.subSequence(start, end);
1463483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
1473483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka
1483483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        @Override
1493483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        public String toString() {
1503483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka            return mText.toString();
1513483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka        }
1523483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka    };
1533483bc7d648ad96916eb68203103dc9668dd0be7Seigo Nonaka}
154