Add BidiFormatter utility

See bug #7587797 Need BidiFormatter class in Android API

Change-Id: I999282b9a4c76d8b4a3fc254b0c12ca95fa6ea4a
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index df8c4c6..7d7e3a9 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -19,43 +19,45 @@
 
 import android.view.View;
 
+import java.nio.CharBuffer;
+
 /**
  * Some objects that implement TextDirectionHeuristic.
  *
- * @hide
  */
 public class TextDirectionHeuristics {
 
-    /** Always decides that the direction is left to right. */
+    /**
+     * Always decides that the direction is left to right.
+     */
     public static final TextDirectionHeuristic LTR =
         new TextDirectionHeuristicInternal(null /* no algorithm */, false);
 
-    /** Always decides that the direction is right to left. */
+    /**
+     * Always decides that the direction is right to left.
+     */
     public static final TextDirectionHeuristic RTL =
         new TextDirectionHeuristicInternal(null /* no algorithm */, true);
 
     /**
-     * Determines the direction based on the first strong directional character,
-     * including bidi format chars, falling back to left to right if it
-     * finds none.  This is the default behavior of the Unicode Bidirectional
-     * Algorithm.
+     * Determines the direction based on the first strong directional character, including bidi
+     * format chars, falling back to left to right if it finds none. This is the default behavior
+     * of the Unicode Bidirectional Algorithm.
      */
     public static final TextDirectionHeuristic FIRSTSTRONG_LTR =
         new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false);
 
     /**
-     * Determines the direction based on the first strong directional character,
-     * including bidi format chars, falling back to right to left if it
-     * finds none.  This is similar to the default behavior of the Unicode
-     * Bidirectional Algorithm, just with different fallback behavior.
+     * Determines the direction based on the first strong directional character, including bidi
+     * format chars, falling back to right to left if it finds none. This is similar to the default
+     * behavior of the Unicode Bidirectional Algorithm, just with different fallback behavior.
      */
     public static final TextDirectionHeuristic FIRSTSTRONG_RTL =
         new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true);
 
     /**
-     * If the text contains any strong right to left non-format character, determines
-     * that the direction is right to left, falling back to left to right if it
-     * finds none.
+     * If the text contains any strong right to left non-format character, determines that the
+     * direction is right to left, falling back to left to right if it finds none.
      */
     public static final TextDirectionHeuristic ANYRTL_LTR =
         new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false);
@@ -65,8 +67,39 @@
      */
     public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE;
 
-    private static enum TriState {
-        TRUE, FALSE, UNKNOWN;
+    /**
+     * State constants for taking care about true / false / unknown
+     */
+    private static final int STATE_TRUE = 0;
+    private static final int STATE_FALSE = 1;
+    private static final int STATE_UNKNOWN = 2;
+
+    private static int isRtlText(int directionality) {
+        switch (directionality) {
+            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+                return STATE_FALSE;
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+                return STATE_TRUE;
+            default:
+                return STATE_UNKNOWN;
+        }
+    }
+
+    private static int isRtlTextOrFormat(int directionality) {
+        switch (directionality) {
+            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+            case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
+            case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
+                return STATE_FALSE;
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
+                return STATE_TRUE;
+            default:
+                return STATE_UNKNOWN;
+        }
     }
 
     /**
@@ -87,21 +120,26 @@
         abstract protected boolean defaultIsRtl();
 
         @Override
-        public boolean isRtl(char[] chars, int start, int count) {
-            if (chars == null || start < 0 || count < 0 || chars.length - count < start) {
+        public boolean isRtl(char[] array, int start, int count) {
+            return isRtl(CharBuffer.wrap(array), start, count);
+        }
+
+        @Override
+        public boolean isRtl(CharSequence cs, int start, int count) {
+            if (cs == null || start < 0 || count < 0 || cs.length() - count < start) {
                 throw new IllegalArgumentException();
             }
             if (mAlgorithm == null) {
                 return defaultIsRtl();
             }
-            return doCheck(chars, start, count);
+            return doCheck(cs, start, count);
         }
 
-        private boolean doCheck(char[] chars, int start, int count) {
-            switch(mAlgorithm.checkRtl(chars, start, count)) {
-                case TRUE:
+        private boolean doCheck(CharSequence cs, int start, int count) {
+            switch(mAlgorithm.checkRtl(cs, start, count)) {
+                case STATE_TRUE:
                     return true;
-                case FALSE:
+                case STATE_FALSE:
                     return false;
                 default:
                     return defaultIsRtl();
@@ -124,58 +162,26 @@
         }
     }
 
-    private static TriState isRtlText(int directionality) {
-        switch (directionality) {
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
-                return TriState.FALSE;
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-                return TriState.TRUE;
-            default:
-                return TriState.UNKNOWN;
-        }
-    }
-
-    private static TriState isRtlTextOrFormat(int directionality) {
-        switch (directionality) {
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
-                return TriState.FALSE;
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
-                return TriState.TRUE;
-            default:
-                return TriState.UNKNOWN;
-        }
-    }
-
     /**
      * Interface for an algorithm to guess the direction of a paragraph of text.
-     *
      */
     private static interface TextDirectionAlgorithm {
         /**
          * Returns whether the range of text is RTL according to the algorithm.
-         *
          */
-        TriState checkRtl(char[] text, int start, int count);
+        int checkRtl(CharSequence cs, int start, int count);
     }
 
     /**
-     * Algorithm that uses the first strong directional character to determine
-     * the paragraph direction.  This is the standard Unicode Bidirectional
-     * algorithm.
-     *
+     * Algorithm that uses the first strong directional character to determine the paragraph
+     * direction. This is the standard Unicode Bidirectional algorithm.
      */
     private static class FirstStrong implements TextDirectionAlgorithm {
         @Override
-        public TriState checkRtl(char[] text, int start, int count) {
-            TriState result = TriState.UNKNOWN;
-            for (int i = start, e = start + count; i < e && result == TriState.UNKNOWN; ++i) {
-                result = isRtlTextOrFormat(Character.getDirectionality(text[i]));
+        public int checkRtl(CharSequence cs, int start, int count) {
+            int result = STATE_UNKNOWN;
+            for (int i = start, e = start + count; i < e && result == STATE_UNKNOWN; ++i) {
+                result = isRtlTextOrFormat(Character.getDirectionality(cs.charAt(i)));
             }
             return result;
         }
@@ -190,25 +196,24 @@
      * Algorithm that uses the presence of any strong directional non-format
      * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
      * direction of text.
-     *
      */
     private static class AnyStrong implements TextDirectionAlgorithm {
         private final boolean mLookForRtl;
 
         @Override
-        public TriState checkRtl(char[] text, int start, int count) {
+        public int checkRtl(CharSequence cs, int start, int count) {
             boolean haveUnlookedFor = false;
             for (int i = start, e = start + count; i < e; ++i) {
-                switch (isRtlText(Character.getDirectionality(text[i]))) {
-                    case TRUE:
+                switch (isRtlText(Character.getDirectionality(cs.charAt(i)))) {
+                    case STATE_TRUE:
                         if (mLookForRtl) {
-                            return TriState.TRUE;
+                            return STATE_TRUE;
                         }
                         haveUnlookedFor = true;
                         break;
-                    case FALSE:
+                    case STATE_FALSE:
                         if (!mLookForRtl) {
-                            return TriState.FALSE;
+                            return STATE_FALSE;
                         }
                         haveUnlookedFor = true;
                         break;
@@ -217,9 +222,9 @@
                 }
             }
             if (haveUnlookedFor) {
-                return mLookForRtl ? TriState.FALSE : TriState.TRUE;
+                return mLookForRtl ? STATE_FALSE : STATE_TRUE;
             }
-            return TriState.UNKNOWN;
+            return STATE_UNKNOWN;
         }
 
         private AnyStrong(boolean lookForRtl) {