|  | // © 2016 and later: Unicode, Inc. and others. | 
|  | // License & terms of use: http://www.unicode.org/copyright.html | 
|  | /* | 
|  | ******************************************************************************* | 
|  | * | 
|  | *   Copyright (C) 2009-2014, International Business Machines | 
|  | *   Corporation and others.  All Rights Reserved. | 
|  | * | 
|  | ******************************************************************************* | 
|  | *   file name:  bidiconf.cpp | 
|  | *   encoding:   UTF-8 | 
|  | *   tab size:   8 (not used) | 
|  | *   indentation:4 | 
|  | * | 
|  | *   created on: 2009oct16 | 
|  | *   created by: Markus W. Scherer | 
|  | * | 
|  | *   BiDi conformance test, using the Unicode BidiTest.txt and BidiCharacterTest.txt files. | 
|  | */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include "unicode/utypes.h" | 
|  | #include "unicode/ubidi.h" | 
|  | #include "unicode/errorcode.h" | 
|  | #include "unicode/localpointer.h" | 
|  | #include "unicode/putil.h" | 
|  | #include "unicode/unistr.h" | 
|  | #include "intltest.h" | 
|  | #include "uparse.h" | 
|  |  | 
|  | class BiDiConformanceTest : public IntlTest { | 
|  | public: | 
|  | BiDiConformanceTest() : | 
|  | directionBits(0), lineNumber(0), levelsCount(0), orderingCount(0), | 
|  | errorCount(0) {} | 
|  |  | 
|  | void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=NULL) override; | 
|  |  | 
|  | void TestBidiTest(); | 
|  | void TestBidiCharacterTest(); | 
|  | private: | 
|  | UBool parseLevels(const char *&start); | 
|  | UBool parseOrdering(const char *start); | 
|  | UBool parseInputStringFromBiDiClasses(const char *&start); | 
|  |  | 
|  | UBool checkLevels(const UBiDiLevel actualLevels[], int32_t actualCount); | 
|  | UBool checkOrdering(UBiDi *ubidi); | 
|  |  | 
|  | void printErrorLine(); | 
|  |  | 
|  | char line[10000]; | 
|  | UBiDiLevel levels[1000]; | 
|  | uint32_t directionBits; | 
|  | int32_t ordering[1000]; | 
|  | int32_t lineNumber; | 
|  | int32_t levelsCount; | 
|  | int32_t orderingCount; | 
|  | int32_t errorCount; | 
|  | UnicodeString inputString; | 
|  | const char *paraLevelName; | 
|  | char levelNameString[12]; | 
|  | }; | 
|  |  | 
|  | extern IntlTest *createBiDiConformanceTest() { | 
|  | return new BiDiConformanceTest(); | 
|  | } | 
|  |  | 
|  | void BiDiConformanceTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) { | 
|  | if(exec) { | 
|  | logln("TestSuite BiDiConformanceTest: "); | 
|  | } | 
|  | TESTCASE_AUTO_BEGIN; | 
|  | TESTCASE_AUTO(TestBidiTest); | 
|  | TESTCASE_AUTO(TestBidiCharacterTest); | 
|  | TESTCASE_AUTO_END; | 
|  | } | 
|  |  | 
|  | U_DEFINE_LOCAL_OPEN_POINTER(LocalStdioFilePointer, FILE, fclose); | 
|  |  | 
|  | UBool BiDiConformanceTest::parseLevels(const char *&start) { | 
|  | directionBits=0; | 
|  | levelsCount=0; | 
|  | while(*start!=0 && *(start=u_skipWhitespace(start))!=0 && *start!=';') { | 
|  | if(*start=='x') { | 
|  | levels[levelsCount++]=UBIDI_DEFAULT_LTR; | 
|  | ++start; | 
|  | } else { | 
|  | char *end; | 
|  | uint32_t value=(uint32_t)strtoul(start, &end, 10); | 
|  | if(end<=start || (!U_IS_INV_WHITESPACE(*end) && *end!=0 && *end!=';') | 
|  | || value>(UBIDI_MAX_EXPLICIT_LEVEL+1)) { | 
|  | errln("\nError on line %d: Levels parse error at %s", (int)lineNumber, start); | 
|  | printErrorLine(); | 
|  | return false; | 
|  | } | 
|  | levels[levelsCount++]=(UBiDiLevel)value; | 
|  | directionBits|=(1<<(value&1)); | 
|  | start=end; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | UBool BiDiConformanceTest::parseOrdering(const char *start) { | 
|  | orderingCount=0; | 
|  | while(*start!=0 && *(start=u_skipWhitespace(start))!=0 && *start!=';') { | 
|  | char *end; | 
|  | uint32_t value=(uint32_t)strtoul(start, &end, 10); | 
|  | if(end<=start || (!U_IS_INV_WHITESPACE(*end) && *end!=0 && *end!=';') || value>=1000) { | 
|  | errln("\nError on line %d: Reorder parse error at %s", (int)lineNumber, start); | 
|  | printErrorLine(); | 
|  | return false; | 
|  | } | 
|  | ordering[orderingCount++]=(int32_t)value; | 
|  | start=end; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static const UChar charFromBiDiClass[U_CHAR_DIRECTION_COUNT]={ | 
|  | 0x6c,   // 'l' for L | 
|  | 0x52,   // 'R' for R | 
|  | 0x33,   // '3' for EN | 
|  | 0x2d,   // '-' for ES | 
|  | 0x25,   // '%' for ET | 
|  | 0x39,   // '9' for AN | 
|  | 0x2c,   // ',' for CS | 
|  | 0x2f,   // '/' for B | 
|  | 0x5f,   // '_' for S | 
|  | 0x20,   // ' ' for WS | 
|  | 0x3d,   // '=' for ON | 
|  | 0x65,   // 'e' for LRE | 
|  | 0x6f,   // 'o' for LRO | 
|  | 0x41,   // 'A' for AL | 
|  | 0x45,   // 'E' for RLE | 
|  | 0x4f,   // 'O' for RLO | 
|  | 0x2a,   // '*' for PDF | 
|  | 0x60,   // '`' for NSM | 
|  | 0x7c,   // '|' for BN | 
|  | // new in Unicode 6.3/ICU 52 | 
|  | 0x53,   // 'S' for FSI | 
|  | 0x69,   // 'i' for LRI | 
|  | 0x49,   // 'I' for RLI | 
|  | 0x2e    // '.' for PDI | 
|  | }; | 
|  |  | 
|  | U_CDECL_BEGIN | 
|  |  | 
|  | static UCharDirection U_CALLCONV | 
|  | biDiConfUBiDiClassCallback(const void * /*context*/, UChar32 c) { | 
|  | for(int i=0; i<U_CHAR_DIRECTION_COUNT; ++i) { | 
|  | if(c==charFromBiDiClass[i]) { | 
|  | return (UCharDirection)i; | 
|  | } | 
|  | } | 
|  | // Character not in our hardcoded table. | 
|  | // Should not occur during testing. | 
|  | return U_BIDI_CLASS_DEFAULT; | 
|  | } | 
|  |  | 
|  | U_CDECL_END | 
|  |  | 
|  | static const int8_t biDiClassNameLengths[U_CHAR_DIRECTION_COUNT+1]={ | 
|  | 1, 1, 2, 2, 2, 2, 2, 1, 1, 2, 2, 3, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 0 | 
|  | }; | 
|  |  | 
|  | UBool BiDiConformanceTest::parseInputStringFromBiDiClasses(const char *&start) { | 
|  | inputString.remove(); | 
|  | /* | 
|  | * Lengthy but fast BiDi class parser. | 
|  | * A simple parser could terminate or extract the name string and use | 
|  | *   int32_t biDiClassInt=u_getPropertyValueEnum(UCHAR_BIDI_CLASS, bidiClassString); | 
|  | * but that makes this test take significantly more time. | 
|  | */ | 
|  | while(*start!=0 && *(start=u_skipWhitespace(start))!=0 && *start!=';') { | 
|  | UCharDirection biDiClass=U_CHAR_DIRECTION_COUNT; | 
|  | // Compare each character once until we have a match on | 
|  | // a complete, short BiDi class name. | 
|  | if(start[0]=='L') { | 
|  | if(start[1]=='R') { | 
|  | if(start[2]=='E') { | 
|  | biDiClass=U_LEFT_TO_RIGHT_EMBEDDING; | 
|  | } else if(start[2]=='I') { | 
|  | biDiClass=U_LEFT_TO_RIGHT_ISOLATE; | 
|  | } else if(start[2]=='O') { | 
|  | biDiClass=U_LEFT_TO_RIGHT_OVERRIDE; | 
|  | } | 
|  | } else { | 
|  | biDiClass=U_LEFT_TO_RIGHT; | 
|  | } | 
|  | } else if(start[0]=='R') { | 
|  | if(start[1]=='L') { | 
|  | if(start[2]=='E') { | 
|  | biDiClass=U_RIGHT_TO_LEFT_EMBEDDING; | 
|  | } else if(start[2]=='I') { | 
|  | biDiClass=U_RIGHT_TO_LEFT_ISOLATE; | 
|  | } else if(start[2]=='O') { | 
|  | biDiClass=U_RIGHT_TO_LEFT_OVERRIDE; | 
|  | } | 
|  | } else { | 
|  | biDiClass=U_RIGHT_TO_LEFT; | 
|  | } | 
|  | } else if(start[0]=='E') { | 
|  | if(start[1]=='N') { | 
|  | biDiClass=U_EUROPEAN_NUMBER; | 
|  | } else if(start[1]=='S') { | 
|  | biDiClass=U_EUROPEAN_NUMBER_SEPARATOR; | 
|  | } else if(start[1]=='T') { | 
|  | biDiClass=U_EUROPEAN_NUMBER_TERMINATOR; | 
|  | } | 
|  | } else if(start[0]=='A') { | 
|  | if(start[1]=='L') { | 
|  | biDiClass=U_RIGHT_TO_LEFT_ARABIC; | 
|  | } else if(start[1]=='N') { | 
|  | biDiClass=U_ARABIC_NUMBER; | 
|  | } | 
|  | } else if(start[0]=='C' && start[1]=='S') { | 
|  | biDiClass=U_COMMON_NUMBER_SEPARATOR; | 
|  | } else if(start[0]=='B') { | 
|  | if(start[1]=='N') { | 
|  | biDiClass=U_BOUNDARY_NEUTRAL; | 
|  | } else { | 
|  | biDiClass=U_BLOCK_SEPARATOR; | 
|  | } | 
|  | } else if(start[0]=='S') { | 
|  | biDiClass=U_SEGMENT_SEPARATOR; | 
|  | } else if(start[0]=='W' && start[1]=='S') { | 
|  | biDiClass=U_WHITE_SPACE_NEUTRAL; | 
|  | } else if(start[0]=='O' && start[1]=='N') { | 
|  | biDiClass=U_OTHER_NEUTRAL; | 
|  | } else if(start[0]=='P' && start[1]=='D') { | 
|  | if(start[2]=='F') { | 
|  | biDiClass=U_POP_DIRECTIONAL_FORMAT; | 
|  | } else if(start[2]=='I') { | 
|  | biDiClass=U_POP_DIRECTIONAL_ISOLATE; | 
|  | } | 
|  | } else if(start[0]=='N' && start[1]=='S' && start[2]=='M') { | 
|  | biDiClass=U_DIR_NON_SPACING_MARK; | 
|  | } else if(start[0]=='F' && start[1]=='S' && start[2]=='I') { | 
|  | biDiClass=U_FIRST_STRONG_ISOLATE; | 
|  | } | 
|  | // Now we verify that the class name is terminated properly, | 
|  | // and not just the start of a longer word. | 
|  | int8_t biDiClassNameLength=biDiClassNameLengths[biDiClass]; | 
|  | char c=start[biDiClassNameLength]; | 
|  | if(biDiClass<U_CHAR_DIRECTION_COUNT && (U_IS_INV_WHITESPACE(c) || c==';' || c==0)) { | 
|  | inputString.append(charFromBiDiClass[biDiClass]); | 
|  | start+=biDiClassNameLength; | 
|  | continue; | 
|  | } | 
|  | errln("\nError on line %d: BiDi class string not recognized at %s", (int)lineNumber, start); | 
|  | printErrorLine(); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void BiDiConformanceTest::TestBidiTest() { | 
|  | IcuTestErrorCode errorCode(*this, "TestBidiTest"); | 
|  | const char *sourceTestDataPath=getSourceTestData(errorCode); | 
|  | if(errorCode.errIfFailureAndReset("unable to find the source/test/testdata " | 
|  | "folder (getSourceTestData())")) { | 
|  | return; | 
|  | } | 
|  | char bidiTestPath[400]; | 
|  | strcpy(bidiTestPath, sourceTestDataPath); | 
|  | strcat(bidiTestPath, "BidiTest.txt"); | 
|  | LocalStdioFilePointer bidiTestFile(fopen(bidiTestPath, "r")); | 
|  | if(bidiTestFile.isNull()) { | 
|  | errln("unable to open %s", bidiTestPath); | 
|  | return; | 
|  | } | 
|  | LocalUBiDiPointer ubidi(ubidi_open()); | 
|  | ubidi_setClassCallback(ubidi.getAlias(), biDiConfUBiDiClassCallback, NULL, | 
|  | NULL, NULL, errorCode); | 
|  | if(errorCode.errIfFailureAndReset("ubidi_setClassCallback()")) { | 
|  | return; | 
|  | } | 
|  | lineNumber=0; | 
|  | levelsCount=0; | 
|  | orderingCount=0; | 
|  | errorCount=0; | 
|  | // paraLevelName must be initialized in case the first non-comment line is in error | 
|  | paraLevelName="N/A"; | 
|  | while(errorCount<10 && fgets(line, (int)sizeof(line), bidiTestFile.getAlias())!=NULL) { | 
|  | ++lineNumber; | 
|  | // Remove trailing comments and whitespace. | 
|  | char *commentStart=strchr(line, '#'); | 
|  | if(commentStart!=NULL) { | 
|  | *commentStart=0; | 
|  | } | 
|  | u_rtrim(line); | 
|  | const char *start=u_skipWhitespace(line); | 
|  | if(*start==0) { | 
|  | continue;  // Skip empty and comment-only lines. | 
|  | } | 
|  | if(*start=='@') { | 
|  | ++start; | 
|  | if(0==strncmp(start, "Levels:", 7)) { | 
|  | start+=7; | 
|  | if(!parseLevels(start)) { | 
|  | return; | 
|  | } | 
|  | } else if(0==strncmp(start, "Reorder:", 8)) { | 
|  | if(!parseOrdering(start+8)) { | 
|  | return; | 
|  | } | 
|  | } | 
|  | // Skip unknown @Xyz: ... | 
|  | } else { | 
|  | if(!parseInputStringFromBiDiClasses(start)) { | 
|  | return; | 
|  | } | 
|  | start=u_skipWhitespace(start); | 
|  | if(*start!=';') { | 
|  | errln("missing ; separator on input line %s", line); | 
|  | return; | 
|  | } | 
|  | start=u_skipWhitespace(start+1); | 
|  | char *end; | 
|  | uint32_t bitset=(uint32_t)strtoul(start, &end, 16); | 
|  | if(end<=start || (!U_IS_INV_WHITESPACE(*end) && *end!=';' && *end!=0)) { | 
|  | errln("input bitset parse error at %s", start); | 
|  | return; | 
|  | } | 
|  | // Loop over the bitset. | 
|  | static const UBiDiLevel paraLevels[]={ UBIDI_DEFAULT_LTR, 0, 1, UBIDI_DEFAULT_RTL }; | 
|  | static const char *const paraLevelNames[]={ "auto/LTR", "LTR", "RTL", "auto/RTL" }; | 
|  | for(int i=0; i<=3; ++i) { | 
|  | if(bitset&(1<<i)) { | 
|  | ubidi_setPara(ubidi.getAlias(), inputString.getBuffer(), inputString.length(), | 
|  | paraLevels[i], NULL, errorCode); | 
|  | const UBiDiLevel *actualLevels=ubidi_getLevels(ubidi.getAlias(), errorCode); | 
|  | if(errorCode.errIfFailureAndReset("ubidi_setPara() or ubidi_getLevels()")) { | 
|  | errln("Input line %d: %s", (int)lineNumber, line); | 
|  | return; | 
|  | } | 
|  | paraLevelName=paraLevelNames[i]; | 
|  | if(!checkLevels(actualLevels, ubidi_getProcessedLength(ubidi.getAlias()))) { | 
|  | // continue outerLoop;  does not exist in C++ | 
|  | // so just break out of the inner loop. | 
|  | break; | 
|  | } | 
|  | if(!checkOrdering(ubidi.getAlias())) { | 
|  | // continue outerLoop;  does not exist in C++ | 
|  | // so just break out of the inner loop. | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | ******************************************************************************* | 
|  | * | 
|  | *   created on: 2013jul01 | 
|  | *   created by: Matitiahu Allouche | 
|  |  | 
|  | This function performs a conformance test for implementations of the | 
|  | Unicode Bidirectional Algorithm, specified in UAX #9: Unicode | 
|  | Bidirectional Algorithm, at https://www.unicode.org/reports/tr9/ | 
|  |  | 
|  | Each test case is represented in a single line which is read from a file | 
|  | named BidiCharacter.txt.  Empty, blank and comment lines may also appear | 
|  | in this file. | 
|  |  | 
|  | The format of the test data is specified below.  Note that each test | 
|  | case constitutes a single line of text; reordering is applied within a | 
|  | single line and independently of a rendering engine, and rules L3 and L4 | 
|  | are out of scope. | 
|  |  | 
|  | The number sign '#' is the comment character: everything is ignored from | 
|  | the occurrence of '#' until the end of the line, | 
|  | Empty lines and lines containing only spaces and/or comments are ignored. | 
|  |  | 
|  | Lines which represent test cases consist of 4 or 5 fields separated by a | 
|  | semicolon.  Each field consists of tokens separated by whitespace (space | 
|  | or Tab).  Whitespace before and after semicolons is optional. | 
|  |  | 
|  | Field 0: A sequence of hexadecimal code point values separated by space | 
|  |  | 
|  | Field 1: A value representing the paragraph direction, as follows: | 
|  | - 0 represents left-to-right | 
|  | - 1 represents right-to-left | 
|  | - 2 represents auto-LTR according to rules P2 and P3 of the algorithm | 
|  | - 3 represents auto-RTL according to rules P2 and P3 of the algorithm | 
|  | - a negative number whose absolute value is taken as paragraph level; | 
|  | this may be useful to test cases where the embedding level approaches | 
|  | or exceeds the maximum embedding level. | 
|  |  | 
|  | Field 2: The resolved paragraph embedding level.  If the input (field 0) | 
|  | includes more than one paragraph, this field represents the | 
|  | resolved level of the first paragraph. | 
|  |  | 
|  | Field 3: An ordered list of resulting levels for each token in field 0 | 
|  | (each token represents one source character). | 
|  | The UBA does not assign levels to certain characters (e.g. LRO); | 
|  | characters removed in rule X9 are indicated with an 'x'. | 
|  |  | 
|  | Field 4: An ordered list of indices showing the resulting visual ordering | 
|  | from left to right; characters with a resolved level of 'x' are | 
|  | skipped.  The number are zero-based.  Each index corresponds to | 
|  | a character in the reordered (visual) string. It represents the | 
|  | index of the source character in the input (field 0). | 
|  | This field is optional.  When it is absent, the visual ordering | 
|  | is not verified. | 
|  |  | 
|  | Examples: | 
|  |  | 
|  | # This is a comment line. | 
|  | L L ON R ; 0 ; 0 ; 0 0 0 1 ; 0 1 2 3 | 
|  | L L ON R;0;0;0 0 0 1;0 1 2 3 | 
|  |  | 
|  | # Note: in the next line, 'B' represents a block separator, not the letter 'B'. | 
|  | LRE A B C PDF;2;0;x 2 0 0 x;1 2 3 | 
|  | # Note: in the next line, 'b' represents the letter 'b', not a block separator. | 
|  | a b c 05d0 05d1 x ; 0 ; 0 ; 0 0 0 1 1 0 ; 0 1 2 4 3 5 | 
|  |  | 
|  | a R R x ; 1 ; 1 ; 2 1 1 2 | 
|  | 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 | 
|  |  | 
|  | * | 
|  | ******************************************************************************* | 
|  | */ | 
|  | void BiDiConformanceTest::TestBidiCharacterTest() { | 
|  | IcuTestErrorCode errorCode(*this, "TestBidiCharacterTest"); | 
|  | const char *sourceTestDataPath=getSourceTestData(errorCode); | 
|  | if(errorCode.errIfFailureAndReset("unable to find the source/test/testdata " | 
|  | "folder (getSourceTestData())")) { | 
|  | return; | 
|  | } | 
|  | char bidiTestPath[400]; | 
|  | strcpy(bidiTestPath, sourceTestDataPath); | 
|  | strcat(bidiTestPath, "BidiCharacterTest.txt"); | 
|  | LocalStdioFilePointer bidiTestFile(fopen(bidiTestPath, "r")); | 
|  | if(bidiTestFile.isNull()) { | 
|  | errln("unable to open %s", bidiTestPath); | 
|  | return; | 
|  | } | 
|  | LocalUBiDiPointer ubidi(ubidi_open()); | 
|  | lineNumber=0; | 
|  | levelsCount=0; | 
|  | orderingCount=0; | 
|  | errorCount=0; | 
|  | while(errorCount<20 && fgets(line, (int)sizeof(line), bidiTestFile.getAlias())!=NULL) { | 
|  | ++lineNumber; | 
|  | paraLevelName="N/A"; | 
|  | inputString="N/A"; | 
|  | // Remove trailing comments and whitespace. | 
|  | char *commentStart=strchr(line, '#'); | 
|  | if(commentStart!=NULL) { | 
|  | *commentStart=0; | 
|  | } | 
|  | u_rtrim(line); | 
|  | const char *start=u_skipWhitespace(line); | 
|  | if(*start==0) { | 
|  | continue;  // Skip empty and comment-only lines. | 
|  | } | 
|  | // Parse the code point string in field 0. | 
|  | UChar *buffer=inputString.getBuffer(200); | 
|  | int32_t length=u_parseString(start, buffer, inputString.getCapacity(), NULL, errorCode); | 
|  | if(errorCode.errIfFailureAndReset("Invalid string in field 0")) { | 
|  | errln("Input line %d: %s", (int)lineNumber, line); | 
|  | inputString.remove(); | 
|  | continue; | 
|  | } | 
|  | inputString.releaseBuffer(length); | 
|  | start=strchr(start, ';'); | 
|  | if(start==NULL) { | 
|  | errorCount++; | 
|  | errln("\nError on line %d: Missing ; separator on line: %s", (int)lineNumber, line); | 
|  | continue; | 
|  | } | 
|  | start=u_skipWhitespace(start+1); | 
|  | char *end; | 
|  | int32_t paraDirection=(int32_t)strtol(start, &end, 10); | 
|  | UBiDiLevel paraLevel=UBIDI_MAX_EXPLICIT_LEVEL+2; | 
|  | if(paraDirection==0) { | 
|  | paraLevel=0; | 
|  | paraLevelName="LTR"; | 
|  | } | 
|  | else if(paraDirection==1) { | 
|  | paraLevel=1; | 
|  | paraLevelName="RTL"; | 
|  | } | 
|  | else if(paraDirection==2) { | 
|  | paraLevel=UBIDI_DEFAULT_LTR; | 
|  | paraLevelName="Auto/LTR"; | 
|  | } | 
|  | else if(paraDirection==3) { | 
|  | paraLevel=UBIDI_DEFAULT_RTL; | 
|  | paraLevelName="Auto/RTL"; | 
|  | } | 
|  | else if(paraDirection<0 && -paraDirection<=(UBIDI_MAX_EXPLICIT_LEVEL+1)) { | 
|  | paraLevel=(UBiDiLevel)(-paraDirection); | 
|  | sprintf(levelNameString, "%d", (int)paraLevel); | 
|  | paraLevelName=levelNameString; | 
|  | } | 
|  | if(end<=start || (!U_IS_INV_WHITESPACE(*end) && *end!=';' && *end!=0) || | 
|  | paraLevel==(UBIDI_MAX_EXPLICIT_LEVEL+2)) { | 
|  | errln("\nError on line %d: Input paragraph direction incorrect at %s", (int)lineNumber, start); | 
|  | printErrorLine(); | 
|  | continue; | 
|  | } | 
|  | start=u_skipWhitespace(end); | 
|  | if(*start!=';') { | 
|  | errorCount++; | 
|  | errln("\nError on line %d: Missing ; separator on line: %s", (int)lineNumber, line); | 
|  | continue; | 
|  | } | 
|  | start++; | 
|  | uint32_t resolvedParaLevel=(uint32_t)strtoul(start, &end, 10); | 
|  | if(end<=start || (!U_IS_INV_WHITESPACE(*end) && *end!=';' && *end!=0) || | 
|  | resolvedParaLevel>1) { | 
|  | errln("\nError on line %d: Resolved paragraph level incorrect at %s", (int)lineNumber, start); | 
|  | printErrorLine(); | 
|  | continue; | 
|  | } | 
|  | start=u_skipWhitespace(end); | 
|  | if(*start!=';') { | 
|  | errorCount++; | 
|  | errln("\nError on line %d: Missing ; separator on line: %s", (int)lineNumber, line); | 
|  | return; | 
|  | } | 
|  | start++; | 
|  | if(!parseLevels(start)) { | 
|  | continue; | 
|  | } | 
|  | start=u_skipWhitespace(start); | 
|  | if(*start==';') { | 
|  | if(!parseOrdering(start+1)) { | 
|  | continue; | 
|  | } | 
|  | } | 
|  | else | 
|  | orderingCount=-1; | 
|  |  | 
|  | ubidi_setPara(ubidi.getAlias(), inputString.getBuffer(), inputString.length(), | 
|  | paraLevel, NULL, errorCode); | 
|  | const UBiDiLevel *actualLevels=ubidi_getLevels(ubidi.getAlias(), errorCode); | 
|  | if(errorCode.errIfFailureAndReset("ubidi_setPara() or ubidi_getLevels()")) { | 
|  | errln("Input line %d: %s", (int)lineNumber, line); | 
|  | continue; | 
|  | } | 
|  | UBiDiLevel actualLevel; | 
|  | if((actualLevel=ubidi_getParaLevel(ubidi.getAlias()))!=resolvedParaLevel) { | 
|  | printErrorLine(); | 
|  | errln("\nError on line %d: Wrong resolved paragraph level; expected %d actual %d", | 
|  | (int)lineNumber, resolvedParaLevel, actualLevel); | 
|  | continue; | 
|  | } | 
|  | if(!checkLevels(actualLevels, ubidi_getProcessedLength(ubidi.getAlias()))) { | 
|  | continue; | 
|  | } | 
|  | if(orderingCount>=0 && !checkOrdering(ubidi.getAlias())) { | 
|  | continue; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static UChar printLevel(UBiDiLevel level) { | 
|  | if(level<UBIDI_DEFAULT_LTR) { | 
|  | return 0x30+level; | 
|  | } else { | 
|  | return 0x78;  // 'x' | 
|  | } | 
|  | } | 
|  |  | 
|  | static uint32_t getDirectionBits(const UBiDiLevel actualLevels[], int32_t actualCount) { | 
|  | uint32_t actualDirectionBits=0; | 
|  | for(int32_t i=0; i<actualCount; ++i) { | 
|  | actualDirectionBits|=(1<<(actualLevels[i]&1)); | 
|  | } | 
|  | return actualDirectionBits; | 
|  | } | 
|  |  | 
|  | UBool BiDiConformanceTest::checkLevels(const UBiDiLevel actualLevels[], int32_t actualCount) { | 
|  | UBool isOk=true; | 
|  | if(levelsCount!=actualCount) { | 
|  | errln("\nError on line %d: Wrong number of level values; expected %d actual %d", | 
|  | (int)lineNumber, (int)levelsCount, (int)actualCount); | 
|  | isOk=false; | 
|  | } else { | 
|  | for(int32_t i=0; i<actualCount; ++i) { | 
|  | if(levels[i]!=actualLevels[i] && levels[i]<UBIDI_DEFAULT_LTR) { | 
|  | if(directionBits!=3 && directionBits==getDirectionBits(actualLevels, actualCount)) { | 
|  | // ICU used a shortcut: | 
|  | // Since the text is unidirectional, it did not store the resolved | 
|  | // levels but just returns all levels as the paragraph level 0 or 1. | 
|  | // The reordering result is the same, so this is fine. | 
|  | break; | 
|  | } else { | 
|  | errln("\nError on line %d: Wrong level value at index %d; expected %d actual %d", | 
|  | (int)lineNumber, (int)i, levels[i], actualLevels[i]); | 
|  | isOk=false; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if(!isOk) { | 
|  | printErrorLine(); | 
|  | UnicodeString els("Expected levels:   "); | 
|  | int32_t i; | 
|  | for(i=0; i<levelsCount; ++i) { | 
|  | els.append((UChar)0x20).append(printLevel(levels[i])); | 
|  | } | 
|  | UnicodeString als("Actual   levels:   "); | 
|  | for(i=0; i<actualCount; ++i) { | 
|  | als.append((UChar)0x20).append(printLevel(actualLevels[i])); | 
|  | } | 
|  | errln(els); | 
|  | errln(als); | 
|  | } | 
|  | return isOk; | 
|  | } | 
|  |  | 
|  | // Note: ubidi_setReorderingOptions(ubidi, UBIDI_OPTION_REMOVE_CONTROLS); | 
|  | // does not work for custom BiDi class assignments | 
|  | // and anyway also removes LRM/RLM/ZWJ/ZWNJ which is not desirable here. | 
|  | // Therefore we just skip the indexes for BiDi controls while comparing | 
|  | // with the expected ordering that has them omitted. | 
|  | UBool BiDiConformanceTest::checkOrdering(UBiDi *ubidi) { | 
|  | UBool isOk=true; | 
|  | IcuTestErrorCode errorCode(*this, "checkOrdering()"); | 
|  | int32_t resultLength=ubidi_getResultLength(ubidi);  // visual length including BiDi controls | 
|  | int32_t i, visualIndex; | 
|  | // Note: It should be faster to call ubidi_countRuns()/ubidi_getVisualRun() | 
|  | // and loop over each run's indexes, but that seems unnecessary for this test code. | 
|  | for(i=visualIndex=0; i<resultLength; ++i) { | 
|  | int32_t logicalIndex=ubidi_getLogicalIndex(ubidi, i, errorCode); | 
|  | if(errorCode.errIfFailureAndReset("ubidi_getLogicalIndex()")) { | 
|  | errln("Input line %d: %s", (int)lineNumber, line); | 
|  | return false; | 
|  | } | 
|  | if(levels[logicalIndex]>=UBIDI_DEFAULT_LTR) { | 
|  | continue;  // BiDi control, omitted from expected ordering. | 
|  | } | 
|  | if(visualIndex<orderingCount && logicalIndex!=ordering[visualIndex]) { | 
|  | errln("\nError on line %d: Wrong ordering value at visual index %d; expected %d actual %d", | 
|  | (int)lineNumber, (int)visualIndex, ordering[visualIndex], logicalIndex); | 
|  | isOk=false; | 
|  | break; | 
|  | } | 
|  | ++visualIndex; | 
|  | } | 
|  | // visualIndex is now the visual length minus the BiDi controls, | 
|  | // which should match the length of the BidiTest.txt ordering. | 
|  | if(isOk && orderingCount!=visualIndex) { | 
|  | errln("\nError on line %d: Wrong number of ordering values; expected %d actual %d", | 
|  | (int)lineNumber, (int)orderingCount, (int)visualIndex); | 
|  | isOk=false; | 
|  | } | 
|  | if(!isOk) { | 
|  | printErrorLine(); | 
|  | UnicodeString eord("Expected ordering: "); | 
|  | for(i=0; i<orderingCount; ++i) { | 
|  | eord.append((UChar)0x20).append((UChar)(0x30+ordering[i])); | 
|  | } | 
|  | UnicodeString aord("Actual   ordering: "); | 
|  | for(i=0; i<resultLength; ++i) { | 
|  | int32_t logicalIndex=ubidi_getLogicalIndex(ubidi, i, errorCode); | 
|  | if(levels[logicalIndex]<UBIDI_DEFAULT_LTR) { | 
|  | aord.append((UChar)0x20).append((UChar)(0x30+logicalIndex)); | 
|  | } | 
|  | } | 
|  | errln(eord); | 
|  | errln(aord); | 
|  | } | 
|  | return isOk; | 
|  | } | 
|  |  | 
|  | void BiDiConformanceTest::printErrorLine() { | 
|  | ++errorCount; | 
|  | errln("Input line %5d:   %s", (int)lineNumber, line); | 
|  | errln(UnicodeString("Input string:       ")+inputString); | 
|  | errln("Para level:         %s", paraLevelName); | 
|  | } |