Loading core/java/android/text/SpannableStringBuilder.java +26 −37 Original line number Original line Diff line number Diff line Loading @@ -98,7 +98,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (en > end - start) if (en > end - start) en = end - start; en = end - start; setSpan(false, spans[i], st, en, fl); setSpan(false, spans[i], st, en, fl, false/*enforceParagraph*/); } } restoreInvariants(); restoreInvariants(); } } Loading Loading @@ -355,7 +355,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } } if (spanStart != ost || spanEnd != oen) { if (spanStart != ost || spanEnd != oen) { setSpan(false, mSpans[i], spanStart, spanEnd, mSpanFlags[i]); setSpan(false, mSpans[i], spanStart, spanEnd, mSpanFlags[i], true/*enforceParagraph*/); changed = true; changed = true; } } } } Loading Loading @@ -430,13 +431,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable int copySpanEnd = en - csStart + start; int copySpanEnd = en - csStart + start; int copySpanFlags = sp.getSpanFlags(spans[i]) | SPAN_ADDED; int copySpanFlags = sp.getSpanFlags(spans[i]) | SPAN_ADDED; int flagsStart = (copySpanFlags & START_MASK) >> START_SHIFT; setSpan(false, spans[i], copySpanStart, copySpanEnd, copySpanFlags, int flagsEnd = copySpanFlags & END_MASK; false/*enforceParagraph*/); if(!isInvalidParagraphStart(copySpanStart, flagsStart) && !isInvalidParagraphEnd(copySpanEnd, flagsEnd)) { setSpan(false, spans[i], copySpanStart, copySpanEnd, copySpanFlags); } } } } } restoreInvariants(); restoreInvariants(); Loading Loading @@ -558,7 +554,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable changed = true; changed = true; setSpan(false, Selection.SELECTION_START, selectionStart, selectionStart, setSpan(false, Selection.SELECTION_START, selectionStart, selectionStart, Spanned.SPAN_POINT_POINT); Spanned.SPAN_POINT_POINT, true/*enforceParagraph*/); } } if (selectionEnd > start && selectionEnd < end) { if (selectionEnd > start && selectionEnd < end) { final long diff = selectionEnd - start; final long diff = selectionEnd - start; Loading @@ -567,7 +563,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable changed = true; changed = true; setSpan(false, Selection.SELECTION_END, selectionEnd, selectionEnd, setSpan(false, Selection.SELECTION_END, selectionEnd, selectionEnd, Spanned.SPAN_POINT_POINT); Spanned.SPAN_POINT_POINT, true/*enforceParagraph*/); } } if (changed) { if (changed) { restoreInvariants(); restoreInvariants(); Loading Loading @@ -673,23 +669,34 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * inserted at the start or end of the span's range. * inserted at the start or end of the span's range. */ */ public void setSpan(Object what, int start, int end, int flags) { public void setSpan(Object what, int start, int end, int flags) { setSpan(true, what, start, end, flags); setSpan(true, what, start, end, flags, true/*enforceParagraph*/); } } // Note: if send is false, then it is the caller's responsibility to restore // Note: if send is false, then it is the caller's responsibility to restore // invariants. If send is false and the span already exists, then this method // invariants. If send is false and the span already exists, then this method // will not change the index of any spans. // will not change the index of any spans. private void setSpan(boolean send, Object what, int start, int end, int flags) { private void setSpan(boolean send, Object what, int start, int end, int flags, boolean enforceParagraph) { checkRange("setSpan", start, end); checkRange("setSpan", start, end); int flagsStart = (flags & START_MASK) >> START_SHIFT; int flagsStart = (flags & START_MASK) >> START_SHIFT; if(isInvalidParagraphStart(start, flagsStart)) { if (isInvalidParagraph(start, flagsStart)) { throw new RuntimeException("PARAGRAPH span must start at paragraph boundary"); if (!enforceParagraph) { // do not set the span return; } throw new RuntimeException("PARAGRAPH span must start at paragraph boundary" + " (" + start + " follows " + charAt(start - 1) + ")"); } } int flagsEnd = flags & END_MASK; int flagsEnd = flags & END_MASK; if(isInvalidParagraphEnd(end, flagsEnd)) { if (isInvalidParagraph(end, flagsEnd)) { throw new RuntimeException("PARAGRAPH span must end at paragraph boundary"); if (!enforceParagraph) { // do not set the span return; } throw new RuntimeException("PARAGRAPH span must end at paragraph boundary" + " (" + end + " follows " + charAt(end - 1) + ")"); } } // 0-length Spanned.SPAN_EXCLUSIVE_EXCLUSIVE // 0-length Spanned.SPAN_EXCLUSIVE_EXCLUSIVE Loading Loading @@ -767,26 +774,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } } } } private final boolean isInvalidParagraphStart(int start, int flagsStart) { private boolean isInvalidParagraph(int index, int flag) { if (flagsStart == PARAGRAPH) { return flag == PARAGRAPH && index != 0 && index != length() && charAt(index - 1) != '\n'; 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; } } /** /** Loading core/java/android/text/SpannableStringInternal.java +23 −15 Original line number Original line Diff line number Diff line Loading @@ -65,7 +65,7 @@ import java.lang.reflect.Array; if (en > end) if (en > end) en = end; en = end; setSpan(spans[i], st - start, en - start, fl); setSpan(spans[i], st - start, en - start, fl, false/*enforceParagraph*/); } } } } Loading Loading @@ -149,28 +149,36 @@ import java.lang.reflect.Array; } } /* package */ void setSpan(Object what, int start, int end, int flags) { /* package */ void setSpan(Object what, int start, int end, int flags) { setSpan(what, start, end, flags, true/*enforceParagraph*/); } private boolean isIndexFollowsNextLine(int index) { return index != 0 && index != length() && charAt(index - 1) != '\n'; } private void setSpan(Object what, int start, int end, int flags, boolean enforceParagraph) { int nstart = start; int nstart = start; int nend = end; int nend = end; checkRange("setSpan", start, end); checkRange("setSpan", start, end); if ((flags & Spannable.SPAN_PARAGRAPH) == Spannable.SPAN_PARAGRAPH) { if ((flags & Spannable.SPAN_PARAGRAPH) == Spannable.SPAN_PARAGRAPH) { if (start != 0 && start != length()) { if (isIndexFollowsNextLine(start)) { char c = charAt(start - 1); if (!enforceParagraph) { // do not set the span if (c != '\n') return; throw new RuntimeException( } "PARAGRAPH span must start at paragraph boundary" + throw new RuntimeException("PARAGRAPH span must start at paragraph boundary" " (" + start + " follows " + c + ")"); + " (" + start + " follows " + charAt(start - 1) + ")"); } } if (end != 0 && end != length()) { if (isIndexFollowsNextLine(end)) { char c = charAt(end - 1); if (!enforceParagraph) { // do not set the span if (c != '\n') return; throw new RuntimeException( } "PARAGRAPH span must end at paragraph boundary" + throw new RuntimeException("PARAGRAPH span must end at paragraph boundary" " (" + end + " follows " + c + ")"); + " (" + end + " follows " + charAt(end - 1) + ")"); } } } } Loading Loading
core/java/android/text/SpannableStringBuilder.java +26 −37 Original line number Original line Diff line number Diff line Loading @@ -98,7 +98,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (en > end - start) if (en > end - start) en = end - start; en = end - start; setSpan(false, spans[i], st, en, fl); setSpan(false, spans[i], st, en, fl, false/*enforceParagraph*/); } } restoreInvariants(); restoreInvariants(); } } Loading Loading @@ -355,7 +355,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } } if (spanStart != ost || spanEnd != oen) { if (spanStart != ost || spanEnd != oen) { setSpan(false, mSpans[i], spanStart, spanEnd, mSpanFlags[i]); setSpan(false, mSpans[i], spanStart, spanEnd, mSpanFlags[i], true/*enforceParagraph*/); changed = true; changed = true; } } } } Loading Loading @@ -430,13 +431,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable int copySpanEnd = en - csStart + start; int copySpanEnd = en - csStart + start; int copySpanFlags = sp.getSpanFlags(spans[i]) | SPAN_ADDED; int copySpanFlags = sp.getSpanFlags(spans[i]) | SPAN_ADDED; int flagsStart = (copySpanFlags & START_MASK) >> START_SHIFT; setSpan(false, spans[i], copySpanStart, copySpanEnd, copySpanFlags, int flagsEnd = copySpanFlags & END_MASK; false/*enforceParagraph*/); if(!isInvalidParagraphStart(copySpanStart, flagsStart) && !isInvalidParagraphEnd(copySpanEnd, flagsEnd)) { setSpan(false, spans[i], copySpanStart, copySpanEnd, copySpanFlags); } } } } } restoreInvariants(); restoreInvariants(); Loading Loading @@ -558,7 +554,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable changed = true; changed = true; setSpan(false, Selection.SELECTION_START, selectionStart, selectionStart, setSpan(false, Selection.SELECTION_START, selectionStart, selectionStart, Spanned.SPAN_POINT_POINT); Spanned.SPAN_POINT_POINT, true/*enforceParagraph*/); } } if (selectionEnd > start && selectionEnd < end) { if (selectionEnd > start && selectionEnd < end) { final long diff = selectionEnd - start; final long diff = selectionEnd - start; Loading @@ -567,7 +563,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable changed = true; changed = true; setSpan(false, Selection.SELECTION_END, selectionEnd, selectionEnd, setSpan(false, Selection.SELECTION_END, selectionEnd, selectionEnd, Spanned.SPAN_POINT_POINT); Spanned.SPAN_POINT_POINT, true/*enforceParagraph*/); } } if (changed) { if (changed) { restoreInvariants(); restoreInvariants(); Loading Loading @@ -673,23 +669,34 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * inserted at the start or end of the span's range. * inserted at the start or end of the span's range. */ */ public void setSpan(Object what, int start, int end, int flags) { public void setSpan(Object what, int start, int end, int flags) { setSpan(true, what, start, end, flags); setSpan(true, what, start, end, flags, true/*enforceParagraph*/); } } // Note: if send is false, then it is the caller's responsibility to restore // Note: if send is false, then it is the caller's responsibility to restore // invariants. If send is false and the span already exists, then this method // invariants. If send is false and the span already exists, then this method // will not change the index of any spans. // will not change the index of any spans. private void setSpan(boolean send, Object what, int start, int end, int flags) { private void setSpan(boolean send, Object what, int start, int end, int flags, boolean enforceParagraph) { checkRange("setSpan", start, end); checkRange("setSpan", start, end); int flagsStart = (flags & START_MASK) >> START_SHIFT; int flagsStart = (flags & START_MASK) >> START_SHIFT; if(isInvalidParagraphStart(start, flagsStart)) { if (isInvalidParagraph(start, flagsStart)) { throw new RuntimeException("PARAGRAPH span must start at paragraph boundary"); if (!enforceParagraph) { // do not set the span return; } throw new RuntimeException("PARAGRAPH span must start at paragraph boundary" + " (" + start + " follows " + charAt(start - 1) + ")"); } } int flagsEnd = flags & END_MASK; int flagsEnd = flags & END_MASK; if(isInvalidParagraphEnd(end, flagsEnd)) { if (isInvalidParagraph(end, flagsEnd)) { throw new RuntimeException("PARAGRAPH span must end at paragraph boundary"); if (!enforceParagraph) { // do not set the span return; } throw new RuntimeException("PARAGRAPH span must end at paragraph boundary" + " (" + end + " follows " + charAt(end - 1) + ")"); } } // 0-length Spanned.SPAN_EXCLUSIVE_EXCLUSIVE // 0-length Spanned.SPAN_EXCLUSIVE_EXCLUSIVE Loading Loading @@ -767,26 +774,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } } } } private final boolean isInvalidParagraphStart(int start, int flagsStart) { private boolean isInvalidParagraph(int index, int flag) { if (flagsStart == PARAGRAPH) { return flag == PARAGRAPH && index != 0 && index != length() && charAt(index - 1) != '\n'; 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; } } /** /** Loading
core/java/android/text/SpannableStringInternal.java +23 −15 Original line number Original line Diff line number Diff line Loading @@ -65,7 +65,7 @@ import java.lang.reflect.Array; if (en > end) if (en > end) en = end; en = end; setSpan(spans[i], st - start, en - start, fl); setSpan(spans[i], st - start, en - start, fl, false/*enforceParagraph*/); } } } } Loading Loading @@ -149,28 +149,36 @@ import java.lang.reflect.Array; } } /* package */ void setSpan(Object what, int start, int end, int flags) { /* package */ void setSpan(Object what, int start, int end, int flags) { setSpan(what, start, end, flags, true/*enforceParagraph*/); } private boolean isIndexFollowsNextLine(int index) { return index != 0 && index != length() && charAt(index - 1) != '\n'; } private void setSpan(Object what, int start, int end, int flags, boolean enforceParagraph) { int nstart = start; int nstart = start; int nend = end; int nend = end; checkRange("setSpan", start, end); checkRange("setSpan", start, end); if ((flags & Spannable.SPAN_PARAGRAPH) == Spannable.SPAN_PARAGRAPH) { if ((flags & Spannable.SPAN_PARAGRAPH) == Spannable.SPAN_PARAGRAPH) { if (start != 0 && start != length()) { if (isIndexFollowsNextLine(start)) { char c = charAt(start - 1); if (!enforceParagraph) { // do not set the span if (c != '\n') return; throw new RuntimeException( } "PARAGRAPH span must start at paragraph boundary" + throw new RuntimeException("PARAGRAPH span must start at paragraph boundary" " (" + start + " follows " + c + ")"); + " (" + start + " follows " + charAt(start - 1) + ")"); } } if (end != 0 && end != length()) { if (isIndexFollowsNextLine(end)) { char c = charAt(end - 1); if (!enforceParagraph) { // do not set the span if (c != '\n') return; throw new RuntimeException( } "PARAGRAPH span must end at paragraph boundary" + throw new RuntimeException("PARAGRAPH span must end at paragraph boundary" " (" + end + " follows " + c + ")"); + " (" + end + " follows " + charAt(end - 1) + ")"); } } } } Loading