home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / awt / font / BidiInfo.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  32.0 KB  |  1,012 lines

  1. /*
  2.  * @(#)BidiInfo.java    1.7 98/03/18
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. /*
  16.  * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
  17.  * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
  18.  *
  19.  * The original version of this source code and documentation is
  20.  * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
  21.  * of IBM. These materials are provided under terms of a License
  22.  * Agreement between Taligent and Sun. This technology is protected
  23.  * by multiple US and International patents.
  24.  *
  25.  * This notice and attribution to Taligent may not be removed.
  26.  * Taligent is a registered trademark of Taligent, Inc.
  27.  *
  28.  */
  29.  
  30. package java.awt.font;
  31.  
  32. // !!! TODO to do too...
  33. // 1) arabic characters before segment separators affect interpretation of numbers after them.
  34. // so I need to handle segment separators, or pass in extra information
  35. // 2) return information on embeddings and overrides independent of resolved levels for clients
  36. // to use, i.e. cursor placement.
  37. // 3) support all bidi classes and overriding of them via leveldirs (except bs?) if warranted.
  38. // probably it is not, and I can have leveldirs only carry embedding/override info.  Since
  39. // we go to the text to see if a character is arabic, the user can't completely control the
  40. // result anyway.  Alternatively, given this additional information in leveldirs I could
  41. // dispense with.
  42.  
  43. class BidiInfo extends Object {
  44.  
  45. /* Should these all be public so clients can construct a full dir array, or only L and R? */
  46.  
  47.     public static final byte L    = 0;    /* left to right (strong) */
  48.     public static final byte R    = 1;    /* right to left (strong) */
  49.     public static final byte EN    = 2;    /* european number (weak) */
  50.     public static final byte ES    = 3;    /* european number separator (weak) */
  51.     public static final byte ET    = 4;    /* european number terminator (weak) */
  52.     public static final byte AN    = 5;    /* arabic number (weak) */
  53.     public static final byte CS    = 6;    /* common number separator (weak) */
  54.     public static final byte N    = 7;    /* other neutrals, block separator, segment separator, whitespace */
  55.  
  56.     private static final byte X = 8;    // internal code for ignored explicit codes
  57.  
  58.     public static final int BASEDIRECTION_DEFAULT = -1;
  59.  
  60. /* These belong in some unicode char naming class. */
  61.  
  62.     private static final char LRE = 0x202A;    /* left to right embedding */
  63.     private static final char RLE = 0x202B;    /* right to left embedding */
  64.     private static final char PDF = 0x202C;    /* pop directional formatting */
  65.     private static final char LRO = 0x202D;    /* left to right override */
  66.     private static final char RLO = 0x202E;    /* right to left override */
  67.  
  68.     private static final char MIN_EXPLICIT_CODE = LRE; // bounds of range of explicit formatting codes
  69.     private static final char MAX_EXPLICIT_CODE = RLO;
  70.  
  71.     private static final char LRM = 0x200E;    /* left to right mark */
  72.     private static final char RLM = 0x200F;    /* right to left mark */
  73.  
  74.     private static final char ALS = 0x0600;    /* Arabic Letters start */
  75.     private static final char ALE = 0x06EF;    /* Arabic Letters end */
  76.  
  77.     private static final char NUMLEVELS = 16;  /* number of valid nesting levels. */
  78.  
  79.     private int length;
  80.     private char[] chars;      // the characters
  81.     private byte[] dirs;       // the directional formatting codes
  82.     private byte[] levels;     // the nesting levels
  83.     private byte[] embeddings; // the embeddings
  84.     private byte baseLevel;    // the base nesting level (line direction)
  85.  
  86.     // Return true if the character is arabic, used in resolving weak types.
  87.  
  88.     private boolean isArabic(char c)
  89.     {
  90.         // !!! TEST VERSION
  91.         return c >= 'A' && c <= 'M';
  92.     }
  93.  
  94.     private boolean isBlockSeparator(char c)
  95.     {
  96.         return c == '\t' || c == '\n' || c == '\r' || c == '\u2029'; // !!! not LS U+2028
  97.     }
  98.  
  99.     // Return true if the character is whitespace. We'll rely on this instead of the
  100.     // internal direction array, so we can remove the distinction between WS and ON
  101.     // when implementing the algorithm.
  102.  
  103.     private boolean isWhiteSpace(char c)
  104.     {
  105.         // !!! TEST VERSION
  106.         return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\u2028' || c == '\u2029';
  107.     }
  108.  
  109.     public static byte getDirectionClass(char c)
  110.     {
  111.         // !!! TEST VERSION
  112.         byte dir;
  113.  
  114.         if (((c >= 'a') && (c <= 'z')) || c == LRO || c == LRE || c == LRM)
  115.             dir = L;
  116.         else if (((c >= 'A') && (c <= 'Z')) || c == RLO || c == RLE || c == RLM)
  117.             dir = R;
  118.         else if ((c >= '0') && (c <= '4'))
  119.             dir = EN;
  120.         else if ((c == '.') || (c == '/'))
  121.             dir = ES;
  122.         else if ((c == '$') || (c == '+'))
  123.             dir = ET;
  124.         else if ((c >= '5') && (c <= '9'))
  125.             dir = AN;
  126.         else if ((c == ',') || (c == ':'))
  127.             dir = CS;
  128.         else // explicit formatting codes, block and segment separators, whitespace all map to N
  129.             dir = N;
  130.  
  131.         return dir;
  132.     }
  133.  
  134. /* Pretty inefficient, might rebuild into a binary search when we really implement it.
  135.  
  136.     public static byte getDirectionClass(char c)
  137.     {
  138.         char dir;
  139.  
  140.         if ((c == 0x0026) || (c == 0x0040) ||
  141.                 ((c >= 0x0040) && (c <= 0x005A)) ||
  142.                 ((c >= 0x0061) && (c <= 0x007A)) ||
  143.                 ((c >= 0x00C0) && (c <= 0x00D6)) ||
  144.                 ((c >= 0x00D8) && (c <= 0x00F6)) ||
  145.                 ((c >= 0x00F8) && (c <= 0x058F)) ||
  146.                 ((c >= 0x0900) && (c <= 0x0E3A)) ||
  147.                 ((c >= 0x0E40) && (c <= 0x11FF)) ||
  148.                 ((c >= 0x1E00) && (c <= 0x1FFF)) ||
  149.                 ((c >= 0x20D0) && (c <= 0x20FF)) ||
  150.                 ((c >= 0x2160) && (c <= 0x2182)) ||
  151.                 ((c >= 0x3040) && (c <= 0x9FFF)) ||
  152.                 (c == LRM) || (c == LRE) || (c == LRO))
  153.             dir = L;
  154.         else if (((c >= 0x0590) && (c <= 0x065F)) ||
  155.                 ((c >= 0x066D) && (c <= 0x06EF)) ||
  156.                 (c == RLM) || (c == RLE) || (c == RLO))
  157.             dir = R;
  158.         else if (((c >= 0x0030) && (c <= 0x0039)) ||
  159.                 ((c >= 0x06F0) && (c <= 0x06F9)) ||
  160.                 (c == 0x00B2) || (c == 0x00B3) ||
  161.                 (c == 0x00B9) || (c == 0x2070) ||
  162.                 ((c >= 0x2074) && (c <= 0x2079)) ||
  163.                 ((c >= 0x2080) && (c <= 0x2089)))
  164.             dir = EN;
  165.         else if ((c == 0x002E) || (c == 0x002F) || (c == 0x2007))
  166.             dir = ES;
  167.         else if (((c >= 0x0023) && (c <= 0x0025)) ||
  168.                 ((c >= 0x00A2) && (c <= 0x00A5)) ||
  169.                 (c == 0x0E3F) || (c == 0x066A) ||
  170.                 ((c >= 0x20A0) && (c <= 0x20CF)) ||
  171.                 (c == 0x002B) || (c == 0x002D) ||
  172.                 (c == 0x00B0) || (c == 0x00B1) ||
  173.                 (c == 0x2032) || (c == 0x2033) ||
  174.                 (c == 0x207A) || (c == 0x207B) ||
  175.                 (c == 0x208A) || (c == 0x208B) ||
  176.                 (c == 0x2212) || (c == 0x2213))
  177.             dir = ET;
  178.         else if (((c >= 0x0660) && (c <= 0x0669)) ||
  179.                 (c == 0x066B) || (c == 0x066C))
  180.             dir = AN;
  181.         else if ((c == 0x002C) || (c == 0x003A))
  182.             dir = CS;
  183. //        else if (c == 0x0009)
  184. //            dir = S;
  185. //        else if ((c == 0x2028) || (c == 0x2029) ||
  186. //                (c == 0x0009) || (c == '\r'))            // ret, B, and S map to B
  187. //            dir = B;
  188. //        else if ((c == 0x0020) || (c == 0x00A0) ||
  189. //                (c == 0x3000) || (c == 0xFEFF) ||
  190. //                ((c >= 0x2000) && (c <= 0x2006)) ||
  191. //                ((c >= 0x2008) && (c <= 0x200B)))
  192. //            dir = WS;
  193.         else
  194.             dir = N;
  195.  
  196.         return dir;
  197.     }
  198. */
  199.  
  200.     //
  201.     // debugging code
  202.     //
  203.  
  204.     // global debug flag
  205.     private static boolean DEBUGGING = false;
  206.     private static boolean SHOWFORMAT = false;
  207.  
  208.     // names of direction codes
  209.     private static final String[] dirnames = {
  210.         "L",
  211.         "R",
  212.         "EN",
  213.         "ES",
  214.         "ET",
  215.         "AN",
  216.         "CS",
  217.         "N",
  218.         "X"
  219.     };
  220.  
  221.     // string used to pad output
  222.  
  223.     private static final String padstring = "          "; // 10 chars
  224.  
  225.     // if s.length() < count, pad s with leading spaces so that s.length() == count
  226.  
  227.     private static final String pad(String s, int count)
  228.     {
  229.         count -= s.length();
  230.  
  231.         while (count > 0) {
  232.             int pad = Math.min(count, padstring.length());
  233.             s = padstring.substring(0, pad) + s;
  234.             count -= pad;
  235.         }
  236.  
  237.         return s;
  238.     }
  239.  
  240.     // output a message and the current state of the algorithm
  241.  
  242.     private void debug(String message)
  243.     {
  244.         if (!DEBUGGING) return;
  245.  
  246.         System.out.println(message);
  247.  
  248.         System.out.println("length: " + length + ", baseLevel: " + baseLevel);
  249.  
  250.         if (chars != null) {
  251.             for (int i = 0; i < length; ++i)
  252.                 System.out.print("  " + chars[i]);
  253.             System.out.println();
  254.         }
  255.  
  256.         if (dirs != null) {
  257.             for (int i = 0; i < length; ++i)
  258.                 System.out.print(pad(dirnames[dirs[i]], 3));
  259.             System.out.println();
  260.         }
  261.  
  262.         if (levels != null) {
  263.             for (int i = 0; i < length; ++i)
  264.                 System.out.print(pad(Integer.toString(levels[i]), 3));
  265.             System.out.println();
  266.         }
  267.  
  268.         System.out.println();
  269.     }
  270.  
  271.     //
  272.     // Constructors
  273.     //
  274.  
  275.     /**
  276.      * String is a single, entire block.
  277.      * Base level will default using standard algorithm.  Strong directional formatting
  278.      * codes will be parsed.  Default direction codes will be used.
  279.      */
  280.     public BidiInfo(String str)
  281.     {
  282.         reset(str.toCharArray(), -1, null, null);
  283.     }
  284.  
  285.     /**
  286.      * String is a single, entire block.
  287.      * Baselevel is the base line direction.
  288.      * Embeddings reflect preprocessing of explicit formatting codes.  See reset.
  289.      * Dirs reflect external information on character directionality that overrides the default.  See reset.
  290.      */
  291.     public BidiInfo(String str, int baseLevel, byte[] embeddings, byte[] dirs)
  292.     {
  293.         reset(str.toCharArray(), baseLevel, embeddings, dirs);
  294.     }
  295.  
  296.     /**
  297.      * Iter is a single, entire block.
  298.      * Baselevel is the base line direction.
  299.      * Embeddings reflect preprocessing of explicit formatting codes.  See reset.
  300.      * Dirs reflect external information on character directionality that overrides the default.  See reset.
  301.      */
  302.     public BidiInfo(java.text.CharacterIterator iter, int baseLevel, byte[] embeddings, byte[] dirs)
  303.     {
  304.         int len = iter.getEndIndex() - iter.getBeginIndex();
  305.         char[] chars = new char[len];
  306.         char c = iter.first();
  307.         for (int i = 0; i < len; i++, c = iter.next())
  308.             chars[i] = c;
  309.  
  310.         reset(chars, baseLevel, embeddings, dirs);
  311.     }
  312.  
  313.     /**
  314.      * Chars is a single, entire block.
  315.      * Baselevel is the base line direction.
  316.      * Embeddings reflect preprocessing of explicit formatting codes.  See reset.
  317.      * Dirs reflect external information on character directionality that overrides the default.  See reset.
  318.      */
  319.     public BidiInfo(char[] chars, int baseLevel, byte[] embeddings, byte[] dirs)
  320.     {
  321.         reset(chars, baseLevel, embeddings, dirs);
  322.     }
  323.  
  324.     /**
  325.      * Reset the BidiInfo and have it compute a new reordering.
  326.      *
  327.      * Constructors call this.
  328.      *
  329.      * Chars is a block of characters to manipulate.  This should not contain any segment or
  330.      * block separators, any found will be treated like whitespace.  Clients wishing to parse
  331.      * more text should convert it segment by segment.  BidiInfo can modify this array.
  332.      *
  333.      * BaseLevel is the base level (line direction), either 0, 1, or -1.  If -1, the initial
  334.      * characters will be scanned to default a base level.  The start of the text will be
  335.      * treated like the start of a block, clients who are processing segments should pass
  336.      * an explicit base level rather than letting it default.
  337.      *
  338.      * Embeddings represents the effects of preprocessed explicit formatting codes.
  339.      * Bits 0-3 are the embedding level, bit 4 indicates a directional override.  If present
  340.      * embeddings.length must be equal to chars.length.  If baseLevel == 1 any embeddings of
  341.      * level 0 will be converted to level 1.
  342.      *
  343.      * Dirs represents overrides to the default character directionality.  If present,
  344.      * dirs.length must be equal to chars.length.  If null, default directionality codes
  345.      * will be used.
  346.      */
  347.  
  348.     private void reset(char[] chars, int baseLevel, byte[] embeddings, byte[] dirs)
  349.     {
  350.         length = chars.length;
  351.         this.chars = chars;
  352.  
  353.         if (dirs == null) {
  354.             dirs = new byte[length];
  355.             for (int i = 0; i < length; i++)
  356.                 dirs[i] = getDirectionClass(chars[i]);
  357.         } else {
  358.             if (dirs.length != length)
  359.                 throw new IllegalArgumentException("dirs length != text length");
  360.         }
  361.         this.dirs = dirs;
  362.  
  363.         if (embeddings != null) {
  364.             if (embeddings.length != length)
  365.                 throw new IllegalArgumentException("embeddings length != text length");
  366.  
  367.             if (baseLevel == -1) { // clients ought not default this, though.
  368.                 baseLevel = 1;
  369.                 for (int i = 0; i < length; i++) {
  370.                     if (embeddings[i] == 0 || dirs[i] == L || dirs[i] == R) {
  371.                         if (embeddings[i] == 0 || dirs[i] == L)
  372.                             baseLevel = 0;
  373.                         break;
  374.                     }
  375.                 }
  376.             } else if (baseLevel == 1) {
  377.                 for (int i = 0; i < length; i++)
  378.                     if (embeddings[i] == 0)
  379.                         embeddings[i] = 1;
  380.             }
  381.  
  382.             for (int i = 0; i < length; i++) {
  383.                 if ((embeddings[i] & 0x10) != 0)
  384.                     dirs[i] = (byte)(embeddings[i] & 0x1); // directional overrides
  385.             }
  386.             this.embeddings = embeddings;
  387.             this.baseLevel = (byte)baseLevel;
  388.  
  389.             ignoreExplicitFormattingCodes();
  390.         } else {
  391.             if (baseLevel == -1) { // only use when embeddings == null
  392.                 baseLevel = 0; // last-ditch default
  393.                 for (int i = 0; i < length; i++) {
  394.                     byte dir = dirs[i];
  395.                     if (dir == L || dir == R) {
  396.                         baseLevel = dir == L ? 0 : 1;
  397.                         break;
  398.                     }
  399.                 }
  400.             }
  401.             this.baseLevel = (byte)baseLevel;
  402.  
  403.             embeddings = new byte[length];
  404.             this.embeddings = embeddings;
  405.             processExplicitFormattingCodes(); // sets embeddings, override directions, etc.
  406.         }
  407.  
  408.  
  409.         boolean canonical = baseLevel == 0;
  410.         for (int i = 0; canonical && i < length; i++)
  411.             canonical = dirs[i] != R;
  412.  
  413.         if (canonical) { // hey, we're done!
  414.             if (DEBUGGING) System.out.println("*** bidi canonical: " + new String(chars));
  415.             levels = null;
  416.         } else {
  417.             levels = new byte[length];
  418.             for (int i = 0; i < length; i++)
  419.                 levels[i] = (byte)(embeddings[i] & 0xf);
  420.  
  421.             resolveWeakTypes();
  422.  
  423.             debug("after resolveWeakTypes");
  424.  
  425.             resolveNeutralTypes();
  426.  
  427.             debug("after resolveNeutralTypes");
  428.  
  429.             resolveImplicitLevels();
  430.  
  431.             debug("after resolveImplicitLevels");
  432.         }
  433.    }
  434.  
  435.     // Called to compute embedding and direction information from explicit formatting codes.  These
  436.     // codes are converted to L or R as appropriate to the level they control.
  437.  
  438.     private void processExplicitFormattingCodes()
  439.     {
  440.         // Mark says two adjacent runs of the same kind should be merged
  441.         // and the intervening codes removed, i.e. LRO a PDF LRO b PDF --> LRO a b PDF
  442.         // We will handle ignored codes using 'X' values rather than by actually removing
  443.         // them, sigh.
  444.  
  445.         byte level = baseLevel;
  446.         byte override = -1;
  447.         byte value = baseLevel; // merged level and override flags for embeddings array
  448.  
  449.         int s = 0; // stack counter
  450.         int skip = 0; // skip counter when codes don't affect the stack
  451.         byte levelStack[] = new byte[NUMLEVELS];
  452.         byte overrideStack[] = new byte[NUMLEVELS];
  453.         byte ignorelevel = -1; // flag to catch series of similar formatting codes
  454.  
  455.         for (int i = 0; i < length; ++i) {
  456.             char c = chars[i];
  457.             byte newignorelevel = -1;
  458.  
  459.             switch (c) {
  460.                 case LRE:
  461.                 case LRO: {
  462.                     byte newlevel = (byte)((level & 0x0e) + 2);
  463.                     if (newlevel < NUMLEVELS) {
  464.                         if (newlevel == ignorelevel) {
  465.                             dirs[i-1] = X;
  466.                             dirs[i] = X;
  467.                         }
  468.                         levelStack[s] = level;
  469.                         overrideStack[s] = override;
  470.                         level = newlevel;
  471.                         if (c == LRO) {
  472.                             override = L;
  473.                             value = (byte)(level + 0x10);
  474.                         } else {
  475.                             override = -1;
  476.                             value = level;
  477.                         }
  478.                     } else {
  479.                         ++skip;
  480.                     }
  481.                     ++s;
  482.                     embeddings[i] = value;
  483.                 } break;
  484.  
  485.                 case RLE:
  486.                 case RLO: {
  487.                     byte newlevel = (byte)((level + 1) | 0x01);
  488.                     if (newlevel < NUMLEVELS) {
  489.                         if (newlevel == ignorelevel) {
  490.                             dirs[i-1] = X;
  491.                             dirs[i] = X;
  492.                         }
  493.                         levelStack[s] = level;
  494.                         overrideStack[s] = override;
  495.                         level = newlevel;
  496.                         if (c == RLO) {
  497.                             override = R;
  498.                             value = (byte)(level + 0x10);
  499.                         } else {
  500.                             override = -1;
  501.                             value = level;
  502.                         }
  503.                     } else {
  504.                         ++skip;
  505.                     }
  506.                     ++s;
  507.                     embeddings[i] = value;
  508.                 } break;
  509.  
  510.                 case PDF:
  511.                     embeddings[i] = value;
  512.                     if (s > 0) {
  513.                         dirs[i] = ((level & 0x1) == 0) ? L : R;
  514.                         --s;
  515.                         if (skip > 0) {
  516.                             --skip;
  517.                         } else {
  518.                             newignorelevel = level;
  519.                             level = levelStack[s];
  520.                             override = overrideStack[s];
  521.                             value = override == -1 ? level : (byte)(level + 0x10);
  522.                         }
  523.                     } else {
  524.                         dirs[i] = X;
  525.                     }
  526.                     break;
  527.  
  528.                 default:
  529.                     embeddings[i] = value;
  530.                     if (override != -1)
  531.                         dirs[i] = override;
  532.                     break;
  533.             }
  534.  
  535.             ignorelevel = newignorelevel;
  536.         }
  537.     }
  538.  
  539.     // Mark all explicit codes as X
  540.     private void ignoreExplicitFormattingCodes()
  541.     {
  542.         for (int i = 0; i < length; i++) {
  543.             if (dirs[i] < MIN_EXPLICIT_CODE) continue;
  544.             if (dirs[i] > MAX_EXPLICIT_CODE) continue;
  545.             dirs[i] = X;
  546.         }
  547.     }
  548.  
  549.     // This resolves serially in order from left to right, with the results of previous changes
  550.     // taken into account for later characters.  So, for example, a series of ET's after an EN
  551.     // will all change to EN, since once the first ET changes to EN, it is then treated as EN
  552.     // for transforming the following ET, and so on.  It will also process ETs before EN by
  553.     // scanning forward across runs of ET and checking the following character.
  554.     //
  555.     // This does not take embedded levels into account.
  556.  
  557.     private void resolveWeakTypes()
  558.     {
  559.         byte prev = -1;
  560.         int i = 0;
  561.         while (i < length && dirs[i] == X)
  562.             i++;
  563.         byte cur = dirs[i];
  564.         boolean lastStrongWasArabic = cur <= R && isArabic(chars[i]);
  565.  
  566.         while (i < length) {
  567.             int ii = i + 1;
  568.             while (ii < length && dirs[ii] == X) {
  569.                 dirs[ii] = N; // set it, but we'll ignore it
  570.                 ii++;
  571.             }
  572.  
  573.             byte next = (ii == length) ? -1 : dirs[ii];
  574.  
  575.             if (next == EN && lastStrongWasArabic)
  576.                 next = AN;
  577.  
  578.             switch (cur) {
  579.                 case L:
  580.                 case R:
  581.                     lastStrongWasArabic = isArabic(chars[i]);
  582.                     break;
  583.  
  584.                 case ES:
  585.                     if (prev == EN && next == EN)
  586.                         cur = EN;
  587.                     else
  588.                         cur = N;
  589.                     break;
  590.  
  591.                 case CS:
  592.                     if (prev == EN && next == EN)
  593.                         cur = EN;
  594.                     else if (prev == AN && next == AN)
  595.                         cur = AN;
  596.                     else
  597.                         cur = N;
  598.                     break;
  599.  
  600.                 case ET:
  601.                     if (prev == EN || next == EN) {
  602.                         cur = EN;
  603.                     } else if (next == ET && !lastStrongWasArabic) { // forward scan to handle ET ET EN
  604.                         for (int j = ii + 1; j < length; ++j) {
  605.                             if (dirs[j] == ET || dirs[j] == X)
  606.                                 continue; // we'll map X to EN if we succeed, but that's ok
  607.  
  608.                             if (dirs[j] == EN) {
  609.                                 while (ii < j)
  610.                                     dirs[ii++] = EN;
  611.                                 cur = EN;
  612.                                 next = EN;
  613.                             }
  614.                             break;
  615.                         }
  616.                     } else {
  617.                         cur = N;
  618.                     }
  619.                     break;
  620.  
  621.                 default:
  622.                     break;
  623.             }
  624.  
  625.             dirs[i] = cur;
  626.             i = ii;
  627.             prev = cur;
  628.             cur = next;
  629.         }
  630.     }
  631.  
  632.     // According to Mark, this operation should never span a level boundary.  The start and end
  633.     // of the level should be treated like sot and eot, with the base direction the direction of the
  634.     // level.
  635.  
  636.     private void resolveNeutralTypes()
  637.     {
  638.         int i = 0;
  639.         while (i < length) {
  640.             byte tempBaseLevel = levels[i];
  641.             byte tempBaseDir = ((tempBaseLevel & 0x1) == 0) ? L : R;
  642.  
  643.             int eot = i + 1;
  644.             while (eot < length && levels[eot] == tempBaseLevel)
  645.                 eot++;
  646.  
  647.             byte last = tempBaseDir;
  648.             byte lastStrongDir = tempBaseDir;
  649.             while (i < eot) {
  650.                 if (dirs[i] == N) {
  651.                     int j = i + 1;
  652.                     while (j < eot && dirs[j] == N)
  653.                         j++;
  654.  
  655.                     byte next = tempBaseDir;
  656.                     if (j < eot) {
  657.                         switch(dirs[j]) {
  658.                             case L: next = L; break;
  659.                             case R: next = R; break;
  660.                             case EN: next = lastStrongDir; break;
  661.                             case AN: next = R; break;
  662.                         }
  663.                     }
  664.  
  665.                     if (last != next)
  666.                         last = tempBaseDir;
  667.  
  668.                     while (i < j) {
  669.                         dirs[i] = last;
  670.                         i++;
  671.                     }
  672.  
  673.                     if (i == eot)
  674.                         break;
  675.                 }
  676.  
  677.                 switch (dirs[i]) {
  678.                     case L: last = lastStrongDir = L; break;
  679.                     case R: last = lastStrongDir = R; break;
  680.                     case EN: last = lastStrongDir; break;
  681.                     case AN: last = R; break;
  682.                 }
  683.  
  684.                 i++;
  685.             }
  686.         }
  687.     }
  688.  
  689.     // Mark says to not use "global direction" but instead use the resolved level.
  690.     // EN processing is influenced by level boundaries.
  691.  
  692.     private void resolveImplicitLevels()
  693.     {
  694.         for (int i = 0; i < length; i++) {
  695.             byte level = levels[i];
  696.             if (isBlockSeparator(chars[i])) {
  697.                 level = baseLevel;
  698.             } else {
  699.                 switch (dirs[i]) {
  700.                     case L: level = (byte)((level + 1) & 0xe); break;
  701.                     case R: level = (byte)(level | 0x1); break;
  702.                     case AN: level = (byte)((level + 2) & 0xe); break;
  703.                     case EN: if ((level & 0x1) != 0)
  704.                                 level += 1;
  705.                              else if (i == 0 || (levels[i-1] != level) || dirs[i-1] == L || dirs[i-1] == EN)
  706.                                 level += 2;
  707.                              break;
  708.                 }
  709.                 if (level < NUMLEVELS)
  710.                     levels[i] = level;
  711.             }
  712.         }
  713.     }
  714.  
  715.     // Create mapping to reflect resolved levels, using entire text.
  716.  
  717.     public int[] createVisualToLogicalOrdering()
  718.     {
  719.         return createVisualToLogicalOrdering(0, length);
  720.     }
  721.  
  722.     // Create mapping to reflect resolved levels, using a subrange of the text
  723.     // to represent a line.  Whitespace at the end of the line is mapped to the
  724.     // base level.
  725.  
  726.     public int[] createVisualToLogicalOrdering(int start, int limit)
  727.     {
  728.         if (levels == null)
  729.             return null;
  730.  
  731.         boolean canonical = true;
  732.         for (int i = start; canonical && i < limit; i++)
  733.             canonical = (levels[i] & 0x1) == 0;
  734.         if (canonical) {
  735.             if (DEBUGGING) System.out.println("*** ordering canonical from " + start + " to " + limit);
  736.             return null;
  737.         }
  738.  
  739.         int maplen = limit - start;
  740.         int[] mapping = new int[maplen];
  741.  
  742.         // find out how much trailing whitespace there is
  743.         int ws = 0;
  744.         for (int i = limit - 1; i >= start && isWhiteSpace(chars[i]); --i)
  745.             ws++;
  746.  
  747.         // don't process these values, we'll special case them later
  748.         limit -= ws;
  749.  
  750.         int mapstart = baseLevel == 0 ? 0 : ws;
  751.  
  752.         byte lowestOddLevel = (byte)(NUMLEVELS + 1);
  753.         byte highestLevel = 0;
  754.  
  755.         // initialize mapping and levels
  756.  
  757.         for (int i = start; i < limit; i++) {
  758.             mapping[i - start + mapstart] = i;
  759.  
  760.             byte level = levels[i];
  761.             if (level > highestLevel)
  762.                 highestLevel = level;
  763.             if (((level & 0x01) != 0) && (level < lowestOddLevel))
  764.                 lowestOddLevel = level;
  765.         }
  766.  
  767.         while (highestLevel >= lowestOddLevel) {
  768.               int i = start;
  769.             for (;;) {
  770.                 while ((i < limit) && (levels[i] < highestLevel))
  771.                     i++;
  772.                 int begin = i++;
  773.  
  774.                 if (begin == limit)
  775.                     break; // no more runs at this level
  776.  
  777.                 while ((i < limit) && (levels[i] >= highestLevel))
  778.                     i++;
  779.                 int end = i - 1;
  780.  
  781.                 begin -= start - mapstart;
  782.                 end -= start - mapstart;
  783.                 while (begin < end) {
  784.                     int temp = mapping[begin];
  785.                     mapping[begin] = mapping[end];
  786.                     mapping[end] = temp;
  787.                     ++begin;
  788.                     --end;
  789.                 }
  790.             }
  791.  
  792.             // debug("after remap " + highestLevel + " " + mappedString());
  793.  
  794.             --highestLevel;
  795.         }
  796.  
  797.         // now let's handle the whitespace
  798.         if (baseLevel == 0) {
  799.             for (int i = limit; ws > 0; --ws, ++i)
  800.                 mapping[i - start] = i;
  801.         } else {
  802.             while (ws > 0)
  803.                 mapping[--ws] = limit++;
  804.         }
  805.  
  806.         return mapping;
  807.     }
  808.  
  809.     /**
  810.      * return base direction
  811.      */
  812.  
  813.     public byte getBaseLevel()
  814.     {
  815.         return baseLevel;
  816.     }
  817.  
  818.     /**
  819.      * Convenience to interpret the base level as LTR or RTL.
  820.      */
  821.     public boolean isDirectionLTR()
  822.     {
  823.         return (baseLevel & 0x1) == 0;
  824.     }
  825.  
  826.     /*
  827.      * return the level array.
  828.      */
  829.     public byte[] createLevels()
  830.     {
  831.         return createLevels(0, length);
  832.     }
  833.  
  834.     // !!! Optimize away level arrays where everything is even, on the assumption
  835.     // that the system won't want to represent levels in a special way, and all
  836.     // it cares about is the directionality.  But if this assumption changes, the
  837.     // test will need to be recoded.
  838.  
  839.     public byte[] createLevels(int start, int limit)
  840.     {
  841.         if (levels == null)
  842.             return null;
  843.  
  844.         boolean canonical = true;
  845.         for (int i = start; canonical && i < limit; i++)
  846.             canonical = (levels[i] & 0x1) == 0; // ??? or should I only test == 0?
  847.         if (canonical) {
  848.             if (DEBUGGING) System.out.println("*** levels canonical from " + start + " to " + limit);
  849.             return null;
  850.         }
  851.  
  852.         int levlen = limit - start;
  853.         byte[] newlevels = new byte[levlen];
  854.         System.arraycopy(levels, start, newlevels, 0, levlen);
  855.  
  856.         // set trailing whitespace to base level.  Don't worry about
  857.         // extra work if this is a lower odd level than the ideal odd
  858.         // level for this line, this situation won't happen often.
  859.  
  860.         for (int i = limit - 1; i >= start && isWhiteSpace(chars[i]); --i)
  861.             newlevels[i - start] = baseLevel;
  862.  
  863.         return newlevels;
  864.     }
  865.  
  866.     /*
  867.      * this is for debugging only, remapping is something fonts do to glyphs
  868.      */
  869.     private static char mappedChar(char c)
  870.     {
  871.         switch (c) {
  872.             case '(': return ')';
  873.             case ')': return '(';
  874.             case '[': return ']';
  875.             case ']': return '[';
  876.             case '<': return '>';
  877.             case '>': return '<';
  878.             case '{': return '}';
  879.             case '}': return '{';
  880.         }
  881.  
  882.         return c;
  883.     }
  884.  
  885.     /*
  886.      * Return a string containing the reordered characters.  Debugging only.
  887.      */
  888.     public String mappedString()
  889.     {
  890.         return mappedString(0, length);
  891.     }
  892.  
  893.     public String mappedString(int start, int limit)
  894.     {
  895.         String result = null;
  896.  
  897.         int[] mapping = createVisualToLogicalOrdering(start, limit);
  898.  
  899.         if (DEBUGGING && mapping != null) {
  900.             for (int i = 0; i < mapping.length; ++i)
  901.                 System.out.print(pad(Integer.toString(mapping[i]), 3));
  902.             System.out.println();
  903.         }
  904.  
  905.         if (mapping == null) {
  906.             result = new String(chars, start, limit - start);
  907.         } else {
  908.             StringBuffer buffer = new StringBuffer(mapping.length);
  909.  
  910.             for (int i = 0; i < mapping.length; i++) {
  911.                 char c = chars[mapping[i]];
  912.                 switch (c) {
  913.                     case LRE: if (SHOWFORMAT) buffer.append("[LRE]"); break;
  914.                     case LRO: if (SHOWFORMAT) buffer.append("[LRO]"); break;
  915.                     case RLE: if (SHOWFORMAT) buffer.append("[RLE]"); break;
  916.                     case RLO: if (SHOWFORMAT) buffer.append("[RLO]"); break;
  917.                     case PDF: if (SHOWFORMAT) buffer.append("[PDF]"); break;
  918.                     case LRM: if (SHOWFORMAT) buffer.append("[LRM]"); break;
  919.                     case RLM: if (SHOWFORMAT) buffer.append("[RLM]"); break;
  920.  
  921.                     default:
  922.                         if ((levels[mapping[i]] & 0x1) != 0)
  923.                             buffer.append(mappedChar(c));
  924.                         else
  925.                             buffer.append(c);
  926.                         break;
  927.                 }
  928.             }
  929.  
  930.             result = buffer.toString();
  931.         }
  932.  
  933.         return result;
  934.     }
  935.  
  936.     public static void main(String args[])
  937.     {
  938.         // symantec 1.53 NT 3.51 312-360 avg about 328 (long string)
  939.  
  940.         // String str = "HE SAID [" + LRE + "she said (" + RLE + "you SAID TO BUY 20, 30, or 40" + PDF + ")" + PDF + "]?  ";
  941.         String str = "I OWZ 123 dollars";
  942.  
  943.         if (DEBUGGING == false) {
  944.             // timing test
  945.             int strlen = str.length();
  946.             for (int trials = 10; trials > 0; --trials) {
  947.                 System.gc();
  948.                 long t = System.currentTimeMillis();
  949.                 for (int i = 0; i < 10; ++i) {
  950.                     BidiInfo b = new BidiInfo(str);
  951.                     for (int j = 0; j < strlen; ++j) {
  952.                         for (int k = j; k <= strlen; ++k) {
  953.                             byte[] levels = b.createLevels(j, k);
  954.                             int[] mapping = b.createVisualToLogicalOrdering(j, k);
  955.                         }
  956.                     }
  957.                 }
  958.                 t = System.currentTimeMillis() - t;
  959.                 System.out.println("timing test: " + t);
  960.             }
  961.         }
  962.  
  963.         // if (args.length > 0)
  964.         //     str = args[0];
  965.  
  966.         str = "AbcdEFGHijklMNOPqrsT   ";
  967.  
  968.         System.out.println("source: " + str);
  969.         System.out.println("result: " + new BidiInfo(str).mappedString());
  970.         System.out.println("result: " + new BidiInfo(str, 0, null, null).mappedString());
  971.  
  972.         str = "HE SAID [she said (you SAID)]?  ";
  973.         byte[] embeddings = {
  974.             1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 1, 1, 1
  975.         };
  976.  
  977.         System.out.println("result: " + new BidiInfo(str, 1, embeddings, null).mappedString());
  978.         System.out.println("result: " + new BidiInfo(str, 0, embeddings, null).mappedString());
  979.  
  980.         str = "start: " + LRE + "one run" + PDF + LRE + "another run" + PDF + ".  ";
  981.  
  982.         System.out.println("source: " + str);
  983.         System.out.println("result: " + new BidiInfo(str, 0, null, null).mappedString());
  984.         System.out.println("result: " + new BidiInfo(str, 1, null, null).mappedString());
  985.  
  986.         str = "he said \"" + RLE + "IT IS A bmw 400, OK." + PDF + "\"  ";
  987.         System.out.println("source: " + str);
  988.         System.out.println("result: " + new BidiInfo(str).mappedString());
  989.  
  990.         str = "he said [" + RLE + "THEY ARE 123, 456, 789, OK." + PDF + "]  ";
  991.         System.out.println("source: " + str);
  992.         System.out.println("result: " + new BidiInfo(str).mappedString());
  993.  
  994.         BidiInfo b = new BidiInfo(str);
  995.         DEBUGGING = false;
  996.         for (int i = 0; i <= str.length(); ++i)
  997.             System.out.println("[0," + pad(Integer.toString(i), 2) + "] >" + b.mappedString(0, i) + "<");
  998.  
  999.         for (int i = 0; i <= str.length() - 20; ++i)
  1000.             System.out.println("[" + pad(Integer.toString(i), 2) + "," + pad(Integer.toString(i + 20), 2) + "] >" +
  1001.                 b.mappedString(i, i + 20) + "<");
  1002.  
  1003.         try {
  1004.             for (;;)
  1005.                 Thread.sleep(100);
  1006.         }
  1007.         catch (Exception e) {
  1008.             System.out.println(e);
  1009.         }
  1010.     }
  1011. }
  1012.