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

Commit 3463c04b authored by Siyamed Sinir's avatar Siyamed Sinir
Browse files

Fix SpannableStringBuilder.replace() exception.

When SpannableStringBuilder.replace is called with a source text that
contains a span with SPAN_PARAGRAPH flag, the code tries to retain the
spans. However when the paragraph boundary constraint is not satisfied,
setSpan method throws an exception. Updated the rule as: if the source
text has a span with SPAN_PARAGRAPH flag check if it can be copied into
the target. If not, discard the span. Also updated the JavaDoc for
Spanned and Editable.

Bug: 22521443
Change-Id: Ie8541e00bfdf5b8b0115ad7b26cb9f83a6a3ee55
parent 9d7272a6
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -40,10 +40,14 @@ extends CharSequence, GetChars, Spannable, Appendable
     * is Spanned, the spans from it are preserved into the Editable.
     * Existing spans within the Editable that entirely cover the replaced
     * range are retained, but any that were strictly within the range
     * that was replaced are removed.  As a special case, the cursor
     * position is preserved even when the entire range where it is
     * located is replaced.
     * that was replaced are removed. If the <code>source</code> contains a span
     * with {@link Spanned#SPAN_PARAGRAPH} flag, and it does not satisfy the
     * paragraph boundary constraint, it is not retained. As a special case, the
     * cursor position is preserved even when the entire range where it is located
     * is replaced.
     * @return  a reference to this object.
     *
     * @see Spanned#SPAN_PARAGRAPH
     */
    public Editable replace(int st, int en, CharSequence source, int start, int end);

+37 −16
Original line number Diff line number Diff line
@@ -421,8 +421,17 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable

                // Add span only if this object is not yet used as a span in this string
                if (getSpanStart(spans[i]) < 0) {
                    setSpan(false, spans[i], st - csStart + start, en - csStart + start,
                            sp.getSpanFlags(spans[i]) | SPAN_ADDED);
                    int copySpanStart = st - csStart + start;
                    int copySpanEnd = en - csStart + start;
                    int copySpanFlags = sp.getSpanFlags(spans[i]) | SPAN_ADDED;

                    int flagsStart = (copySpanFlags & START_MASK) >> START_SHIFT;
                    int flagsEnd = copySpanFlags & END_MASK;

                    if(!isInvalidParagraphStart(copySpanStart, flagsStart) &&
                            !isInvalidParagraphEnd(copySpanEnd, flagsEnd)) {
                        setSpan(false, spans[i], copySpanStart, copySpanEnd, copySpanFlags);
                    }
                }
            }
            restoreInvariants();
@@ -666,24 +675,14 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
        checkRange("setSpan", start, end);

        int flagsStart = (flags & START_MASK) >> START_SHIFT;
        if (flagsStart == PARAGRAPH) {
            if (start != 0 && start != length()) {
                char c = charAt(start - 1);

                if (c != '\n')
        if(isInvalidParagraphStart(start, flagsStart)) {
            throw new RuntimeException("PARAGRAPH span must start at paragraph boundary");
        }
        }

        int flagsEnd = flags & END_MASK;
        if (flagsEnd == PARAGRAPH) {
            if (end != 0 && end != length()) {
                char c = charAt(end - 1);

                if (c != '\n')
        if(isInvalidParagraphEnd(end, flagsEnd)) {
            throw new RuntimeException("PARAGRAPH span must end at paragraph boundary");
        }
        }

        // 0-length Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        if (flagsStart == POINT && flagsEnd == MARK && start == end) {
@@ -761,6 +760,28 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
        }
    }

    private final boolean isInvalidParagraphStart(int start, int flagsStart) {
        if (flagsStart == PARAGRAPH) {
            if (start != 0 && start != length()) {
                char c = charAt(start - 1);

                if (c != '\n') return true;
            }
        }
        return false;
    }

    private final boolean isInvalidParagraphEnd(int end, int flagsEnd) {
        if (flagsEnd == PARAGRAPH) {
            if (end != 0 && end != length()) {
                char c = charAt(end - 1);

                if (c != '\n') return true;
            }
        }
        return false;
    }

    /**
     * Remove the specified markup object from the buffer.
     */
+3 −1
Original line number Diff line number Diff line
@@ -81,7 +81,9 @@ extends CharSequence
     * immediately after a \n character, and if the \n
     * that anchors it is deleted, the endpoint is pulled to the
     * next \n that follows in the buffer (or to the end of
     * the buffer).
     * the buffer). If a span with SPAN_PARAGRAPH flag is pasted
     * into another text and the paragraph boundary constraint
     * is not satisfied, the span is discarded.
     */
    public static final int SPAN_PARAGRAPH =   0x33;