Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5494171d authored by Seigo Nonaka's avatar Seigo Nonaka Committed by android-build-merger
Browse files

Merge "Introduce new constructor for not copying NoCopySpan" into pi-dev

am: 54ff524a

Change-Id: I2e11e189c240df026c478bb9d5a4cd97c5e30e9e
parents 09324731 54ff524a
Loading
Loading
Loading
Loading
+17 −3
Original line number Diff line number Diff line
@@ -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.
@@ -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) {
+61 −20
Original line number Diff line number Diff line
@@ -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
@@ -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]);
@@ -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;

@@ -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;
+17 −2
Original line number Diff line number Diff line
@@ -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) {
+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();
        }
    };
}
+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();
        }
    };
}