1/*
2 *******************************************************************************
3 * Copyright (C) 2010-2015, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
6 */
7package com.ibm.icu.dev.test.bidi;
8
9import java.io.BufferedReader;
10import java.io.IOException;
11
12import com.ibm.icu.dev.test.TestFmwk;
13import com.ibm.icu.dev.test.TestUtil;
14import com.ibm.icu.lang.UCharacterDirection;
15import com.ibm.icu.text.Bidi;
16import com.ibm.icu.text.BidiClassifier;
17
18/**
19 * @author Markus W. Scherer
20 * BiDi conformance test, using the Unicode BidiTest.txt and BidiCharacterTest.txt files.
21 * Ported from ICU4C intltest/bidiconf.cpp .
22 */
23public class BiDiConformanceTest extends TestFmwk {
24    public static void main(String[] args) throws Exception {
25        new BiDiConformanceTest().run(args);
26    }
27    public BiDiConformanceTest() {}
28
29    public void TestBidiTest() throws IOException {
30        BufferedReader bidiTestFile = TestUtil.getDataReader("unicode/BidiTest.txt");
31        try {
32            Bidi ubidi = new Bidi();
33            ubidi.setCustomClassifier(new ConfTestBidiClassifier());
34            lineNumber = 0;
35            levelsCount = 0;
36            orderingCount = 0;
37            errorCount = 0;
38outerLoop:
39            while (errorCount < 10 && (line = bidiTestFile.readLine()) != null) {
40                ++lineNumber;
41                lineIndex = 0;
42                // Remove trailing comments and whitespace.
43                int commentStart = line.indexOf('#');
44                if (commentStart >= 0) {
45                    line = line.substring(0, commentStart);
46                }
47                if (!skipWhitespace()) {
48                    continue; // Skip empty and comment-only lines.
49                }
50                if (line.charAt(lineIndex) == '@') {
51                    ++lineIndex;
52                    if (line.startsWith("Levels:", lineIndex)) {
53                        lineIndex += 7;
54                        if (!parseLevels(line.substring(lineIndex))) {
55                            break;
56                        }
57                    } else if (line.startsWith("Reorder:", lineIndex)) {
58                        lineIndex += 8;
59                        if (!parseOrdering(line.substring(lineIndex))) {
60                            break;
61                        }
62                    }
63                    // Skip unknown @Xyz: ...
64                } else {
65                    parseInputStringFromBiDiClasses();
66                    if (!skipWhitespace() || line.charAt(lineIndex++) != ';') {
67                        errln("missing ; separator on input line " + line);
68                        return;
69                    }
70                    int bitset = Integer.parseInt(line.substring(lineIndex).trim(), 16);
71                    // Loop over the bitset.
72                    for (int i = 0; i <= 3; ++i) {
73                        if ((bitset & (1 << i)) != 0) {
74                            ubidi.setPara(inputString, paraLevels[i], null);
75                            byte actualLevels[] = ubidi.getLevels();
76                            paraLevelName = paraLevelNames[i];
77                            if (!checkLevels(actualLevels)) {
78                                continue outerLoop;
79                            }
80                            if (!checkOrdering(ubidi)) {
81                                continue outerLoop;
82                            }
83                        }
84                    }
85                }
86            }
87        } finally {
88            bidiTestFile.close();
89        }
90    }
91
92    /*
93    *******************************************************************************
94    *
95    *   created on: 2013jul01
96    *   created by: Matitiahu Allouche
97
98    This function performs a conformance test for implementations of the
99    Unicode Bidirectional Algorithm, specified in UAX #9: Unicode
100    Bidirectional Algorithm, at http://www.unicode.org/unicode/reports/tr9/
101
102    Each test case is represented in a single line which is read from a file
103    named BidiCharacter.txt.  Empty, blank and comment lines may also appear
104    in this file.
105
106    The format of the test data is specified below.  Note that each test
107    case constitutes a single line of text; reordering is applied within a
108    single line and independently of a rendering engine, and rules L3 and L4
109    are out of scope.
110
111    The number sign '#' is the comment character: everything is ignored from
112    the occurrence of '#' until the end of the line,
113    Empty lines and lines containing only spaces and/or comments are ignored.
114
115    Lines which represent test cases consist of 4 or 5 fields separated by a
116    semicolon.  Each field consists of tokens separated by whitespace (space
117    or Tab).  Whitespace before and after semicolons is optional.
118
119    Field 0: A sequence of hexadecimal code point values separated by space
120
121    Field 1: A value representing the paragraph direction, as follows:
122        - 0 represents left-to-right
123        - 1 represents right-to-left
124        - 2 represents auto-LTR according to rules P2 and P3 of the algorithm
125        - 3 represents auto-RTL according to rules P2 and P3 of the algorithm
126        - a negative number whose absolute value is taken as paragraph level;
127          this may be useful to test cases where the embedding level approaches
128          or exceeds the maximum embedding level.
129
130    Field 2: The resolved paragraph embedding level.  If the input (field 0)
131             includes more than one paragraph, this field represents the
132             resolved level of the first paragraph.
133
134    Field 3: An ordered list of resulting levels for each token in field 0
135             (each token represents one source character).
136             The UBA does not assign levels to certain characters (e.g. LRO);
137             characters removed in rule X9 are indicated with an 'x'.
138
139    Field 4: An ordered list of indices showing the resulting visual ordering
140             from left to right; characters with a resolved level of 'x' are
141             skipped.  The number are zero-based.  Each index corresponds to
142             a character in the reordered (visual) string. It represents the
143             index of the source character in the input (field 0).
144             This field is optional.  When it is absent, the visual ordering
145             is not verified.
146
147    Examples:
148
149    # This is a comment line.
150    L L ON R ; 0 ; 0 ; 0 0 0 1 ; 0 1 2 3
151    L L ON R;0;0;0 0 0 1;0 1 2 3
152
153    # Note: in the next line, 'B' represents a block separator, not the letter 'B'.
154    LRE A B C PDF;2;0;x 2 0 0 x;1 2 3
155    # Note: in the next line, 'b' represents the letter 'b', not a block separator.
156    a b c 05d0 05d1 x ; 0 ; 0 ; 0 0 0 1 1 0 ; 0 1 2 4 3 5
157
158    a R R x ; 1 ; 1 ; 2 1 1 2
159    L L R R R B R R L L L B ON ON ; 3 ; 0 ; 0 0 1 1 1 0 1 1 2 2 2 1 1 1
160
161    *
162    *******************************************************************************
163    */
164    public void TestBidiCharacterTest() throws IOException {
165        BufferedReader bidiTestFile = TestUtil.getDataReader("unicode/BidiCharacterTest.txt");
166        try {
167            Bidi ubidi = new Bidi();
168            lineNumber = 0;
169            levelsCount = 0;
170            orderingCount = 0;
171            errorCount = 0;
172outerLoop:
173            while (errorCount < 20 && (line = bidiTestFile.readLine()) != null) {
174                ++lineNumber;
175                paraLevelName = "N/A";
176                inputString = "N/A";
177                lineIndex = 0;
178                // Remove trailing comments and whitespace.
179                int commentStart = line.indexOf('#');
180                if (commentStart >= 0) {
181                    line = line.substring(0, commentStart);
182                }
183                if (!skipWhitespace()) {
184                    continue; // Skip empty and comment-only lines.
185                }
186                String[] parts = line.split(";");
187                if (parts.length < 4) {
188                    errorCount++;
189                    errln(" on line " + lineNumber + ": Missing ; separator on line: " + line);
190                    continue;
191                }
192                // Parse the code point string in field 0.
193                try {
194                    inputStringBuilder.delete(0, inputStringBuilder.length());
195                    for (String cp : parts[0].trim().split("[ \t]+")) {
196                        inputStringBuilder.appendCodePoint(Integer.parseInt(cp, 16));
197                    }
198                    inputString = inputStringBuilder.toString();
199                } catch (Exception e) {
200                    errln(" ------------ Invalid string in field 0 on line '" + line + "'");
201                    ++errorCount;
202                    continue;
203                }
204                int paraDirection = intFromString(parts[1].trim());
205                byte paraLevel;
206                if (paraDirection == 0) {
207                    paraLevel = 0;
208                    paraLevelName = "LTR";
209                } else if (paraDirection == 1) {
210                    paraLevel = 1;
211                    paraLevelName = "RTL";
212                } else if (paraDirection == 2) {
213                    paraLevel = Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
214                    paraLevelName = "Auto/LTR";
215                } else if (paraDirection == 3) {
216                    paraLevel = Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
217                    paraLevelName = "Auto/RTL";
218                } else if (paraDirection < 0 && -paraDirection <= (Bidi.MAX_EXPLICIT_LEVEL + 1)) {
219                    paraLevel = (byte) (-paraDirection);
220                    paraLevelName = Byte.toString(paraLevel);
221                } else {
222                    errorCount++;
223                    errln(" on line " + lineNumber + ": Input paragraph direction incorrect at " + line);
224                    continue;
225                }
226                int resolvedParaLevel = intFromString(parts[2].trim());
227                if (resolvedParaLevel < 0 || resolvedParaLevel > (Bidi.MAX_EXPLICIT_LEVEL + 1)) {
228                    errorCount++;
229                    errln(" on line " + lineNumber + ": Resolved paragraph level incorrect at " + line);
230                    continue;
231                }
232                if (!parseLevels(parts[3])) {
233                    continue;
234                }
235                if (parts.length > 4) {
236                    if (!parseOrdering(parts[4])) {
237                        continue;
238                    }
239                } else {
240                    orderingCount = -1;
241                }
242
243                ubidi.setPara(inputString, paraLevel, null);
244                byte actualParaLevel = ubidi.getParaLevel();
245                if (actualParaLevel != resolvedParaLevel) {
246                    errln(" ------------ Wrong resolved paragraph level; expected " + resolvedParaLevel + " actual "
247                            + actualParaLevel);
248                    printErrorLine();
249                }
250                byte[] actualLevels = ubidi.getLevels();
251                if (!checkLevels(actualLevels)) {
252                    continue outerLoop;
253                }
254                if (!checkOrdering(ubidi)) {
255                    continue outerLoop;
256                }
257            }
258        } finally {
259            bidiTestFile.close();
260        }
261    }
262
263    private static final byte paraLevels[]={
264        Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT,
265        0,
266        1,
267        Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT
268    };
269    private static final String paraLevelNames[]={ "auto/LTR", "LTR", "RTL", "auto/RTL" };
270
271    private int intFromString(String str) {
272        try {
273            return Integer.parseInt(str);
274        } catch (Exception e) {
275            return -9999;
276        }
277    }
278
279    private boolean parseLevels(String s) {
280        directionBits=0;
281        levelsCount=0;
282        String[] levelStrings=s.trim().split("[ \t]+");
283        for(String levelString: levelStrings) {
284            if(levelString.length()==0) { continue; }
285            if(levelString.equals("x")) {
286                levels[levelsCount++]=-1;
287            } else {
288                try {
289                    int value=Integer.parseInt(levelString);
290                    if(0<=value && value<=(Bidi.MAX_EXPLICIT_LEVEL+1)) {
291                        levels[levelsCount++]=(byte)value;
292                        directionBits|=(1<<(value&1));
293                        continue;
294                    }
295                } catch(Exception e) {
296                }
297                errln(" ------------ Levels parse error at '"+levelString+"'");
298                printErrorLine();
299                return false;
300            }
301        }
302        return true;
303    }
304    private boolean parseOrdering(String s) {
305        orderingCount=0;
306        String[] orderingStrings=s.trim().split("[ \t]+");
307        for(String orderingString: orderingStrings) {
308            if(orderingString.length()==0) { continue; }
309            try {
310                int value=Integer.parseInt(orderingString);
311                if(value<1000) {
312                    ordering[orderingCount++]=value;
313                    continue;
314                }
315            } catch(Exception e) {
316            }
317            errln(" ------------ Reorder parse error at '"+orderingString+"'");
318            printErrorLine();
319            return false;
320        }
321        return true;
322    }
323    private static char charFromBiDiClass[]={
324        0x6c,   // 'l' for L
325        0x52,   // 'R' for R
326        0x33,   // '3' for EN
327        0x2d,   // '-' for ES
328        0x25,   // '%' for ET
329        0x39,   // '9' for AN
330        0x2c,   // ',' for CS
331        0x2f,   // '/' for B
332        0x5f,   // '_' for S
333        0x20,   // ' ' for WS
334        0x3d,   // '=' for ON
335        0x65,   // 'e' for LRE
336        0x6f,   // 'o' for LRO
337        0x41,   // 'A' for AL
338        0x45,   // 'E' for RLE
339        0x4f,   // 'O' for RLO
340        0x2a,   // '*' for PDF
341        0x60,   // '`' for NSM
342        0x7c,   // '|' for BN
343        // new in Unicode 6.3/ICU 52
344        0x53,   // 'S' for FSI
345        0x69,   // 'i' for LRI
346        0x49,   // 'I' for RLI
347        0x2e    // '.' for PDI
348    };
349    private class ConfTestBidiClassifier extends BidiClassifier {
350        public ConfTestBidiClassifier() {
351            super(null);
352        }
353        @Override
354        public int classify(int c) {
355            for(int i=0; i<charFromBiDiClass.length; ++i) {
356                if(c==charFromBiDiClass[i]) {
357                    return i;
358                }
359            }
360            // Character not in our hardcoded table.
361            // Should not occur during testing.
362            return Bidi.CLASS_DEFAULT;
363        }
364    }
365    private static final int biDiClassNameLengths[]={
366        1, 1, 2, 2, 2, 2, 2, 1, 1, 2, 2, 3, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 0
367    };
368    private void parseInputStringFromBiDiClasses() {
369        inputStringBuilder.delete(0, 0x7fffffff);
370        /*
371         * Lengthy but fast BiDi class parser.
372         * A simple parser could terminate or extract the name string and use
373         *   int32_t biDiClassInt=u_getPropertyValueEnum(UCHAR_BIDI_CLASS, bidiClassString);
374         * but that makes this test take significantly more time.
375         */
376        char c0, c1, c2;
377        while(skipWhitespace() && (c0=line.charAt(lineIndex))!=';') {
378            int biDiClass=UCharacterDirection.CHAR_DIRECTION_COUNT;
379            // Compare each character once until we have a match on
380            // a complete, short BiDi class name.
381            if(c0=='L') {
382                if((lineIndex+2)<line.length() && line.charAt(lineIndex+1)=='R') {
383                    c2=line.charAt(lineIndex+2);
384                    if(c2=='E') {
385                        biDiClass=UCharacterDirection.LEFT_TO_RIGHT_EMBEDDING;
386                    } else if(c2=='I') {
387                        biDiClass=UCharacterDirection.LEFT_TO_RIGHT_ISOLATE;
388                    } else if(c2=='O') {
389                        biDiClass=UCharacterDirection.LEFT_TO_RIGHT_OVERRIDE;
390                    }
391                } else {
392                    biDiClass=UCharacterDirection.LEFT_TO_RIGHT;
393                }
394            } else if(c0=='R') {
395                if((lineIndex+2)<line.length() && line.charAt(lineIndex+1)=='L') {
396                    c2=line.charAt(lineIndex+2);
397                    if(c2=='E') {
398                        biDiClass=UCharacterDirection.RIGHT_TO_LEFT_EMBEDDING;
399                    } else if(c2=='I') {
400                        biDiClass=UCharacterDirection.RIGHT_TO_LEFT_ISOLATE;
401                    } else if(c2=='O') {
402                        biDiClass=UCharacterDirection.RIGHT_TO_LEFT_OVERRIDE;
403                    }
404                } else {
405                    biDiClass=UCharacterDirection.RIGHT_TO_LEFT;
406                }
407            } else if(c0=='E') {
408                if((lineIndex+1)>=line.length()) {
409                    // too short
410                } else if((c1=line.charAt(lineIndex+1))=='N') {
411                    biDiClass=UCharacterDirection.EUROPEAN_NUMBER;
412                } else if(c1=='S') {
413                    biDiClass=UCharacterDirection.EUROPEAN_NUMBER_SEPARATOR;
414                } else if(c1=='T') {
415                    biDiClass=UCharacterDirection.EUROPEAN_NUMBER_TERMINATOR;
416                }
417            } else if(c0=='A') {
418                if((lineIndex+1)>=line.length()) {
419                    // too short
420                } else if((c1=line.charAt(lineIndex+1))=='L') {
421                    biDiClass=UCharacterDirection.RIGHT_TO_LEFT_ARABIC;
422                } else if(c1=='N') {
423                    biDiClass=UCharacterDirection.ARABIC_NUMBER;
424                }
425            } else if(c0=='C' && (lineIndex+1)<line.length() && line.charAt(lineIndex+1)=='S') {
426                biDiClass=UCharacterDirection.COMMON_NUMBER_SEPARATOR;
427            } else if(c0=='B') {
428                if((lineIndex+1)<line.length() && line.charAt(lineIndex+1)=='N') {
429                    biDiClass=UCharacterDirection.BOUNDARY_NEUTRAL;
430                } else {
431                    biDiClass=UCharacterDirection.BLOCK_SEPARATOR;
432                }
433            } else if(c0=='S') {
434                biDiClass=UCharacterDirection.SEGMENT_SEPARATOR;
435            } else if(c0=='W' && (lineIndex+1)<line.length() && line.charAt(lineIndex+1)=='S') {
436                biDiClass=UCharacterDirection.WHITE_SPACE_NEUTRAL;
437            } else if(c0=='O' && (lineIndex+1)<line.length() && line.charAt(lineIndex+1)=='N') {
438                biDiClass=UCharacterDirection.OTHER_NEUTRAL;
439            } else if(c0=='P' && (lineIndex+2)<line.length() && line.charAt(lineIndex+1)=='D') {
440                if(line.charAt(lineIndex+2)=='F') {
441                    biDiClass=UCharacterDirection.POP_DIRECTIONAL_FORMAT;
442                } else if(line.charAt(lineIndex+2)=='I') {
443                    biDiClass=UCharacterDirection.POP_DIRECTIONAL_ISOLATE;
444                }
445            } else if(c0=='N' && (lineIndex+2)<line.length() &&
446                      line.charAt(lineIndex+1)=='S' && line.charAt(lineIndex+2)=='M') {
447                biDiClass=UCharacterDirection.DIR_NON_SPACING_MARK;
448            } else if(c0=='F' && (lineIndex+2)<line.length() &&
449                    line.charAt(lineIndex+1)=='S' && line.charAt(lineIndex+2)=='I') {
450                biDiClass=UCharacterDirection.FIRST_STRONG_ISOLATE;
451            }
452            // Now we verify that the class name is terminated properly,
453            // and not just the start of a longer word.
454            int biDiClassNameLength=biDiClassNameLengths[biDiClass];
455            char c;
456            if( biDiClass==UCharacterDirection.CHAR_DIRECTION_COUNT ||
457                ((lineIndex+biDiClassNameLength)<line.length() &&
458                 !isInvWhitespace(c=line.charAt(lineIndex+biDiClassNameLength)) &&
459                 c!=';')
460            ) {
461                throw new IllegalArgumentException(
462                    "BiDi class string not recognized at "+line.substring(lineIndex)+" in "+line);
463            }
464            inputStringBuilder.append(charFromBiDiClass[biDiClass]);
465            lineIndex+=biDiClassNameLength;
466        }
467        inputString=inputStringBuilder.toString();
468    }
469
470    private static char printLevel(byte level) {
471        if(level<0) {
472            return 'x';
473        } else {
474            return (char)('0'+level);
475        }
476    }
477
478    private static int getDirectionBits(byte actualLevels[]) {
479        int actualDirectionBits=0;
480        for(int i=0; i<actualLevels.length; ++i) {
481            actualDirectionBits|=(1<<(actualLevels[i]&1));
482        }
483        return actualDirectionBits;
484    }
485    private boolean checkLevels(byte actualLevels[]) {
486        boolean isOk=true;
487        if(levelsCount!=actualLevels.length) {
488            errln(" ------------ Wrong number of level values; expected "+levelsCount+" actual "+actualLevels.length);
489            isOk=false;
490        } else {
491            for(int i=0; i<actualLevels.length; ++i) {
492                if(levels[i]!=actualLevels[i] && levels[i]>=0) {
493                    if(directionBits!=3 && directionBits==getDirectionBits(actualLevels)) {
494                        // ICU used a shortcut:
495                        // Since the text is unidirectional, it did not store the resolved
496                        // levels but just returns all levels as the paragraph level 0 or 1.
497                        // The reordering result is the same, so this is fine.
498                        break;
499                    } else {
500                        errln(" ------------ Wrong level value at index "+i+"; expected "+levels[i]+" actual "+actualLevels[i]);
501                        isOk=false;
502                        break;
503                    }
504                }
505            }
506        }
507        if(!isOk) {
508            printErrorLine();
509            StringBuilder els=new StringBuilder("Expected levels:   ");
510            int i;
511            for(i=0; i<levelsCount; ++i) {
512                els.append(' ').append(printLevel(levels[i]));
513            }
514            StringBuilder als=new StringBuilder("Actual   levels:   ");
515            for(i=0; i<actualLevels.length; ++i) {
516                als.append(' ').append(printLevel(actualLevels[i]));
517            }
518            errln(els.toString());
519            errln(als.toString());
520        }
521        return isOk;
522    }
523
524    // Note: ubidi_setReorderingOptions(ubidi, UBIDI_OPTION_REMOVE_CONTROLS);
525    // does not work for custom BiDi class assignments
526    // and anyway also removes LRM/RLM/ZWJ/ZWNJ which is not desirable here.
527    // Therefore we just skip the indexes for BiDi controls while comparing
528    // with the expected ordering that has them omitted.
529    private boolean checkOrdering(Bidi ubidi) {
530        if(orderingCount<0)
531            return true;
532        boolean isOk=true;
533        int resultLength=ubidi.getResultLength();  // visual length including BiDi controls
534        int i, visualIndex;
535        // Note: It should be faster to call ubidi_countRuns()/ubidi_getVisualRun()
536        // and loop over each run's indexes, but that seems unnecessary for this test code.
537        for(i=visualIndex=0; i<resultLength; ++i) {
538            int logicalIndex=ubidi.getLogicalIndex(i);
539            if(levels[logicalIndex]<0) {
540                continue;  // BiDi control, omitted from expected ordering.
541            }
542            if(visualIndex<orderingCount && logicalIndex!=ordering[visualIndex]) {
543                errln(" ------------ Wrong ordering value at visual index "+visualIndex+"; expected "+
544                      ordering[visualIndex]+" actual "+logicalIndex);
545                isOk=false;
546                break;
547            }
548            ++visualIndex;
549        }
550        // visualIndex is now the visual length minus the BiDi controls,
551        // which should match the length of the BidiTest.txt ordering.
552        if(isOk && orderingCount!=visualIndex) {
553            errln(" ------------ Wrong number of ordering values; expected "+orderingCount+" actual "+visualIndex);
554            isOk=false;
555        }
556        if(!isOk) {
557            printErrorLine();
558            StringBuilder eord=new StringBuilder("Expected ordering: ");
559            for(i=0; i<orderingCount; ++i) {
560                eord.append(' ').append((char)('0'+ordering[i]));
561            }
562            StringBuilder aord=new StringBuilder("Actual   ordering: ");
563            for(i=0; i<resultLength; ++i) {
564                int logicalIndex=ubidi.getLogicalIndex(i);
565                if(levels[logicalIndex]<Bidi.LEVEL_DEFAULT_LTR) {
566                    aord.append(' ').append((char)('0'+logicalIndex));
567                }
568            }
569            errln(eord.toString());
570            errln(aord.toString());
571        }
572        return isOk;
573    }
574
575    private void printErrorLine() {
576        ++errorCount;
577        errln(String.format("Input line %5d:   %s", lineNumber, line));
578        errln("Input string:       "+inputString);
579        errln("Para level:         "+paraLevelName);
580    }
581
582    private static boolean isInvWhitespace(char c) {
583        return ((c)==' ' || (c)=='\t' || (c)=='\r' || (c)=='\n');
584    }
585    /**
586     * Skip isInvWhitespace() characters.
587     * @return true if line.charAt[lineIndex] is a non-whitespace, false if lineIndex>=line.length()
588     */
589    private boolean skipWhitespace() {
590        while(lineIndex<line.length()) {
591            if(!isInvWhitespace(line.charAt(lineIndex))) {
592                return true;
593            }
594            ++lineIndex;
595        }
596        return false;
597    }
598
599    private String line;
600    private int lineIndex;
601    private byte levels[]=new byte[1000];  // UBiDiLevel
602    private int directionBits;
603    private int ordering[]=new int[1000];
604    private int lineNumber;
605    private int levelsCount;
606    private int orderingCount;
607    private int errorCount;
608    private String inputString;
609    private String paraLevelName;
610    private StringBuilder inputStringBuilder=new StringBuilder();
611}
612