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