You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GenerateBidiTestData.java 46KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.complexscripts.bidi;
  19. import java.io.BufferedReader;
  20. import java.io.File;
  21. import java.io.FileOutputStream;
  22. import java.io.FileWriter;
  23. import java.io.IOException;
  24. import java.io.InputStreamReader;
  25. import java.io.ObjectOutputStream;
  26. import java.io.PrintWriter;
  27. import java.net.URL;
  28. import java.text.CharacterIterator;
  29. import java.text.StringCharacterIterator;
  30. import java.util.ArrayList;
  31. import java.util.HashMap;
  32. import java.util.Iterator;
  33. import java.util.List;
  34. import java.util.Map;
  35. import java.util.TreeMap;
  36. import org.apache.fop.util.License;
  37. // CSOFF: LineLengthCheck
  38. // CSOFF: NoWhitespaceAfterCheck
  39. // CSOFF: InnerAssignmentCheck
  40. // CSOFF: SimplifyBooleanReturnCheck
  41. // CSOFF: EmptyForIteratorPadCheck
  42. /**
  43. * <p>Utility for generating a Java class and associated data files representing
  44. * bidirectional confomance test data from the Unicode Character Database and
  45. * Unicode BidiTest data files.</p>
  46. *
  47. * <p>This code is derived in part from GenerateBidiClassUtils.java.</p>
  48. *
  49. * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
  50. */
  51. public final class GenerateBidiTestData {
  52. // local constants
  53. private static final String PFX_TYPE = "@Type:";
  54. private static final String PFX_LEVELS = "@Levels:";
  55. private static final String PFX_REORDER = "@Reorder:";
  56. // command line options
  57. private static boolean ignoreDeprecatedTypeData;
  58. private static boolean verbose;
  59. // instrumentation
  60. private static int lineNumber;
  61. private static int numTypeRanges;
  62. private static int numLevelSpecs;
  63. private static int numTestSpecs;
  64. // compiled data
  65. private static int[][] td; // types data
  66. private static int[][] ld; // levels data
  67. // ensure non-instantiation
  68. private GenerateBidiTestData() {
  69. }
  70. /**
  71. * Generate a class managing bidi test data for Unicode characters.
  72. *
  73. * @param ucdFileName name (as URL) of file containing unicode character database data
  74. * @param bidiFileName name (as URL) of file containing bidi test data
  75. * @param outFileName name of the output class file
  76. * @throws Exception
  77. */
  78. private static void convertBidiTestData(String ucdFileName, String bidiFileName, String outFileName) throws Exception {
  79. // read type data from UCD if ignoring deprecated type data
  80. if ( ignoreDeprecatedTypeData ) {
  81. readBidiTypeData(ucdFileName);
  82. }
  83. // read bidi test data
  84. readBidiTestData(bidiFileName);
  85. // generate class
  86. PrintWriter out = new PrintWriter(new FileWriter(outFileName));
  87. License.writeJavaLicenseId(out);
  88. out.println();
  89. out.println("package org.apache.fop.complexscripts.bidi;");
  90. out.println();
  91. out.println("import java.io.IOException;");
  92. out.println("import java.io.InputStream;");
  93. out.println("import java.io.ObjectInputStream;");
  94. out.println();
  95. out.println("// CSOFF: WhitespaceAfterCheck");
  96. out.println();
  97. out.println("/*");
  98. out.println(" * !!! THIS IS A GENERATED FILE !!!");
  99. out.println(" * If updates to the source are needed, then:");
  100. out.println(" * - apply the necessary modifications to");
  101. out.println(" * 'src/codegen/unicode/java/org/apache/fop/text/bidi/GenerateBidiTestData.java'");
  102. out.println(" * - run 'ant codegen-unicode', which will generate a new BidiTestData.java");
  103. out.println(" * in 'test/java/org/apache/fop/complexscripts/bidi'");
  104. out.println(" * - commit BOTH changed files");
  105. out.println(" */");
  106. out.println();
  107. out.println("/** Bidirectional test data. */");
  108. out.println("public final class BidiTestData {");
  109. out.println();
  110. out.println(" private BidiTestData() {");
  111. out.println(" }");
  112. out.println();
  113. dumpData ( out, outFileName );
  114. out.println(" public static final int NUM_TEST_SEQUENCES = " + numTestSpecs + ";");
  115. out.println();
  116. out.println(" public static int[] readTestData ( String prefix, int index ) {");
  117. out.println(" int[] data = null;");
  118. out.println(" InputStream is = null;");
  119. out.println(" Class btc = BidiTestData.class;");
  120. out.println(" String name = btc.getSimpleName() + \"$\" + prefix + index + \".ser\";");
  121. out.println(" try {");
  122. out.println(" if ( ( is = btc.getResourceAsStream ( name ) ) != null ) {");
  123. out.println(" ObjectInputStream ois = new ObjectInputStream ( is );");
  124. out.println(" data = (int[]) ois.readObject();");
  125. out.println(" ois.close();");
  126. out.println(" }");
  127. out.println(" } catch ( IOException e ) {");
  128. out.println(" data = null;");
  129. out.println(" } catch ( ClassNotFoundException e ) {");
  130. out.println(" data = null;");
  131. out.println(" } finally {");
  132. out.println(" if ( is != null ) {");
  133. out.println(" try { is.close(); } catch ( Exception e ) {}");
  134. out.println(" }");
  135. out.println(" }");
  136. out.println(" return data;");
  137. out.println(" }");
  138. out.println("}");
  139. out.flush();
  140. out.close();
  141. }
  142. /**
  143. * Read bidi type data.
  144. *
  145. * @param ucdFileName name (as URL) of unicode character database data
  146. */
  147. private static void readBidiTypeData(String ucdFileName) throws Exception {
  148. BufferedReader b = new BufferedReader(new InputStreamReader(new URL(ucdFileName).openStream()));
  149. String line;
  150. int n;
  151. // singleton map - derived from single char entry
  152. Map/*<Integer,List>*/ sm = new HashMap/*<Integer,List>*/();
  153. // interval map - derived from pair of block endpoint entries
  154. Map/*<String,int[3]>*/ im = new HashMap/*<String,int[3]>*/();
  155. if ( verbose ) {
  156. System.out.print("Reading bidi type data...");
  157. }
  158. for ( lineNumber = 0; ( line = b.readLine() ) != null; ) {
  159. lineNumber++;
  160. if ( line.length() == 0 ) {
  161. continue;
  162. } else if ( line.startsWith("#") ) {
  163. continue;
  164. } else {
  165. parseTypeProperties ( line, sm, im );
  166. }
  167. }
  168. // extract type data list
  169. List tdl = processTypeData ( sm, im, new ArrayList() );
  170. // dump instrumentation
  171. if ( verbose ) {
  172. System.out.println();
  173. System.out.println("Read type ranges : " + numTypeRanges );
  174. System.out.println("Read lines : " + lineNumber );
  175. }
  176. td = (int[][]) tdl.toArray ( new int [ tdl.size() ] [] );
  177. }
  178. private static void parseTypeProperties ( String line, Map/*<Integer,List>*/ sm, Map/*<String,int[3]>*/ im ) {
  179. String[] sa = line.split(";");
  180. if ( sa.length >= 5 ) {
  181. int uc = Integer.parseInt ( sa[0], 16 );
  182. int bc = parseBidiClassAny ( sa[4] );
  183. if ( bc >= 0 ) {
  184. String ucName = sa[1];
  185. if ( isBlockStart ( ucName ) ) {
  186. String ucBlock = getBlockName ( ucName );
  187. if ( ! im.containsKey ( ucBlock ) ) {
  188. im.put ( ucBlock, new int[] { uc, -1, bc } );
  189. } else {
  190. throw new IllegalArgumentException ( "duplicate start of block '" + ucBlock + "' at entry: " + line );
  191. }
  192. } else if ( isBlockEnd ( ucName ) ) {
  193. String ucBlock = getBlockName ( ucName );
  194. if ( im.containsKey ( ucBlock ) ) {
  195. int[] ba = (int[]) im.get ( ucBlock );
  196. assert ba.length == 3;
  197. if ( ba[1] < 0 ) {
  198. ba[1] = uc;
  199. } else {
  200. throw new IllegalArgumentException ( "duplicate end of block '" + ucBlock + "' at entry: " + line );
  201. }
  202. } else {
  203. throw new IllegalArgumentException ( "missing start of block '" + ucBlock + "' at entry: " + line );
  204. }
  205. } else {
  206. Integer k = Integer.valueOf ( bc );
  207. List sl;
  208. if ( ! sm.containsKey ( k ) ) {
  209. sl = new ArrayList();
  210. sm.put ( k, sl );
  211. } else {
  212. sl = (List) sm.get ( k );
  213. }
  214. assert sl != null;
  215. sl.add ( Integer.valueOf ( uc ) );
  216. }
  217. } else {
  218. throw new IllegalArgumentException ( "invalid bidi class '" + sa[4] + "' at entry: " + line );
  219. }
  220. } else {
  221. throw new IllegalArgumentException ( "invalid unicode character database entry: " + line );
  222. }
  223. }
  224. private static boolean isBlockStart ( String s ) {
  225. return s.startsWith("<") && s.endsWith("First>");
  226. }
  227. private static boolean isBlockEnd ( String s ) {
  228. return s.startsWith("<") && s.endsWith("Last>");
  229. }
  230. private static String getBlockName ( String s ) {
  231. String[] sa = s.substring ( 1, s.length() - 1 ).split(",");
  232. assert ( sa != null ) && ( sa.length > 0 );
  233. return sa[0].trim();
  234. }
  235. private static List processTypeData ( Map/*<Integer,List>*/ sm, Map/*<String,int[3]>*/ im, List tdl ) {
  236. for ( int i = BidiConstants.FIRST, k = BidiConstants.LAST; i <= k; i++ ) {
  237. Map/*<Integer,Integer>*/ rm = new TreeMap/*<Integer,Integer>*/();
  238. // populate intervals from singleton map
  239. List sl = (List) sm.get ( Integer.valueOf ( i ) );
  240. if ( sl != null ) {
  241. for ( Iterator it = sl.iterator(); it.hasNext(); ) {
  242. Integer s = (Integer) it.next();
  243. int uc = s.intValue();
  244. rm.put ( Integer.valueOf ( uc ), Integer.valueOf ( uc + 1 ) );
  245. }
  246. }
  247. // populate intervals from (block) interval map
  248. if ( ! im.isEmpty() ) {
  249. for ( Iterator it = im.values().iterator(); it.hasNext(); ) {
  250. int[] ba = (int[]) it.next();
  251. assert ( ba != null ) && ( ba.length > 2 );
  252. if ( ba[2] == i ) {
  253. rm.put ( Integer.valueOf ( ba[0] ), Integer.valueOf ( ba[1] + 1 ) );
  254. }
  255. }
  256. }
  257. tdl.add ( createTypeData ( i, extractRanges ( rm ) ) );
  258. }
  259. return tdl;
  260. }
  261. private static List extractRanges ( Map/*<Integer,Integer>*/ rm ) {
  262. List ranges = new ArrayList();
  263. int sLast = 0;
  264. int eLast = 0;
  265. for ( Iterator it = rm.entrySet().iterator(); it.hasNext(); ) {
  266. Map.Entry/*<Integer,Integer>*/ me = (Map.Entry/*<Integer,Integer>*/) it.next();
  267. int s = ((Integer) me.getKey()).intValue();
  268. int e = ((Integer) me.getValue()).intValue();
  269. if ( s > eLast ) {
  270. if ( eLast > sLast ) {
  271. ranges.add ( new int[] { sLast, eLast } );
  272. if ( verbose ) {
  273. if ( ( ++numTypeRanges % 10 ) == 0 ) {
  274. System.out.print("#");
  275. }
  276. }
  277. }
  278. sLast = s;
  279. eLast = e;
  280. } else if ( ( s >= sLast ) && ( e >= eLast ) ) {
  281. eLast = e;
  282. }
  283. }
  284. if ( eLast > sLast ) {
  285. ranges.add ( new int[] { sLast, eLast } );
  286. if ( verbose ) {
  287. if ( ( ++numTypeRanges % 10 ) == 0 ) {
  288. System.out.print("#");
  289. }
  290. }
  291. }
  292. return ranges;
  293. }
  294. /**
  295. * Read biditest data.
  296. *
  297. * @param bidiFileName name (as URL) of bidi test data
  298. */
  299. private static void readBidiTestData(String bidiFileName) throws Exception {
  300. BufferedReader b = new BufferedReader(new InputStreamReader(new URL(bidiFileName).openStream()));
  301. String line;
  302. int n;
  303. List tdl = new ArrayList();
  304. List ldl = new ArrayList();
  305. if ( verbose ) {
  306. System.out.print("Reading bidi test data...");
  307. }
  308. for ( lineNumber = 0; ( line = b.readLine() ) != null; ) {
  309. lineNumber++;
  310. if ( line.length() == 0 ) {
  311. continue;
  312. } else if ( line.startsWith("#") ) {
  313. continue;
  314. } else if ( line.startsWith(PFX_TYPE) && ! ignoreDeprecatedTypeData ) {
  315. List lines = new ArrayList();
  316. if ( ( n = readType ( line, b, lines ) ) < 0 ) {
  317. break;
  318. } else {
  319. lineNumber += n;
  320. tdl.add ( parseType ( lines ) );
  321. }
  322. } else if ( line.startsWith(PFX_LEVELS) ) {
  323. List lines = new ArrayList();
  324. if ( ( n = readLevels ( line, b, lines ) ) < 0 ) {
  325. break;
  326. } else {
  327. lineNumber += n;
  328. ldl.add ( parseLevels ( lines ) );
  329. }
  330. }
  331. }
  332. // dump instrumentation
  333. if ( verbose ) {
  334. System.out.println();
  335. if ( ! ignoreDeprecatedTypeData ) {
  336. System.out.println("Read type ranges : " + numTypeRanges );
  337. }
  338. System.out.println("Read level specs : " + numLevelSpecs );
  339. System.out.println("Read test specs : " + numTestSpecs );
  340. System.out.println("Read lines : " + lineNumber );
  341. }
  342. if ( ! ignoreDeprecatedTypeData ) {
  343. td = (int[][]) tdl.toArray ( new int [ tdl.size() ] [] );
  344. }
  345. ld = (int[][]) ldl.toArray ( new int [ ldl.size() ] [] );
  346. }
  347. private static int readType ( String line, BufferedReader b, List lines ) throws IOException {
  348. lines.add ( line );
  349. return 0;
  350. }
  351. private static int readLevels ( String line, BufferedReader b, List lines ) throws IOException {
  352. boolean done = false;
  353. int n = 0;
  354. lines.add ( line );
  355. while ( ! done ) {
  356. switch ( testPrefix ( b, PFX_LEVELS ) ) {
  357. case 0: // within current levels
  358. if ( ( line = b.readLine() ) != null ) {
  359. n++;
  360. if ( ( line.length() > 0 ) && ! line.startsWith("#") ) {
  361. lines.add ( line );
  362. }
  363. } else {
  364. done = true;
  365. }
  366. break;
  367. case 1: // end of current levels
  368. case -1: // eof
  369. default:
  370. done = true;
  371. break;
  372. }
  373. }
  374. return n;
  375. }
  376. private static int testPrefix ( BufferedReader b, String pfx ) throws IOException {
  377. int rv = 0;
  378. int pfxLen = pfx.length();
  379. b.mark ( pfxLen );
  380. for ( int i = 0, n = pfxLen; i < n; i++ ) {
  381. int c = b.read();
  382. if ( c < 0 ) {
  383. rv = -1;
  384. break;
  385. } else if ( c != pfx.charAt ( i ) ) {
  386. rv = 0;
  387. break;
  388. } else {
  389. rv = 1;
  390. }
  391. }
  392. b.reset();
  393. return rv;
  394. }
  395. private static int[] parseType ( List lines ) {
  396. if ( ( lines != null ) && ( lines.size() >= 1 ) ) {
  397. String line = (String) lines.get(0);
  398. if ( line.startsWith(PFX_TYPE) ) {
  399. // @Type: BIDI_CLASS ':' LWSP CHARACTER_CLASS
  400. String[] sa = line.split ( ":" );
  401. if ( sa.length == 3 ) {
  402. String bcs = sa[1].trim();
  403. String crs = sa[2].trim();
  404. int bc = parseBidiClass ( bcs );
  405. List rl = parseCharacterRanges ( crs );
  406. return createTypeData ( bc, rl );
  407. }
  408. }
  409. }
  410. return null;
  411. }
  412. private static int[] createTypeData ( int bc, List ranges ) {
  413. int[] data = new int [ 1 + ( 2 * ranges.size() ) ];
  414. int k = 0;
  415. data [ k++ ] = bc;
  416. for ( Iterator it = ranges.iterator(); it.hasNext(); ) {
  417. int[] r = (int[]) it.next();
  418. data [ k++ ] = r [ 0 ];
  419. data [ k++ ] = r [ 1 ];
  420. }
  421. return data;
  422. }
  423. private static int parseBidiClass ( String bidiClass ) {
  424. int bc = 0;
  425. if ( "L".equals ( bidiClass ) ) {
  426. bc = BidiConstants.L;
  427. } else if ( "LRE".equals ( bidiClass ) ) {
  428. bc = BidiConstants.LRE;
  429. } else if ( "LRO".equals ( bidiClass ) ) {
  430. bc = BidiConstants.LRO;
  431. } else if ( "R".equals ( bidiClass ) ) {
  432. bc = BidiConstants.R;
  433. } else if ( "AL".equals ( bidiClass ) ) {
  434. bc = BidiConstants.AL;
  435. } else if ( "RLE".equals ( bidiClass ) ) {
  436. bc = BidiConstants.RLE;
  437. } else if ( "RLO".equals ( bidiClass ) ) {
  438. bc = BidiConstants.RLO;
  439. } else if ( "PDF".equals ( bidiClass ) ) {
  440. bc = BidiConstants.PDF;
  441. } else if ( "EN".equals ( bidiClass ) ) {
  442. bc = BidiConstants.EN;
  443. } else if ( "ES".equals ( bidiClass ) ) {
  444. bc = BidiConstants.ES;
  445. } else if ( "ET".equals ( bidiClass ) ) {
  446. bc = BidiConstants.ET;
  447. } else if ( "AN".equals ( bidiClass ) ) {
  448. bc = BidiConstants.AN;
  449. } else if ( "CS".equals ( bidiClass ) ) {
  450. bc = BidiConstants.CS;
  451. } else if ( "NSM".equals ( bidiClass ) ) {
  452. bc = BidiConstants.NSM;
  453. } else if ( "BN".equals ( bidiClass ) ) {
  454. bc = BidiConstants.BN;
  455. } else if ( "B".equals ( bidiClass ) ) {
  456. bc = BidiConstants.B;
  457. } else if ( "S".equals ( bidiClass ) ) {
  458. bc = BidiConstants.S;
  459. } else if ( "WS".equals ( bidiClass ) ) {
  460. bc = BidiConstants.WS;
  461. } else if ( "ON".equals ( bidiClass ) ) {
  462. bc = BidiConstants.ON;
  463. } else {
  464. throw new IllegalArgumentException ( "unknown bidi class: " + bidiClass );
  465. }
  466. return bc;
  467. }
  468. private static int parseBidiClassAny ( String bidiClass ) {
  469. try {
  470. return parseBidiClass ( bidiClass );
  471. } catch ( IllegalArgumentException e ) {
  472. return -1;
  473. }
  474. }
  475. private static List parseCharacterRanges ( String charRanges ) {
  476. List ranges = new ArrayList();
  477. CharacterIterator ci = new StringCharacterIterator ( charRanges );
  478. // read initial list delimiter
  479. skipSpace ( ci );
  480. if ( ! readStartOfList ( ci ) ) {
  481. badRangeSpec ( "missing initial list delimiter", charRanges );
  482. }
  483. // read negation token if present
  484. boolean negated = false;
  485. skipSpace ( ci );
  486. if ( maybeReadNext ( ci, '^' ) ) {
  487. negated = true;
  488. }
  489. // read item
  490. int[] r;
  491. skipSpace ( ci );
  492. if ( ( r = maybeReadItem ( ci ) ) != null ) {
  493. ranges.add ( r );
  494. if ( verbose ) {
  495. if ( ( ++numTypeRanges % 10 ) == 0 ) {
  496. System.out.print("#");
  497. }
  498. }
  499. } else {
  500. badRangeSpec ( "must contain at least one item", charRanges );
  501. }
  502. // read more items if present
  503. boolean more = true;
  504. while ( more ) {
  505. // read separator if present
  506. String s;
  507. skipSpace ( ci );
  508. if ( ( s = maybeReadSeparator ( ci ) ) != null ) {
  509. if ( ( s.length() != 0 ) && ! s.equals("||") ) {
  510. badRangeSpec ( "invalid item separator \"" + s + "\"", charRanges );
  511. }
  512. }
  513. // read item
  514. skipSpace ( ci );
  515. if ( ( r = maybeReadItem ( ci ) ) != null ) {
  516. ranges.add ( r );
  517. if ( verbose ) {
  518. if ( ( ++numTypeRanges % 10 ) == 0 ) {
  519. System.out.print("#");
  520. }
  521. }
  522. } else {
  523. more = false;
  524. }
  525. }
  526. // read terminating list delimiter
  527. skipSpace ( ci );
  528. if ( ! readEndOfList ( ci ) ) {
  529. badRangeSpec ( "missing terminating list delimiter", charRanges );
  530. }
  531. if ( ! atEnd ( ci ) ) {
  532. badRangeSpec ( "extraneous content prior to end of line", ci );
  533. }
  534. if ( negated ) {
  535. ranges = complementRanges ( ranges );
  536. }
  537. return removeSurrogates ( ranges );
  538. }
  539. private static boolean atEnd ( CharacterIterator ci ) {
  540. return ci.getIndex() >= ci.getEndIndex();
  541. }
  542. private static boolean readStartOfList ( CharacterIterator ci ) {
  543. return maybeReadNext ( ci, '[' );
  544. }
  545. private static void skipSpace ( CharacterIterator ci ) {
  546. while ( ! atEnd ( ci ) ) {
  547. char c = ci.current();
  548. if ( ! Character.isWhitespace ( c ) ) {
  549. break;
  550. } else {
  551. ci.next();
  552. }
  553. }
  554. }
  555. private static boolean maybeReadNext ( CharacterIterator ci, char next ) {
  556. while ( ! atEnd ( ci ) ) {
  557. char c = ci.current();
  558. if ( c == next ) {
  559. ci.next();
  560. return true;
  561. } else {
  562. break;
  563. }
  564. }
  565. return false;
  566. }
  567. private static int[] maybeReadItem ( CharacterIterator ci ) {
  568. // read first code point
  569. int p1 = -1;
  570. skipSpace ( ci );
  571. if ( ( p1 = maybeReadCodePoint ( ci ) ) < 0 ) {
  572. return null;
  573. }
  574. // read second code point if present
  575. int p2 = -1;
  576. skipSpace ( ci );
  577. if ( maybeReadNext ( ci, '-' ) ) {
  578. skipSpace ( ci );
  579. if ( ( p2 = maybeReadCodePoint ( ci ) ) < 0 ) {
  580. badRangeSpec ( "incomplete item range, requires second item", ci );
  581. }
  582. }
  583. if ( p2 < 0 ) {
  584. return new int[] { p1, p1 + 1 }; // convert to half open interval [ P1, P1+1 )
  585. } else if ( p1 <= p2 ) {
  586. return new int[] { p1, p2 + 1 }; // convert to half open interval [ P1, P2+2 )
  587. } else {
  588. badRangeSpec ( "invalid item range, second item must be greater than or equal to first item", ci );
  589. return null;
  590. }
  591. }
  592. private static int maybeReadCodePoint ( CharacterIterator ci ) {
  593. if ( maybeReadNext ( ci, '\\' ) ) {
  594. if ( maybeReadNext ( ci, 'u' ) ) {
  595. String s = maybeReadHexDigits ( ci, 4 );
  596. if ( s != null ) {
  597. return Integer.parseInt ( s, 16 );
  598. } else {
  599. badRangeSpec ( "incomplete escaped code point, requires 4 hex digits", ci );
  600. }
  601. } else if ( maybeReadNext ( ci, 'U' ) ) {
  602. String s = maybeReadHexDigits ( ci, 8 );
  603. if ( s != null ) {
  604. return Integer.parseInt ( s, 16 );
  605. } else {
  606. badRangeSpec ( "incomplete escaped code point, requires 8 hex digits", ci );
  607. }
  608. } else {
  609. char c = ci.current();
  610. if ( c == CharacterIterator.DONE ) {
  611. badRangeSpec ( "incomplete escaped code point", ci );
  612. } else {
  613. ci.next();
  614. return (int) c;
  615. }
  616. }
  617. } else {
  618. char c = ci.current();
  619. if ( ( c == CharacterIterator.DONE ) || ( c == ']' ) ) {
  620. return -1;
  621. } else {
  622. ci.next();
  623. return (int) c;
  624. }
  625. }
  626. return -1;
  627. }
  628. private static String maybeReadHexDigits ( CharacterIterator ci, int numDigits ) {
  629. StringBuffer sb = new StringBuffer();
  630. while ( ( numDigits < 0 ) || ( sb.length() < numDigits ) ) {
  631. char c = ci.current();
  632. if ( c != CharacterIterator.DONE ) {
  633. if ( isHexDigit ( c ) ) {
  634. ci.next();
  635. sb.append ( c );
  636. } else {
  637. break;
  638. }
  639. } else {
  640. break;
  641. }
  642. }
  643. if ( ( ( numDigits < 0 ) && ( sb.length() > 0 ) ) || ( sb.length() == numDigits ) ) {
  644. return sb.toString();
  645. } else {
  646. return null;
  647. }
  648. }
  649. private static boolean isHexDigit ( char c ) {
  650. return ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'a' ) && ( c <= 'f' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) );
  651. }
  652. private static String maybeReadSeparator ( CharacterIterator ci ) {
  653. if ( maybeReadNext ( ci, '|' ) ) {
  654. if ( maybeReadNext ( ci, '|' ) ) {
  655. return "||";
  656. } else {
  657. return "|";
  658. }
  659. } else {
  660. return "";
  661. }
  662. }
  663. private static boolean readEndOfList ( CharacterIterator ci ) {
  664. return maybeReadNext ( ci, ']' );
  665. }
  666. private static List complementRanges ( List ranges ) {
  667. Map/*<Integer,Integer>*/ rm = new TreeMap/*<Integer,Integer>*/();
  668. for ( Iterator it = ranges.iterator(); it.hasNext(); ) {
  669. int[] r = (int[]) it.next();
  670. rm.put ( Integer.valueOf ( r[0] ), Integer.valueOf ( r[1] ) );
  671. }
  672. // add complement ranges save last
  673. int s;
  674. int e;
  675. int cs = 0;
  676. List compRanges = new ArrayList ( rm.size() + 1 );
  677. for ( Iterator it = rm.entrySet().iterator(); it.hasNext(); ) {
  678. Map.Entry/*<Integer,Integer>*/ me = (Map.Entry/*<Integer,Integer>*/) it.next();
  679. s = ( (Integer) me.getKey() ).intValue();
  680. e = ( (Integer) me.getValue() ).intValue();
  681. if ( s > cs ) {
  682. compRanges.add ( new int[] { cs, s } );
  683. }
  684. cs = e;
  685. }
  686. // add trailing complement range
  687. if ( cs < 0x110000 ) {
  688. compRanges.add ( new int[] { cs, 0x110000 } );
  689. }
  690. return compRanges;
  691. }
  692. private static final int[] SURROGATES = new int[] { 0xD800, 0xE000 };
  693. private static List removeSurrogates ( List ranges ) {
  694. List rsl = new ArrayList ( ranges.size() );
  695. for ( Iterator it = ranges.iterator(); it.hasNext(); ) {
  696. int[] r = (int[]) it.next();
  697. if ( intersectsRange ( r, SURROGATES ) ) {
  698. rsl.addAll ( removeRange ( r, SURROGATES ) );
  699. } else {
  700. rsl.add ( r );
  701. }
  702. }
  703. return rsl;
  704. }
  705. /**
  706. * Determine if range r2 intersects with range r1.
  707. */
  708. private static boolean intersectsRange ( int[] r1, int[] r2 ) {
  709. if ( r1[1] <= r2[0] ) { // r1 precedes r2 or abuts r2 on right
  710. return false;
  711. } else if ( r1[0] >= r2[1] ) { // r2 precedes r1 or abuts r1 on left
  712. return false;
  713. } else if ( ( r1[0] < r2[0] ) && ( r1[1] > r2[1] ) ) { // r1 encloses r2
  714. return true;
  715. } else if ( r1[0] < r2[0] ) { // r1 precedes and overlaps r2
  716. return true;
  717. } else if ( r2[1] < r1[1] ) { // r2 precedes and overlaps r1
  718. return true;
  719. } else { // r2 encloses r1
  720. return true;
  721. }
  722. }
  723. /**
  724. * Remove range r2 from range r1, leaving zero, one, or two
  725. * remaining ranges.
  726. */
  727. private static List removeRange ( int[] r1, int[] r2 ) {
  728. List rl = new ArrayList();
  729. if ( r1[1] <= r2[0] ) { // r1 precedes r2 or abuts r2 on right
  730. rl.add ( r1 );
  731. } else if ( r1[0] >= r2[1] ) { // r2 precedes r1 or abuts r1 on left
  732. rl.add ( r1 );
  733. } else if ( ( r1[0] < r2[0] ) && ( r1[1] > r2[1] ) ) { // r1 encloses r2
  734. rl.add ( new int[] { r1[0], r2[0] } );
  735. rl.add ( new int[] { r2[1], r1[1] } );
  736. } else if ( r1[0] < r2[0] ) { // r1 precedes and overlaps r2
  737. rl.add ( new int[] { r1[0], r2[0] } );
  738. } else if ( r2[1] < r1[1] ) { // r2 precedes and overlaps r1
  739. rl.add ( new int[] { r2[1], r1[1] } );
  740. }
  741. return rl;
  742. }
  743. private static void badRangeSpec ( String reason, String charRanges ) throws IllegalArgumentException {
  744. if ( verbose ) {
  745. System.out.println();
  746. }
  747. throw new IllegalArgumentException ( "bad range specification: " + reason + ": \"" + charRanges + "\"" );
  748. }
  749. private static void badRangeSpec ( String reason, CharacterIterator ci ) throws IllegalArgumentException {
  750. if ( verbose ) {
  751. System.out.println();
  752. }
  753. throw new IllegalArgumentException ( "bad range specification: " + reason + ": starting at \"" + remainder ( ci ) + "\"" );
  754. }
  755. private static String remainder ( CharacterIterator ci ) {
  756. StringBuffer sb = new StringBuffer();
  757. for ( char c; ( c = ci.current() ) != CharacterIterator.DONE; ) {
  758. ci.next();
  759. sb.append ( c );
  760. }
  761. return sb.toString();
  762. }
  763. /**
  764. * Parse levels segment, consisting of multiple lines as follows:
  765. *
  766. * LEVEL_SPEC \n
  767. * REORDER_SPEC \n
  768. * ( TEST_SPEC \n )+
  769. */
  770. private static int[] parseLevels ( List lines ) {
  771. int[] la = null; // levels array
  772. int[] ra = null; // reorder array
  773. List tal = new ArrayList();
  774. if ( ( lines != null ) && ( lines.size() >= 3 ) ) {
  775. for ( Iterator it = lines.iterator(); it.hasNext(); ) {
  776. String line = (String) it.next();
  777. if ( line.startsWith(PFX_LEVELS) ) {
  778. if ( la == null ) {
  779. la = parseLevelSpec ( line );
  780. if ( verbose ) {
  781. if ( ( ++numLevelSpecs % 10 ) == 0 ) {
  782. System.out.print("&");
  783. }
  784. }
  785. } else {
  786. throw new IllegalArgumentException ( "redundant levels array: \"" + line + "\"" );
  787. }
  788. } else if ( line.startsWith(PFX_REORDER) ) {
  789. if ( la == null ) {
  790. throw new IllegalArgumentException ( "missing levels array before: \"" + line + "\"" );
  791. } else if ( ra == null ) {
  792. ra = parseReorderSpec ( line, la );
  793. } else {
  794. throw new IllegalArgumentException ( "redundant reorder array: \"" + line + "\"" );
  795. }
  796. } else if ( ( la != null ) && ( ra != null ) ) {
  797. int[] ta = parseTestSpec ( line, la );
  798. if ( ta != null ) {
  799. if ( verbose ) {
  800. if ( ( ++numTestSpecs % 100 ) == 0 ) {
  801. System.out.print("!");
  802. }
  803. }
  804. tal.add ( ta );
  805. }
  806. } else if ( la == null ) {
  807. throw new IllegalArgumentException ( "missing levels array before: \"" + line + "\"" );
  808. } else if ( ra == null ) {
  809. throw new IllegalArgumentException ( "missing reorder array before: \"" + line + "\"" );
  810. }
  811. }
  812. }
  813. if ( ( la != null ) && ( ra != null ) ) {
  814. return createLevelData ( la, ra, tal );
  815. } else {
  816. return null;
  817. }
  818. }
  819. private static int[] createLevelData ( int[] la, int[] ra, List tal ) {
  820. int nl = la.length;
  821. int[] data = new int [ 1 + nl * 2 + ( ( nl + 1 ) * tal.size() ) ];
  822. int k = 0;
  823. data [ k++ ] = nl;
  824. for ( int i = 0, n = nl; i < n; i++ ) {
  825. data [ k++ ] = la [ i ];
  826. }
  827. int nr = ra.length;
  828. for ( int i = 0, n = nr; i < n; i++ ) {
  829. data [ k++ ] = ra [ i ];
  830. }
  831. for ( Iterator it = tal.iterator(); it.hasNext(); ) {
  832. int[] ta = (int[]) it.next();
  833. if ( ta == null ) {
  834. throw new IllegalStateException ( "null test array" );
  835. } else if ( ta.length == ( nl + 1 ) ) {
  836. for ( int i = 0, n = ta.length; i < n; i++ ) {
  837. data [ k++ ] = ta [ i ];
  838. }
  839. } else {
  840. throw new IllegalStateException ( "test array length error, expected " + ( nl + 1 ) + " entries, got " + ta.length + " entries" );
  841. }
  842. }
  843. assert k == data.length;
  844. return data;
  845. }
  846. /**
  847. * Parse level specification, which follows the following syntax:
  848. *
  849. * @Levels: ( LWSP ( NUMBER | 'x' ) )+
  850. */
  851. private static int[] parseLevelSpec ( String line ) {
  852. CharacterIterator ci = new StringCharacterIterator ( line );
  853. List ll = new ArrayList();
  854. // read prefix
  855. skipSpace ( ci );
  856. if ( ! maybeReadToken ( ci, PFX_LEVELS ) ) {
  857. badLevelSpec ( "missing prefix \"" + PFX_LEVELS + "\"", ci );
  858. }
  859. // read level values
  860. boolean more = true;
  861. while ( more ) {
  862. Integer l;
  863. skipSpace ( ci );
  864. if ( ( l = maybeReadInteger ( ci ) ) != null ) {
  865. ll.add ( l );
  866. } else if ( maybeReadToken ( ci, "x" ) ) {
  867. ll.add ( Integer.valueOf ( -1 ) );
  868. } else {
  869. more = false;
  870. }
  871. }
  872. // read to end of line
  873. skipSpace ( ci );
  874. if ( ! atEnd ( ci ) ) {
  875. badLevelSpec ( "extraneous content prior to end of line", ci );
  876. }
  877. if ( ll.size() == 0 ) {
  878. badLevelSpec ( "must have at least one level value", ci );
  879. }
  880. return createLevelsArray ( ll );
  881. }
  882. private static Integer maybeReadInteger ( CharacterIterator ci ) {
  883. // read optional minus sign if present
  884. boolean negative;
  885. if ( maybeReadNext ( ci, '-' ) ) {
  886. negative = true;
  887. } else {
  888. negative = false;
  889. }
  890. // read digits
  891. StringBuffer sb = new StringBuffer();
  892. while ( true ) {
  893. char c = ci.current();
  894. if ( ( c != CharacterIterator.DONE ) && isDigit ( c ) ) {
  895. ci.next();
  896. sb.append ( c );
  897. } else {
  898. break;
  899. }
  900. }
  901. if ( sb.length() == 0 ) {
  902. return null;
  903. } else {
  904. int value = Integer.parseInt ( sb.toString() );
  905. if ( negative ) {
  906. value = -value;
  907. }
  908. return Integer.valueOf ( value );
  909. }
  910. }
  911. private static boolean isDigit ( char c ) {
  912. return ( ( c >= '0' ) && ( c <= '9' ) );
  913. }
  914. private static boolean maybeReadToken ( CharacterIterator ci, String s ) {
  915. int startIndex = ci.getIndex();
  916. for ( int i = 0, n = s.length(); i < n; i++ ) {
  917. char c = s.charAt ( i );
  918. if ( ci.current() == c ) {
  919. ci.next();
  920. } else {
  921. ci.setIndex ( startIndex );
  922. return false;
  923. }
  924. }
  925. return true;
  926. }
  927. private static void badLevelSpec ( String reason, CharacterIterator ci ) throws IllegalArgumentException {
  928. if ( verbose ) {
  929. System.out.println();
  930. }
  931. throw new IllegalArgumentException ( "bad level specification: " + reason + ": starting at \"" + remainder ( ci ) + "\"" );
  932. }
  933. private static int[] createLevelsArray ( List levels ) {
  934. int[] la = new int [ levels.size() ];
  935. int k = 0;
  936. for ( Iterator it = levels.iterator(); it.hasNext(); ) {
  937. la [ k++ ] = ( (Integer) it.next() ).intValue();
  938. }
  939. return la;
  940. }
  941. /**
  942. * Parse reorder specification, which follows the following syntax:
  943. *
  944. * @Reorder: ( LWSP NUMBER )*
  945. */
  946. private static int[] parseReorderSpec ( String line, int[] levels ) {
  947. CharacterIterator ci = new StringCharacterIterator ( line );
  948. List rl = new ArrayList();
  949. // read prefix
  950. skipSpace ( ci );
  951. if ( ! maybeReadToken ( ci, PFX_REORDER ) ) {
  952. badReorderSpec ( "missing prefix \"" + PFX_REORDER + "\"", ci );
  953. }
  954. // read reorder values
  955. boolean more = true;
  956. while ( more ) {
  957. skipSpace ( ci );
  958. Integer l;
  959. if ( ( l = maybeReadInteger ( ci ) ) != null ) {
  960. rl.add ( l );
  961. } else {
  962. more = false;
  963. }
  964. }
  965. // read to end of line
  966. skipSpace ( ci );
  967. if ( ! atEnd ( ci ) ) {
  968. badReorderSpec ( "extraneous content prior to end of line", ci );
  969. }
  970. return createReorderArray ( rl, levels );
  971. }
  972. private static void badReorderSpec ( String reason, CharacterIterator ci ) throws IllegalArgumentException {
  973. if ( verbose ) {
  974. System.out.println();
  975. }
  976. throw new IllegalArgumentException ( "bad reorder specification: " + reason + ": starting at \"" + remainder ( ci ) + "\"" );
  977. }
  978. private static int[] createReorderArray ( List reorders, int[] levels ) {
  979. int nr = reorders.size();
  980. int nl = levels.length;
  981. if ( nr <= nl ) {
  982. int[] ra = new int [ nl ];
  983. Iterator it = reorders.iterator();
  984. for ( int i = 0, n = nl; i < n; i++ ) {
  985. int r = -1;
  986. if ( levels [ i ] >= 0 ) {
  987. if ( it.hasNext() ) {
  988. r = ( (Integer) it.next() ).intValue();
  989. }
  990. }
  991. ra [ i ] = r;
  992. }
  993. return ra;
  994. } else {
  995. throw new IllegalArgumentException ( "excessive number of reorder array entries, expected no more than " + nl + ", but got " + nr + " entries" );
  996. }
  997. }
  998. /**
  999. * Parse test specification, which follows the following syntax:
  1000. *
  1001. * BIDI_CLASS ( LWSP BIDI_CLASS )+ ';' LWSP NUMBER
  1002. */
  1003. private static int[] parseTestSpec ( String line, int[] levels ) {
  1004. CharacterIterator ci = new StringCharacterIterator ( line );
  1005. List cl = new ArrayList();
  1006. // read bidi class identifier sequence
  1007. while ( ! atEnd ( ci ) && ! maybeReadNext ( ci, ';' ) ) {
  1008. skipSpace ( ci );
  1009. int bc;
  1010. if ( ( bc = maybeReadBidiClass ( ci ) ) >= 0 ) {
  1011. cl.add ( Integer.valueOf ( bc ) );
  1012. } else {
  1013. break;
  1014. }
  1015. }
  1016. // read bit set
  1017. skipSpace ( ci );
  1018. String s;
  1019. int bs = 0;
  1020. if ( ( s = maybeReadHexDigits ( ci, -1 ) ) != null ) {
  1021. bs = Integer.parseInt ( s, 16 );
  1022. } else {
  1023. badTestSpec ( "missing bit set", ci );
  1024. }
  1025. // read to end of line
  1026. skipSpace ( ci );
  1027. if ( ! atEnd ( ci ) ) {
  1028. badTestSpec ( "extraneous content prior to end of line", ci );
  1029. }
  1030. return createTestArray ( cl, bs, levels );
  1031. }
  1032. private static String maybeReadIdentifier ( CharacterIterator ci ) {
  1033. // read keyword chars ([A-Z])
  1034. StringBuffer sb = new StringBuffer();
  1035. while ( true ) {
  1036. char c = ci.current();
  1037. if ( c == CharacterIterator.DONE ) {
  1038. break;
  1039. } else if ( sb.length() == 0 ) {
  1040. if ( Character.isUnicodeIdentifierStart ( c ) ) {
  1041. ci.next();
  1042. sb.append ( c );
  1043. } else {
  1044. break;
  1045. }
  1046. } else {
  1047. if ( Character.isUnicodeIdentifierPart ( c ) ) {
  1048. ci.next();
  1049. sb.append ( c );
  1050. } else {
  1051. break;
  1052. }
  1053. }
  1054. }
  1055. if ( sb.length() == 0 ) {
  1056. return null;
  1057. } else {
  1058. return sb.toString();
  1059. }
  1060. }
  1061. private static int maybeReadBidiClass ( CharacterIterator ci ) {
  1062. int bc = -1;
  1063. int i = ci.getIndex();
  1064. String s;
  1065. if ( ( s = maybeReadIdentifier ( ci ) ) != null ) {
  1066. try {
  1067. bc = parseBidiClass ( s );
  1068. } catch ( IllegalArgumentException e ) {
  1069. throw e;
  1070. }
  1071. }
  1072. if ( bc < 0 ) {
  1073. ci.setIndex ( i );
  1074. }
  1075. return bc;
  1076. }
  1077. private static void badTestSpec ( String reason, CharacterIterator ci ) throws IllegalArgumentException {
  1078. if ( verbose ) {
  1079. System.out.println();
  1080. }
  1081. throw new IllegalArgumentException ( "bad test specification: " + reason + ": starting at \"" + remainder ( ci ) + "\"" );
  1082. }
  1083. private static int[] createTestArray ( List classes, int bitset, int[] levels ) {
  1084. int nc = classes.size();
  1085. if ( nc <= levels.length ) {
  1086. int[] ta = new int [ 1 + nc ];
  1087. int k = 0;
  1088. ta [ k++ ] = bitset;
  1089. for ( Iterator it = classes.iterator(); it.hasNext(); ) {
  1090. ta [ k++ ] = ( (Integer) it.next() ).intValue();
  1091. }
  1092. return ta;
  1093. } else {
  1094. throw new IllegalArgumentException ( "excessive number of test array entries, expected no more than " + levels.length + ", but got " + nc + " entries" );
  1095. }
  1096. }
  1097. /**
  1098. * Dump data arrays to output and resource files.
  1099. * @param out - bidi test data java class file print writer
  1100. * @param outFileName - (full path) name of bidi test data java class file
  1101. */
  1102. private static void dumpData ( PrintWriter out, String outFileName ) throws IOException {
  1103. File f = new File ( outFileName );
  1104. File p = f.getParentFile();
  1105. if ( td != null ) {
  1106. String pfxTD = "TD";
  1107. dumpResourcesDescriptor ( out, pfxTD, td.length );
  1108. dumpResourcesData ( p, f.getName(), pfxTD, td );
  1109. }
  1110. if ( ld != null ) {
  1111. String pfxTD = "LD";
  1112. dumpResourcesDescriptor ( out, pfxTD, ld.length );
  1113. dumpResourcesData ( p, f.getName(), pfxTD, ld );
  1114. }
  1115. }
  1116. private static void dumpResourcesDescriptor ( PrintWriter out, String prefix, int numResources ) {
  1117. out.println ( " public static final String " + prefix + "_PFX = \"" + prefix + "\";" );
  1118. out.println ( " public static final int " + prefix + "_CNT = " + numResources + ";" );
  1119. out.println("");
  1120. }
  1121. private static void dumpResourcesData ( File btcDir, String btcName, String prefix, int[][] data ) throws IOException {
  1122. String btdName = extractDataFileName ( btcName );
  1123. for ( int i = 0, n = data.length; i < n; i++ ) {
  1124. File f = new File ( btcDir, btdName + "$" + prefix + i + ".ser" );
  1125. ObjectOutputStream os = new ObjectOutputStream ( new FileOutputStream ( f ) );
  1126. os.writeObject ( data[i] );
  1127. os.close();
  1128. }
  1129. }
  1130. private static final String JAVA_EXT = ".java";
  1131. private static String extractDataFileName ( String btcName ) {
  1132. if ( btcName.endsWith ( JAVA_EXT ) ) {
  1133. return btcName.substring ( 0, btcName.length() - JAVA_EXT.length() );
  1134. } else {
  1135. return btcName;
  1136. }
  1137. }
  1138. /**
  1139. * Main entry point for generator.
  1140. * @param args array of command line arguments
  1141. */
  1142. public static void main(String[] args) {
  1143. String bidiFileName = "http://www.unicode.org/Public/UNIDATA/BidiTest.txt";
  1144. String ucdFileName = "http://www.unicode.org/Public/UNIDATA/BidiTest.txt";
  1145. String outFileName = "BidiTestData.java";
  1146. boolean ok = true;
  1147. for (int i = 0; ok && ( i < args.length ); i++) {
  1148. String opt = args[i];
  1149. if ("-b".equals(opt)) {
  1150. if ( ( i + 1 ) <= args.length ) {
  1151. bidiFileName = args[++i];
  1152. } else {
  1153. ok = false;
  1154. }
  1155. } else if ("-d".equals(opt)) {
  1156. if ( ( i + 1 ) <= args.length ) {
  1157. ucdFileName = args[++i];
  1158. } else {
  1159. ok = false;
  1160. }
  1161. } else if ("-i".equals(opt)) {
  1162. ignoreDeprecatedTypeData = true;
  1163. } else if ("-o".equals(opt)) {
  1164. if ( ( i + 1 ) <= args.length ) {
  1165. outFileName = args[++i];
  1166. } else {
  1167. ok = false;
  1168. }
  1169. } else if ("-v".equals(opt)) {
  1170. verbose = true;
  1171. } else {
  1172. ok = false;
  1173. }
  1174. }
  1175. if ( ! ok ) {
  1176. System.out.println("Usage: GenerateBidiTestData [-v] [-i] [-d <ucdFile>] [-b <bidiFile>] [-o <outputFile>]");
  1177. System.out.println(" defaults:");
  1178. if ( ignoreDeprecatedTypeData ) {
  1179. System.out.println(" <ucdFile> : " + ucdFileName);
  1180. }
  1181. System.out.println(" <bidiFile> : " + bidiFileName);
  1182. System.out.println(" <outputFile> : " + outFileName);
  1183. } else {
  1184. try {
  1185. convertBidiTestData(ucdFileName, bidiFileName, outFileName);
  1186. System.out.println("Generated " + outFileName + " from");
  1187. if ( ignoreDeprecatedTypeData ) {
  1188. System.out.println(" <ucdFile> : " + ucdFileName);
  1189. }
  1190. System.out.println(" <bidiFile> : " + bidiFileName);
  1191. } catch (Exception e) {
  1192. System.out.println("An unexpected error occured at line: " + lineNumber );
  1193. e.printStackTrace();
  1194. }
  1195. }
  1196. }
  1197. }