am e912a16b: am 316ca505: Merge "Re-use SpanSets in TextLine" into ics-mr1

* commit 'e912a16ba2a6e913b47893bc99c9a846d7ce80a9':
  Re-use SpanSets in TextLine
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index b73d900..b3df8ff 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -59,6 +59,12 @@
     private boolean mCharsValid;
     private Spanned mSpanned;
     private final TextPaint mWorkPaint = new TextPaint();
+    private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet =
+            new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class);
+    private final SpanSet<CharacterStyle> mCharacterStyleSpanSet =
+            new SpanSet<CharacterStyle>(CharacterStyle.class);
+    private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet =
+            new SpanSet<ReplacementSpan>(ReplacementSpan.class);
 
     private static final TextLine[] sCached = new TextLine[3];
 
@@ -119,7 +125,6 @@
      * @param hasTabs true if the line might contain tabs or emoji
      * @param tabStops the tabStops. Can be null.
      */
-    @SuppressWarnings("null")
     void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
             Directions directions, boolean hasTabs, TabStops tabStops) {
         mPaint = paint;
@@ -135,12 +140,10 @@
         mSpanned = null;
 
         boolean hasReplacement = false;
-        SpanSet<ReplacementSpan> replacementSpans = null;
         if (text instanceof Spanned) {
             mSpanned = (Spanned) text;
-            replacementSpans = new SpanSet<ReplacementSpan>(mSpanned, start, limit,
-                    ReplacementSpan.class);
-            hasReplacement = replacementSpans.numberOfSpans > 0;
+            mReplacementSpanSpanSet.init(mSpanned, start, limit);
+            hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
         }
 
         mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
@@ -158,9 +161,8 @@
                 // zero-width characters.
                 char[] chars = mChars;
                 for (int i = start, inext; i < limit; i = inext) {
-                    // replacementSpans cannot be null if hasReplacement is true
-                    inext = replacementSpans.getNextTransition(i, limit);
-                    if (replacementSpans.hasSpansIntersecting(i, inext)) {
+                    inext = mReplacementSpanSpanSet.getNextTransition(i, limit);
+                    if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) {
                         // transition into a span
                         chars[i - start] = '\ufffc';
                         for (int j = i - start + 1, e = inext - start; j < e; ++j) {
@@ -854,21 +856,30 @@
     }
 
     private static class SpanSet<E> {
-        final int numberOfSpans;
-        final E[] spans;
-        final int[] spanStarts;
-        final int[] spanEnds;
-        final int[] spanFlags;
+        int numberOfSpans;
+        E[] spans;
+        int[] spanStarts;
+        int[] spanEnds;
+        int[] spanFlags;
+        final Class<? extends E> classType;
+
+        SpanSet(Class<? extends E> type) {
+            classType = type;
+            numberOfSpans = 0;
+        }
 
         @SuppressWarnings("unchecked")
-        SpanSet(Spanned spanned, int start, int limit, Class<? extends E> type) {
-            final E[] allSpans = spanned.getSpans(start, limit, type);
+        public void init(Spanned spanned, int start, int limit) {
+            final E[] allSpans = spanned.getSpans(start, limit, classType);
             final int length = allSpans.length;
-            // These arrays may end up being too large because of empty spans
-            spans = (E[]) Array.newInstance(type, length);
-            spanStarts = new int[length];
-            spanEnds = new int[length];
-            spanFlags = new int[length];
+
+            if (length > 0 && (spans == null || spans.length < length)) {
+                // These arrays may end up being too large because of empty spans
+                spans = (E[]) Array.newInstance(classType, length);
+                spanStarts = new int[length];
+                spanEnds = new int[length];
+                spanFlags = new int[length];
+            }
 
             int count = 0;
             for (int i = 0; i < length; i++) {
@@ -879,30 +890,11 @@
                 if (spanStart == spanEnd) continue;
 
                 final int spanFlag = spanned.getSpanFlags(span);
-                final int priority = spanFlag & Spanned.SPAN_PRIORITY;
-                if (priority != 0 && count != 0) {
-                    int j;
 
-                    for (j = 0; j < count; j++) {
-                        final int otherPriority = spanFlags[j] & Spanned.SPAN_PRIORITY;
-                        if (priority > otherPriority) break;
-                    }
-
-                    System.arraycopy(spans, j, spans, j + 1, count - j);
-                    System.arraycopy(spanStarts, j, spanStarts, j + 1, count - j);
-                    System.arraycopy(spanEnds, j, spanEnds, j + 1, count - j);
-                    System.arraycopy(spanFlags, j, spanFlags, j + 1, count - j);
-
-                    spans[j] = span;
-                    spanStarts[j] = spanStart;
-                    spanEnds[j] = spanEnd;
-                    spanFlags[j] = spanFlag;
-                } else {
-                    spans[i] = span;
-                    spanStarts[i] = spanStart;
-                    spanEnds[i] = spanEnd;
-                    spanFlags[i] = spanFlag;
-                }
+                spans[i] = span;
+                spanStarts[i] = spanStart;
+                spanEnds[i] = spanEnd;
+                spanFlags[i] = spanFlag;
 
                 count++;
             }
@@ -970,10 +962,8 @@
                     y, bottom, fmi, needWidth || mlimit < measureLimit);
         }
 
-        final SpanSet<MetricAffectingSpan> metricAffectingSpans = new SpanSet<MetricAffectingSpan>(
-                mSpanned, mStart + start, mStart + limit, MetricAffectingSpan.class);
-        final SpanSet<CharacterStyle> characterStyleSpans = new SpanSet<CharacterStyle>(
-                    mSpanned, mStart + start, mStart + limit, CharacterStyle.class);
+        mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit);
+        mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit);
 
         // Shaping needs to take into account context up to metric boundaries,
         // but rendering needs to take into account character style boundaries.
@@ -985,17 +975,18 @@
             TextPaint wp = mWorkPaint;
             wp.set(mPaint);
 
-            inext = metricAffectingSpans.getNextTransition(mStart + i, mStart + limit) - mStart;
+            inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) -
+                    mStart;
             int mlimit = Math.min(inext, measureLimit);
 
             ReplacementSpan replacement = null;
 
-            for (int j = 0; j < metricAffectingSpans.numberOfSpans; j++) {
+            for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) {
                 // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT
                 // empty by construction. This special case in getSpans() explains the >= & <= tests
-                if ((metricAffectingSpans.spanStarts[j] >= mStart + mlimit) ||
-                        (metricAffectingSpans.spanEnds[j] <= mStart + i)) continue;
-                MetricAffectingSpan span = metricAffectingSpans.spans[j];
+                if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) ||
+                        (mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue;
+                MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j];
                 if (span instanceof ReplacementSpan) {
                     replacement = (ReplacementSpan)span;
                 } else {
@@ -1016,16 +1007,16 @@
                         y, bottom, fmi, needWidth || mlimit < measureLimit);
             } else {
                 for (int j = i, jnext; j < mlimit; j = jnext) {
-                    jnext = characterStyleSpans.getNextTransition(mStart + j, mStart + mlimit) -
+                    jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) -
                             mStart;
 
                     wp.set(mPaint);
-                    for (int k = 0; k < characterStyleSpans.numberOfSpans; k++) {
+                    for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
                         // Intentionally using >= and <= as explained above
-                        if ((characterStyleSpans.spanStarts[k] >= mStart + jnext) ||
-                                (characterStyleSpans.spanEnds[k] <= mStart + j)) continue;
+                        if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) ||
+                                (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;
 
-                        CharacterStyle span = characterStyleSpans.spans[k];
+                        CharacterStyle span = mCharacterStyleSpanSet.spans[k];
                         span.updateDrawState(wp);
                     }