Fix alignment issues with RTL paragraphs.

Also remove unused debugging code that depends on junit.
Remove trailing whitespace in changed code.

Change-Id: Ie02d1b8220c599a672ee6e91af0fba634e0f620c
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 3b8f295..2f7720a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -30,9 +30,10 @@
 import android.text.style.ParagraphStyle;
 import android.text.style.ReplacementSpan;
 import android.text.style.TabStopSpan;
+import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
 import android.view.KeyEvent;
 
-import junit.framework.Assert;
+import java.util.Arrays;
 
 /**
  * A base class that manages text layout in visual elements on
@@ -42,7 +43,6 @@
  * For text that will not change, use a {@link StaticLayout}.
  */
 public abstract class Layout {
-    private static final boolean DEBUG = false;
     private static final ParagraphStyle[] NO_PARA_SPANS =
         ArrayUtils.emptyArray(ParagraphStyle.class);
 
@@ -87,8 +87,7 @@
                 next = end;
 
             // note, omits trailing paragraph char
-            float w = measurePara(paint, workPaint,
-                                  source, i, next, true, null);
+            float w = measurePara(paint, workPaint, source, i, next);
 
             if (w > need)
                 need = w;
@@ -186,7 +185,6 @@
             dbottom = sTempRect.bottom;
         }
 
-
         int top = 0;
         int bottom = getLineTop(getLineCount());
 
@@ -209,13 +207,15 @@
         boolean spannedText = mSpannedText;
 
         ParagraphStyle[] spans = NO_PARA_SPANS;
-        int spanend = 0;
+        int spanEnd = 0;
         int textLength = 0;
 
         // First, draw LineBackgroundSpans.
-        // LineBackgroundSpans know nothing about the alignment or direction of
-        // the layout or line.  XXX: Should they?
+        // LineBackgroundSpans know nothing about the alignment, margins, or 
+        // direction of the layout or line.  XXX: Should they?
+        // They are evaluated at each line.
         if (spannedText) {
+            Spanned sp = (Spanned) buf;
             textLength = buf.length();
             for (int i = first; i <= last; i++) {
                 int start = previousLineEnd;
@@ -227,12 +227,14 @@
                 previousLineBottom = lbottom;
                 int lbaseline = lbottom - getLineDescent(i);
 
-                if (start >= spanend) {
-                   Spanned sp = (Spanned) buf;
-                   spanend = sp.nextSpanTransition(start, textLength,
-                                                   LineBackgroundSpan.class);
-                   spans = sp.getSpans(start, spanend,
-                                       LineBackgroundSpan.class);
+                if (start >= spanEnd) {
+                    // These should be infrequent, so we'll use this so that
+                    // we don't have to check as often.
+                    spanEnd = sp.nextSpanTransition(start, textLength, 
+                            LineBackgroundSpan.class);
+                    // All LineBackgroundSpans on a line contribute to its
+                    // background.
+                   spans = sp.getSpans(start, end, LineBackgroundSpan.class);
                 }
 
                 for (int n = 0; n < spans.length; n++) {
@@ -245,7 +247,7 @@
                 }
             }
             // reset to their original values
-            spanend = 0;
+            spanEnd = 0;
             previousLineBottom = getLineTop(first);
             previousLineEnd = getLineStart(first);
             spans = NO_PARA_SPANS;
@@ -266,8 +268,11 @@
         }
 
         Alignment align = mAlignment;
+        TabStops tabStops = null;
+        boolean tabStopsIsInitialized = false;
 
         TextLine tl = TextLine.obtain();
+
         // Next draw the lines, one at a time.
         // the baseline is the top of the following line minus the current
         // line's descent.
@@ -282,18 +287,29 @@
             previousLineBottom = lbottom;
             int lbaseline = lbottom - getLineDescent(i);
 
-            boolean isFirstParaLine = false;
+            int dir = getParagraphDirection(i);
+            int left = 0;
+            int right = mWidth;
+
             if (spannedText) {
-                if (start == 0 || buf.charAt(start - 1) == '\n') {
-                    isFirstParaLine = true;
-                }
-                // New batch of paragraph styles, compute the alignment.
-                // Last alignment style wins.
-                if (start >= spanend) {
-                    Spanned sp = (Spanned) buf;
-                    spanend = sp.nextSpanTransition(start, textLength,
+                Spanned sp = (Spanned) buf;
+                boolean isFirstParaLine = (start == 0 ||
+                        buf.charAt(start - 1) == '\n');
+                
+                // New batch of paragraph styles, collect into spans array.
+                // Compute the alignment, last alignment style wins.
+                // Reset tabStops, we'll rebuild if we encounter a line with
+                // tabs.
+                // We expect paragraph spans to be relatively infrequent, use
+                // spanEnd so that we can check less frequently.  Since
+                // paragraph styles ought to apply to entire paragraphs, we can
+                // just collect the ones present at the start of the paragraph.
+                // If spanEnd is before the end of the paragraph, that's not
+                // our problem.
+                if (start >= spanEnd && (i == first || isFirstParaLine)) {
+                    spanEnd = sp.nextSpanTransition(start, textLength,
                                                     ParagraphStyle.class);
-                    spans = sp.getSpans(start, spanend, ParagraphStyle.class);
+                    spans = sp.getSpans(start, spanEnd, ParagraphStyle.class);
 
                     align = mAlignment;
                     for (int n = spans.length-1; n >= 0; n--) {
@@ -302,45 +318,49 @@
                             break;
                         }
                     }
+                    
+                    tabStopsIsInitialized = false;
                 }
-            }
 
-            int dir = getParagraphDirection(i);
-            int left = 0;
-            int right = mWidth;
-
-            // Draw all leading margin spans.  Adjust left or right according
-            // to the paragraph direction of the line.
-            if (spannedText) {
+                // Draw all leading margin spans.  Adjust left or right according
+                // to the paragraph direction of the line.
                 final int length = spans.length;
                 for (int n = 0; n < length; n++) {
                     if (spans[n] instanceof LeadingMarginSpan) {
                         LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
+                        boolean useFirstLineMargin = isFirstParaLine;
+                        if (margin instanceof LeadingMarginSpan2) {
+                            int count = ((LeadingMarginSpan2) margin).getLeadingMarginLineCount();
+                            int startLine = getLineForOffset(sp.getSpanStart(margin));
+                            useFirstLineMargin = i < startLine + count;
+                        }
 
                         if (dir == DIR_RIGHT_TO_LEFT) {
                             margin.drawLeadingMargin(c, paint, right, dir, ltop,
                                                      lbaseline, lbottom, buf,
                                                      start, end, isFirstParaLine, this);
-
-                            right -= margin.getLeadingMargin(isFirstParaLine);
+                            right -= margin.getLeadingMargin(useFirstLineMargin);
                         } else {
                             margin.drawLeadingMargin(c, paint, left, dir, ltop,
                                                      lbaseline, lbottom, buf,
                                                      start, end, isFirstParaLine, this);
-
-                            boolean useMargin = isFirstParaLine;
-                            if (margin instanceof LeadingMarginSpan.LeadingMarginSpan2) {
-                                int count = ((LeadingMarginSpan.LeadingMarginSpan2)margin).getLeadingMarginLineCount();
-                                useMargin = count > i;
-                            }
-                            left += margin.getLeadingMargin(useMargin);
+                            left += margin.getLeadingMargin(useFirstLineMargin);
                         }
                     }
                 }
             }
 
-            // Adjust the point at which to start rendering depending on the
-            // alignment of the paragraph.
+            boolean hasTabOrEmoji = getLineContainsTab(i);
+            // Can't tell if we have tabs for sure, currently
+            if (hasTabOrEmoji && !tabStopsIsInitialized) {
+                if (tabStops == null) {
+                    tabStops = new TabStops(TAB_INCREMENT, spans);
+                } else {
+                    tabStops.reset(TAB_INCREMENT, spans);
+                }
+                tabStopsIsInitialized = true;
+            }
+
             int x;
             if (align == Alignment.ALIGN_NORMAL) {
                 if (dir == DIR_LEFT_TO_RIGHT) {
@@ -349,44 +369,83 @@
                     x = right;
                 }
             } else {
-                int max = (int)getLineMax(i, spans, false);
+                int max = (int)getLineExtent(i, tabStops, false);
                 if (align == Alignment.ALIGN_OPPOSITE) {
-                    if (dir == DIR_RIGHT_TO_LEFT) {
-                        x = left + max;
-                    } else {
+                    if (dir == DIR_LEFT_TO_RIGHT) {
                         x = right - max;
-                    }
-                } else {
-                    // Alignment.ALIGN_CENTER
-                    max = max & ~1;
-                    int half = (right - left - max) >> 1;
-                    if (dir == DIR_RIGHT_TO_LEFT) {
-                        x = right - half;
                     } else {
-                        x = left + half;
+                        x = left - max;
                     }
+                } else { // Alignment.ALIGN_CENTER
+                    max = max & ~1;
+                    x = (right + left - max) >> 1;
                 }
             }
 
             Directions directions = getLineDirections(i);
-            boolean hasTab = getLineContainsTab(i);
             if (directions == DIRS_ALL_LEFT_TO_RIGHT &&
-                    !spannedText && !hasTab) {
-                if (DEBUG) {
-                    Assert.assertTrue(dir == DIR_LEFT_TO_RIGHT);
-                    Assert.assertNotNull(c);
-                }
+                    !spannedText && !hasTabOrEmoji) {
                 // XXX: assumes there's nothing additional to be done
                 c.drawText(buf, start, end, x, lbaseline, paint);
             } else {
-                tl.set(paint, buf, start, end, dir, directions, hasTab, spans);
+                tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops);
                 tl.draw(c, x, ltop, lbaseline, lbottom);
             }
         }
+
         TextLine.recycle(tl);
     }
 
     /**
+     * Return the start position of the line, given the left and right bounds
+     * of the margins.
+     * 
+     * @param line the line index
+     * @param left the left bounds (0, or leading margin if ltr para)
+     * @param right the right bounds (width, minus leading margin if rtl para)
+     * @return the start position of the line (to right of line if rtl para)
+     */
+    private int getLineStartPos(int line, int left, int right) {
+        // Adjust the point at which to start rendering depending on the
+        // alignment of the paragraph.
+        Alignment align = getParagraphAlignment(line);
+        int dir = getParagraphDirection(line);
+
+        int x;
+        if (align == Alignment.ALIGN_NORMAL) {
+            if (dir == DIR_LEFT_TO_RIGHT) {
+                x = left;
+            } else {
+                x = right;
+            }
+        } else {
+            TabStops tabStops = null;
+            if (mSpannedText && getLineContainsTab(line)) {
+                Spanned spanned = (Spanned) mText;
+                int start = getLineStart(line);
+                int spanEnd = spanned.nextSpanTransition(start, spanned.length(),
+                        TabStopSpan.class);
+                TabStopSpan[] tabSpans = spanned.getSpans(start, spanEnd, TabStopSpan.class);
+                if (tabSpans.length > 0) {
+                    tabStops = new TabStops(TAB_INCREMENT, tabSpans);
+                }
+            }
+            int max = (int)getLineExtent(line, tabStops, false);
+            if (align == Alignment.ALIGN_OPPOSITE) {
+                if (dir == DIR_LEFT_TO_RIGHT) {
+                    x = right - max;
+                } else {
+                    x = left - max;
+                }
+            } else { // Alignment.ALIGN_CENTER
+                max = max & ~1;
+                x = (left + right - max) >> 1;
+            }
+        }
+        return x;
+    }
+
+    /**
      * Return the text that is displayed by this Layout.
      */
     public final CharSequence getText() {
@@ -647,45 +706,28 @@
         int start = getLineStart(line);
         int end = getLineEnd(line);
         int dir = getParagraphDirection(line);
-        boolean tab = getLineContainsTab(line);
+        boolean hasTabOrEmoji = getLineContainsTab(line);
         Directions directions = getLineDirections(line);
 
-        TabStopSpan[] tabs = null;
-        if (tab && mText instanceof Spanned) {
-            tabs = ((Spanned) mText).getSpans(start, end, TabStopSpan.class);
+        TabStops tabStops = null;
+        if (hasTabOrEmoji && mText instanceof Spanned) {
+            // Just checking this line should be good enough, tabs should be
+            // consistent across all lines in a paragraph.
+            TabStopSpan[] tabs = ((Spanned) mText).getSpans(start, end, TabStopSpan.class);
+            if (tabs.length > 0) {
+                tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse
+            }
         }
 
         TextLine tl = TextLine.obtain();
-        tl.set(mPaint, mText, start, end, dir, directions, tab, tabs);
+        tl.set(mPaint, mText, start, end, dir, directions, hasTabOrEmoji, tabStops);
         float wid = tl.measure(offset - start, trailing, null);
         TextLine.recycle(tl);
 
-        Alignment align = getParagraphAlignment(line);
         int left = getParagraphLeft(line);
         int right = getParagraphRight(line);
 
-        if (align == Alignment.ALIGN_NORMAL) {
-            if (dir == DIR_RIGHT_TO_LEFT)
-                return right + wid;
-            else
-                return left + wid;
-        }
-
-        float max = getLineMax(line);
-
-        if (align == Alignment.ALIGN_OPPOSITE) {
-            if (dir == DIR_RIGHT_TO_LEFT)
-                return left + max + wid;
-            else
-                return right - max + wid;
-        } else { /* align == Alignment.ALIGN_CENTER */
-            int imax = ((int) max) & ~1;
-
-            if (dir == DIR_RIGHT_TO_LEFT)
-                return right - (((right - left) - imax) / 2) + wid;
-            else
-                return left + ((right - left) - imax) / 2 + wid;
-        }
+        return getLineStartPos(line, left, right) + wid;
     }
 
     /**
@@ -743,29 +785,73 @@
     }
 
     /**
-     * Gets the horizontal extent of the specified line, excluding
-     * trailing whitespace.
+     * Gets the unsigned horizontal extent of the specified line, including 
+     * leading margin indent, but excluding trailing whitespace.
      */
     public float getLineMax(int line) {
-        return getLineMax(line, null, false);
+        float margin = getParagraphLeadingMargin(line);
+        float signedExtent = getLineExtent(line, false);
+        return margin + signedExtent >= 0 ? signedExtent : -signedExtent;
     }
 
     /**
-     * Gets the horizontal extent of the specified line, including
-     * trailing whitespace.
+     * Gets the unsigned horizontal extent of the specified line, including
+     * leading margin indent and trailing whitespace.
      */
     public float getLineWidth(int line) {
-        return getLineMax(line, null, true);
+        float margin = getParagraphLeadingMargin(line);
+        float signedExtent = getLineExtent(line, true);
+        return margin + signedExtent >= 0 ? signedExtent : -signedExtent;
     }
 
-    private float getLineMax(int line, Object[] tabs, boolean full) {
+    /**
+     * Like {@link #getLineExtent(int,TabStops,boolean)} but determines the
+     * tab stops instead of using the ones passed in.
+     * @param line the index of the line
+     * @param full whether to include trailing whitespace
+     * @return the extent of the line
+     */
+    private float getLineExtent(int line, boolean full) {
         int start = getLineStart(line);
         int end = full ? getLineEnd(line) : getLineVisibleEnd(line);
-        boolean hasTabs = getLineContainsTab(line);
+
+        boolean hasTabsOrEmoji = getLineContainsTab(line);
+        TabStops tabStops = null;
+        if (hasTabsOrEmoji && mText instanceof Spanned) {
+            // Just checking this line should be good enough, tabs should be
+            // consistent across all lines in a paragraph.
+            TabStopSpan[] tabs = ((Spanned) mText).getSpans(start, end, TabStopSpan.class);
+            if (tabs.length > 0) {
+                tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse
+            }
+        }
         Directions directions = getLineDirections(line);
+        int dir = getParagraphDirection(line);
 
         TextLine tl = TextLine.obtain();
-        tl.set(mPaint, mText, start, end, 1, directions, hasTabs, tabs);
+        tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops);
+        float width = tl.metrics(null);
+        TextLine.recycle(tl);
+        return width;
+    }
+
+    /**
+     * Returns the signed horizontal extent of the specified line, excluding
+     * leading margin.  If full is false, excludes trailing whitespace.
+     * @param line the index of the line
+     * @param tabStops the tab stops, can be null if we know they're not used.
+     * @param full whether to include trailing whitespace
+     * @return the extent of the text on this line
+     */
+    private float getLineExtent(int line, TabStops tabStops, boolean full) {
+        int start = getLineStart(line);
+        int end = full ? getLineEnd(line) : getLineVisibleEnd(line);
+        boolean hasTabsOrEmoji = getLineContainsTab(line);
+        Directions directions = getLineDirections(line);
+        int dir = getParagraphDirection(line);
+
+        TextLine tl = TextLine.obtain();
+        tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops);
         float width = tl.metrics(null);
         TextLine.recycle(tl);
         return width;
@@ -910,10 +996,6 @@
     }
 
     private int getLineVisibleEnd(int line, int start, int end) {
-        if (DEBUG) {
-            Assert.assertTrue(getLineStart(line) == start && getLineStart(line+1) == end);
-        }
-
         CharSequence text = mText;
         char ch;
         if (line == getLineCount() - 1) {
@@ -1243,84 +1325,107 @@
      * Get the left edge of the specified paragraph, inset by left margins.
      */
     public final int getParagraphLeft(int line) {
-        int dir = getParagraphDirection(line);
-
         int left = 0;
-
-        boolean par = false;
-        int off = getLineStart(line);
-        if (off == 0 || mText.charAt(off - 1) == '\n')
-            par = true;
-
-        if (dir == DIR_LEFT_TO_RIGHT) {
-            if (mSpannedText) {
-                Spanned sp = (Spanned) mText;
-                LeadingMarginSpan[] spans = sp.getSpans(getLineStart(line),
-                                                        getLineEnd(line),
-                                                        LeadingMarginSpan.class);
-
-                for (int i = 0; i < spans.length; i++) {
-                    boolean margin = par;
-                    LeadingMarginSpan span = spans[i];
-                    if (span instanceof LeadingMarginSpan.LeadingMarginSpan2) {
-                        int count = ((LeadingMarginSpan.LeadingMarginSpan2)span).getLeadingMarginLineCount();
-                        margin = count >= line;
-                    }
-                    left += span.getLeadingMargin(margin);
-                }
-            }
+        int dir = getParagraphDirection(line);
+        if (dir == DIR_RIGHT_TO_LEFT || !mSpannedText) {
+            return left; // leading margin has no impact, or no styles
         }
-
-        return left;
+        return getParagraphLeadingMargin(line);
     }
 
     /**
      * Get the right edge of the specified paragraph, inset by right margins.
      */
     public final int getParagraphRight(int line) {
-        int dir = getParagraphDirection(line);
-
         int right = mWidth;
+        int dir = getParagraphDirection(line);
+        if (dir == DIR_LEFT_TO_RIGHT || !mSpannedText) {
+            return right; // leading margin has no impact, or no styles
+        }
+        return right - getParagraphLeadingMargin(line);
+    }
 
-        boolean par = false;
-        int off = getLineStart(line);
-        if (off == 0 || mText.charAt(off - 1) == '\n')
-            par = true;
-
-
-        if (dir == DIR_RIGHT_TO_LEFT) {
-            if (mSpannedText) {
-                Spanned sp = (Spanned) mText;
-                LeadingMarginSpan[] spans = sp.getSpans(getLineStart(line),
-                                                        getLineEnd(line),
-                                                        LeadingMarginSpan.class);
-
-                for (int i = 0; i < spans.length; i++) {
-                    right -= spans[i].getLeadingMargin(par);
-                }
+    /**
+     * Returns the effective leading margin (unsigned) for this line,
+     * taking into account LeadingMarginSpan and LeadingMarginSpan2.
+     * @param line the line index
+     * @return the leading margin of this line
+     */
+    private int getParagraphLeadingMargin(int line) {
+        if (!mSpannedText) {
+            return 0;
+        }
+        Spanned spanned = (Spanned) mText;
+        
+        int lineStart = getLineStart(line);
+        int lineEnd = getLineEnd(line);
+        int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, 
+                LeadingMarginSpan.class);
+        LeadingMarginSpan[] spans = spanned.getSpans(lineStart, spanEnd,
+                                                LeadingMarginSpan.class);
+        if (spans.length == 0) {
+            return 0; // no leading margin span;
+        }
+        
+        int margin = 0;
+        
+        boolean isFirstParaLine = lineStart == 0 || 
+            spanned.charAt(lineStart - 1) == '\n';
+        
+        for (int i = 0; i < spans.length; i++) {
+            LeadingMarginSpan span = spans[i];
+            boolean useFirstLineMargin = isFirstParaLine;
+            if (span instanceof LeadingMarginSpan2) {
+                int spStart = spanned.getSpanStart(span);
+                int spanLine = getLineForOffset(spStart);
+                int count = ((LeadingMarginSpan2)span).getLeadingMarginLineCount();
+                useFirstLineMargin = line < spanLine + count; 
             }
+            margin += span.getLeadingMargin(useFirstLineMargin);
         }
 
-        return right;
+        return margin;
     }
 
     /* package */
     static float measurePara(TextPaint paint, TextPaint workPaint,
-            CharSequence text, int start, int end, boolean hasTabs,
-            Object[] tabs) {
+            CharSequence text, int start, int end) {
 
         MeasuredText mt = MeasuredText.obtain();
         TextLine tl = TextLine.obtain();
         try {
             mt.setPara(text, start, end, DIR_REQUEST_LTR);
             Directions directions;
-            if (mt.mEasy){
+            int dir;
+            if (mt.mEasy) {
                 directions = DIRS_ALL_LEFT_TO_RIGHT;
+                dir = Layout.DIR_LEFT_TO_RIGHT;
             } else {
                 directions = AndroidBidi.directions(mt.mDir, mt.mLevels,
                     0, mt.mChars, 0, mt.mLen);
+                dir = mt.mDir;
             }
-            tl.set(paint, text, start, end, 1, directions, hasTabs, tabs);
+            char[] chars = mt.mChars;
+            int len = mt.mLen;
+            boolean hasTabs = false;
+            TabStops tabStops = null;
+            for (int i = 0; i < len; ++i) {
+                if (chars[i] == '\t') {
+                    hasTabs = true;
+                    if (text instanceof Spanned) {
+                        Spanned spanned = (Spanned) text;
+                        int spanEnd = spanned.nextSpanTransition(start, end, 
+                                TabStopSpan.class);
+                        TabStopSpan[] spans = spanned.getSpans(start, spanEnd, 
+                                TabStopSpan.class);
+                        if (spans.length > 0) {
+                            tabStops = new TabStops(TAB_INCREMENT, spans);
+                        }
+                    }
+                    break;
+                }
+            }
+            tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops);
             return tl.metrics(null);
         } finally {
             TextLine.recycle(tl);
@@ -1329,6 +1434,67 @@
     }
 
     /**
+     * @hide
+     */
+    /* package */ static class TabStops {
+        private int[] mStops;
+        private int mNumStops;
+        private int mIncrement;
+        
+        TabStops(int increment, Object[] spans) {
+            reset(increment, spans);
+        }
+        
+        void reset(int increment, Object[] spans) {
+            this.mIncrement = increment;
+
+            int ns = 0;
+            if (spans != null) {
+                int[] stops = this.mStops;
+                for (Object o : spans) {
+                    if (o instanceof TabStopSpan) {
+                        if (stops == null) {
+                            stops = new int[10];
+                        } else if (ns == stops.length) {
+                            int[] nstops = new int[ns * 2];
+                            for (int i = 0; i < ns; ++i) {
+                                nstops[i] = stops[i];
+                            }
+                            stops = nstops;
+                        }
+                        stops[ns++] = ((TabStopSpan) o).getTabStop();
+                    }
+                }
+                if (ns > 1) {
+                    Arrays.sort(stops, 0, ns);
+                }
+                if (stops != this.mStops) {
+                    this.mStops = stops;
+                }
+            }
+            this.mNumStops = ns;
+        }
+        
+        float nextTab(float h) {
+            int ns = this.mNumStops;
+            if (ns > 0) {
+                int[] stops = this.mStops;
+                for (int i = 0; i < ns; ++i) {
+                    int stop = stops[i];
+                    if (stop > h) {
+                        return stop;
+                    }
+                }
+            }
+            return nextDefaultStop(h, mIncrement);
+        }
+
+        public static float nextDefaultStop(float h, int inc) {
+            return ((int) ((h + inc) / inc)) * inc;
+        }
+    }
+    
+    /**
      * Returns the position of the next tab stop after h on the line.
      *
      * @param text the text