StaticLayout_Delegate.java revision 7053919a3a55ad1b58e6db701afda18850037732
1package android.text;
2
3import com.android.annotations.NonNull;
4import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
5
6import android.text.StaticLayout.LineBreaks;
7import android.text.Primitive.PrimitiveType;
8
9import java.util.ArrayList;
10import java.util.List;
11
12import com.ibm.icu.text.BreakIterator;
13import com.ibm.icu.util.ULocale;
14import javax.swing.text.Segment;
15
16/**
17 * Delegate that provides implementation for native methods in {@link android.text.StaticLayout}
18 * <p/>
19 * Through the layoutlib_create tool, selected methods of Handler have been replaced by calls to
20 * methods of the same name in this delegate class.
21 */
22public class StaticLayout_Delegate {
23
24    private static final char CHAR_SPACE     = 0x20;
25    private static final char CHAR_TAB       = 0x09;
26    private static final char CHAR_NEWLINE   = 0x0A;
27    private static final char CHAR_ZWSP      = 0x200B;  // Zero width space.
28
29    @LayoutlibDelegate
30    /*package*/ static int nComputeLineBreaks(String locale, char[] inputText, float[] widths,
31            int length, float firstWidth, int firstWidthLineCount, float restWidth,
32            int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle,
33            int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength) {
34
35        // compute all possible breakpoints.
36        BreakIterator it = BreakIterator.getLineInstance(new ULocale(locale));
37        it.setText(new Segment(inputText, 0, length));
38        // average word length in english is 5. So, initialize the possible breaks with a guess.
39        List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d));
40        int loc;
41        it.first();
42        while ((loc = it.next()) != BreakIterator.DONE) {
43            breaks.add(loc);
44        }
45
46        LineWidth lineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
47        TabStops tabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
48        List<Primitive> primitives = computePrimitives(inputText, widths, length, breaks);
49        LineBreaker lineBreaker;
50        if (optimize) {
51            lineBreaker = new OptimizingLineBreaker(primitives, lineWidth, tabStopCalculator);
52        } else {
53            lineBreaker = new GreedyLineBreaker(primitives, lineWidth, tabStopCalculator);
54        }
55        lineBreaker.computeBreaks(recycle);
56        return recycle.breaks.length;
57    }
58
59    /**
60     * Compute metadata each character - things which help in deciding if it's possible to break
61     * at a point or not.
62     */
63    @NonNull
64    private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths,
65            int length, @NonNull List<Integer> breaks) {
66        // Initialize the list with a guess of the number of primitives:
67        // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars)
68        List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833)));
69        int breaksSize = breaks.size();
70        int breakIndex = 0;
71        for (int i = 0; i < length; i++) {
72            char c = text[i];
73            if (c == CHAR_SPACE || c == CHAR_ZWSP) {
74                primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i]));
75            } else if (c == CHAR_TAB) {
76                primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i));
77            } else if (c != CHAR_NEWLINE) {
78                while (breakIndex < breaksSize && breaks.get(breakIndex) < i) {
79                    breakIndex++;
80                }
81                Primitive p;
82                if (widths[i] != 0) {
83                    if (breakIndex < breaksSize && breaks.get(breakIndex) == i) {
84                        p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0);
85                    } else {
86                        p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0);
87                    }
88                    primitives.add(p);
89                }
90
91                primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i]));
92            }
93        }
94        // final break at end of everything
95        primitives.add(
96                PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY));
97        return primitives;
98    }
99}
100