1. /*
  2. * @(#)Utility.java 1.10 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. /*
  8. * @(#)Utility.java 1.10 01/11/29
  9. *
  10. * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
  11. * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
  12. *
  13. * Portions copyright (c) 1997, 1998 Sun Microsystems, Inc.
  14. * All Rights Reserved.
  15. *
  16. * The original version of this source code and documentation
  17. * is copyrighted and owned by Taligent, Inc., a wholly-owned
  18. * subsidiary of IBM. These materials are provided under terms
  19. * of a License Agreement between Taligent and Sun. This technology
  20. * is protected by multiple US and International patents.
  21. *
  22. * This notice and attribution to Taligent may not be removed.
  23. * Taligent is a registered trademark of Taligent, Inc.
  24. *
  25. * Permission to use, copy, modify, and distribute this software
  26. * and its documentation for NON-COMMERCIAL purposes and without
  27. * fee is hereby granted provided that this copyright notice
  28. * appears in all copies. Please refer to the file "copyright.html"
  29. * for further important copyright and licensing information.
  30. *
  31. * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  32. * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  33. * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  34. * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  35. * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  36. * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  37. *
  38. */
  39. package java.text;
  40. final class Utility {
  41. /**
  42. * Convenience utility to compare two Object[]s.
  43. * Ought to be in System
  44. */
  45. final static boolean arrayEquals(Object[] source, Object target) {
  46. if (source == null) return (target == null);
  47. if (!(target instanceof Object[])) return false;
  48. Object[] targ = (Object[]) target;
  49. return (source.length == targ.length
  50. && arrayRegionMatches(source, 0, targ, 0, source.length));
  51. }
  52. /**
  53. * Convenience utility to compare two int[]s
  54. * Ought to be in System
  55. */
  56. final static boolean arrayEquals(int[] source, Object target) {
  57. if (source == null) return (target == null);
  58. if (!(target instanceof int[])) return false;
  59. int[] targ = (int[]) target;
  60. return (source.length == targ.length
  61. && arrayRegionMatches(source, 0, targ, 0, source.length));
  62. }
  63. /**
  64. * Convenience utility to compare two double[]s
  65. * Ought to be in System
  66. */
  67. final static boolean arrayEquals(double[] source, Object target) {
  68. if (source == null) return (target == null);
  69. if (!(target instanceof double[])) return false;
  70. double[] targ = (double[]) target;
  71. return (source.length == targ.length
  72. && arrayRegionMatches(source, 0, targ, 0, source.length));
  73. }
  74. /**
  75. * Convenience utility to compare two Object[]s
  76. * Ought to be in System
  77. */
  78. final static boolean arrayEquals(Object source, Object target) {
  79. if (source == null) return (target == null);
  80. // for some reason, the correct arrayEquals is not being called
  81. // so do it by hand for now.
  82. if (source instanceof Object[])
  83. return(arrayEquals((Object[]) source,target));
  84. if (source instanceof int[])
  85. return(arrayEquals((int[]) source,target));
  86. if (source instanceof double[])
  87. return(arrayEquals((int[]) source,target));
  88. return source.equals(target);
  89. }
  90. /**
  91. * Convenience utility to compare two Object[]s
  92. * Ought to be in System.
  93. * @param len the length to compare.
  94. * The start indices and start+len must be valid.
  95. */
  96. final static boolean arrayRegionMatches(Object[] source, int sourceStart,
  97. Object[] target, int targetStart,
  98. int len)
  99. {
  100. int sourceEnd = sourceStart + len;
  101. int delta = targetStart - sourceStart;
  102. for (int i = sourceStart; i < sourceEnd; i++) {
  103. if (!arrayEquals(source[i],target[i + delta]))
  104. return false;
  105. }
  106. return true;
  107. }
  108. /**
  109. * Convenience utility to compare two int[]s.
  110. * @param len the length to compare.
  111. * The start indices and start+len must be valid.
  112. * Ought to be in System
  113. */
  114. final static boolean arrayRegionMatches(int[] source, int sourceStart,
  115. int[] target, int targetStart,
  116. int len)
  117. {
  118. int sourceEnd = sourceStart + len;
  119. int delta = targetStart - sourceStart;
  120. for (int i = sourceStart; i < sourceEnd; i++) {
  121. if (source[i] != target[i + delta])
  122. return false;
  123. }
  124. return true;
  125. }
  126. /**
  127. * Convenience utility to compare two arrays of doubles.
  128. * @param len the length to compare.
  129. * The start indices and start+len must be valid.
  130. * Ought to be in System
  131. */
  132. final static boolean arrayRegionMatches(double[] source, int sourceStart,
  133. double[] target, int targetStart,
  134. int len)
  135. {
  136. int sourceEnd = sourceStart + len;
  137. int delta = targetStart - sourceStart;
  138. for (int i = sourceStart; i < sourceEnd; i++) {
  139. if (source[i] != target[i + delta])
  140. return false;
  141. }
  142. return true;
  143. }
  144. /**
  145. * Convenience utility. Does null checks on objects, then calls equals.
  146. */
  147. final static boolean objectEquals(Object source, Object target) {
  148. if (source == null)
  149. return (target == null);
  150. else
  151. return source.equals(target);
  152. }
  153. /**
  154. * The ESCAPE character is used during run-length encoding. It signals
  155. * a run of identical chars.
  156. */
  157. static final char ESCAPE = '\uA5A5';
  158. /**
  159. * The ESCAPE_BYTE character is used during run-length encoding. It signals
  160. * a run of identical bytes.
  161. */
  162. static final byte ESCAPE_BYTE = (byte)0xA5;
  163. /**
  164. * Construct a string representing a short array. Use run-length encoding.
  165. * A character represents itself, unless it is the ESCAPE character. Then
  166. * the following notations are possible:
  167. * ESCAPE ESCAPE ESCAPE literal
  168. * ESCAPE n c n instances of character c
  169. * Since an encoded run occupies 3 characters, we only encode runs of 4 or
  170. * more characters. Thus we have n > 0 and n != ESCAPE and n <= 0xFFFF.
  171. * If we encounter a run where n == ESCAPE, we represent this as:
  172. * c ESCAPE n-1 c
  173. * The ESCAPE value is chosen so as not to collide with commonly
  174. * seen values.
  175. */
  176. static final String arrayToRLEString(short[] a) {
  177. StringBuffer buffer = new StringBuffer();
  178. // for (int i=0; i<a.length; ++i) buffer.append((char) a[i]);
  179. buffer.append((char) (a.length >> 16));
  180. buffer.append((char) a.length);
  181. short runValue = a[0];
  182. int runLength = 1;
  183. for (int i=1; i<a.length; ++i) {
  184. short s = a[i];
  185. if (s == runValue && runLength < 0xFFFF) ++runLength;
  186. else {
  187. encodeRun(buffer, runValue, runLength);
  188. runValue = s;
  189. runLength = 1;
  190. }
  191. }
  192. encodeRun(buffer, runValue, runLength);
  193. return buffer.toString();
  194. }
  195. /**
  196. * Construct a string representing a byte array. Use run-length encoding.
  197. * Two bytes are packed into a single char, with a single extra zero byte at
  198. * the end if needed. A byte represents itself, unless it is the
  199. * ESCAPE_BYTE. Then the following notations are possible:
  200. * ESCAPE_BYTE ESCAPE_BYTE ESCAPE_BYTE literal
  201. * ESCAPE_BYTE n b n instances of byte b
  202. * Since an encoded run occupies 3 bytes, we only encode runs of 4 or
  203. * more bytes. Thus we have n > 0 and n != ESCAPE_BYTE and n <= 0xFF.
  204. * If we encounter a run where n == ESCAPE_BYTE, we represent this as:
  205. * b ESCAPE_BYTE n-1 b
  206. * The ESCAPE_BYTE value is chosen so as not to collide with commonly
  207. * seen values.
  208. */
  209. static final String arrayToRLEString(byte[] a) {
  210. StringBuffer buffer = new StringBuffer();
  211. buffer.append((char) (a.length >> 16));
  212. buffer.append((char) a.length);
  213. byte runValue = a[0];
  214. int runLength = 1;
  215. byte[] state = new byte[2];
  216. for (int i=1; i<a.length; ++i) {
  217. byte b = a[i];
  218. if (b == runValue && runLength < 0xFF) ++runLength;
  219. else {
  220. encodeRun(buffer, runValue, runLength, state);
  221. runValue = b;
  222. runLength = 1;
  223. }
  224. }
  225. encodeRun(buffer, runValue, runLength, state);
  226. // We must save the final byte, if there is one, by padding
  227. // an extra zero.
  228. if (state[0] != 0) appendEncodedByte(buffer, (byte)0, state);
  229. return buffer.toString();
  230. }
  231. /**
  232. * Encode a run, possibly a degenerate run (of < 4 values).
  233. * @param length The length of the run; must be > 0 && <= 0xFFFF.
  234. */
  235. private static final void encodeRun(StringBuffer buffer, short value, int length) {
  236. if (length < 4) {
  237. for (int j=0; j<length; ++j) {
  238. if (value == (int) ESCAPE) buffer.append(ESCAPE);
  239. buffer.append((char) value);
  240. }
  241. }
  242. else {
  243. if (length == (int) ESCAPE) {
  244. if (value == (int) ESCAPE) buffer.append(ESCAPE);
  245. buffer.append((char) value);
  246. --length;
  247. }
  248. buffer.append(ESCAPE);
  249. buffer.append((char) length);
  250. buffer.append((char) value); // Don't need to escape this value
  251. }
  252. }
  253. /**
  254. * Encode a run, possibly a degenerate run (of < 4 values).
  255. * @param length The length of the run; must be > 0 && <= 0xFF.
  256. */
  257. private static final void encodeRun(StringBuffer buffer, byte value, int length,
  258. byte[] state) {
  259. if (length < 4) {
  260. for (int j=0; j<length; ++j) {
  261. if (value == ESCAPE_BYTE) appendEncodedByte(buffer, ESCAPE_BYTE, state);
  262. appendEncodedByte(buffer, value, state);
  263. }
  264. }
  265. else {
  266. if (length == ESCAPE_BYTE) {
  267. if (value == ESCAPE_BYTE) appendEncodedByte(buffer, ESCAPE_BYTE, state);
  268. appendEncodedByte(buffer, value, state);
  269. --length;
  270. }
  271. appendEncodedByte(buffer, ESCAPE_BYTE, state);
  272. appendEncodedByte(buffer, (byte)length, state);
  273. appendEncodedByte(buffer, value, state); // Don't need to escape this value
  274. }
  275. }
  276. /**
  277. * Append a byte to the given StringBuffer, packing two bytes into each
  278. * character. The state parameter maintains intermediary data between
  279. * calls.
  280. * @param state A two-element array, with state[0] == 0 if this is the
  281. * first byte of a pair, or state[0] != 0 if this is the second byte
  282. * of a pair, in which case state[1] is the first byte.
  283. */
  284. private static final void appendEncodedByte(StringBuffer buffer, byte value,
  285. byte[] state) {
  286. if (state[0] != 0) {
  287. char c = (char) ((state[1] << 8) | (((int) value) & 0xFF));
  288. buffer.append(c);
  289. state[0] = 0;
  290. }
  291. else {
  292. state[0] = 1;
  293. state[1] = value;
  294. }
  295. }
  296. /**
  297. * Construct an array of shorts from a run-length encoded string.
  298. */
  299. static final short[] RLEStringToShortArray(String s) {
  300. int length = (((int) s.charAt(0)) << 16) | ((int) s.charAt(1));
  301. short[] array = new short[length];
  302. int ai = 0;
  303. for (int i=2; i<s.length(); ++i) {
  304. char c = s.charAt(i);
  305. if (c == ESCAPE) {
  306. c = s.charAt(++i);
  307. if (c == ESCAPE) array[ai++] = (short) c;
  308. else {
  309. int runLength = (int) c;
  310. short runValue = (short) s.charAt(++i);
  311. for (int j=0; j<runLength; ++j) array[ai++] = runValue;
  312. }
  313. }
  314. else {
  315. array[ai++] = (short) c;
  316. }
  317. }
  318. if (ai != length)
  319. throw new InternalError("Bad run-length encoded short array");
  320. return array;
  321. }
  322. /**
  323. * Construct an array of bytes from a run-length encoded string.
  324. */
  325. static final byte[] RLEStringToByteArray(String s) {
  326. int length = (((int) s.charAt(0)) << 16) | ((int) s.charAt(1));
  327. byte[] array = new byte[length];
  328. boolean nextChar = true;
  329. char c = 0;
  330. int node = 0;
  331. int runLength = 0;
  332. int i = 2;
  333. for (int ai=0; ai<length; ) {
  334. // This part of the loop places the next byte into the local
  335. // variable 'b' each time through the loop. It keeps the
  336. // current character in 'c' and uses the boolean 'nextChar'
  337. // to see if we've taken both bytes out of 'c' yet.
  338. byte b;
  339. if (nextChar) {
  340. c = s.charAt(i++);
  341. b = (byte) (c >> 8);
  342. nextChar = false;
  343. }
  344. else {
  345. b = (byte) (c & 0xFF);
  346. nextChar = true;
  347. }
  348. // This part of the loop is a tiny state machine which handles
  349. // the parsing of the run-length encoding. This would be simpler
  350. // if we could look ahead, but we can't, so we use 'node' to
  351. // move between three nodes in the state machine.
  352. switch (node) {
  353. case 0:
  354. // Normal idle node
  355. if (b == ESCAPE_BYTE) {
  356. node = 1;
  357. }
  358. else {
  359. array[ai++] = b;
  360. }
  361. break;
  362. case 1:
  363. // We have seen one ESCAPE_BYTE; we expect either a second
  364. // one, or a run length and value.
  365. if (b == ESCAPE_BYTE) {
  366. array[ai++] = ESCAPE_BYTE;
  367. node = 0;
  368. }
  369. else {
  370. runLength = b;
  371. // Interpret signed byte as unsigned
  372. if (runLength < 0) runLength += 0x100;
  373. node = 2;
  374. }
  375. break;
  376. case 2:
  377. // We have seen an ESCAPE_BYTE and length byte. We interpret
  378. // the next byte as the value to be repeated.
  379. for (int j=0; j<runLength; ++j) array[ai++] = b;
  380. node = 0;
  381. break;
  382. }
  383. }
  384. if (node != 0)
  385. throw new InternalError("Bad run-length encoded byte array");
  386. if (i != s.length())
  387. throw new InternalError("Excess data in RLE byte array string");
  388. return array;
  389. }
  390. /**
  391. * Format a String for representation in a source file. This includes
  392. * breaking it into lines escaping characters using octal notation
  393. * when necessary (control characters and double quotes).
  394. */
  395. static final String formatForSource(String s) {
  396. StringBuffer buffer = new StringBuffer();
  397. for (int i=0; i<s.length();) {
  398. if (i > 0) buffer.append("+\n");
  399. buffer.append(" \"");
  400. int count = 11;
  401. while (i<s.length() && count<80) {
  402. char c = s.charAt(i++);
  403. if (c < '\u0020' || c == '"') {
  404. // Represent control characters and the double quote
  405. // using octal notation; otherwise the string we form
  406. // won't compile, since Unicode escape sequences are
  407. // processed before tokenization.
  408. buffer.append('\\');
  409. buffer.append(HEX_DIGIT[(c & 0700) >> 6]); // HEX_DIGIT works for octal
  410. buffer.append(HEX_DIGIT[(c & 0070) >> 3]);
  411. buffer.append(HEX_DIGIT[(c & 0007)]);
  412. count += 4;
  413. }
  414. else if (c <= '\u007E') {
  415. buffer.append(c);
  416. count += 1;
  417. }
  418. else {
  419. buffer.append("\\u");
  420. buffer.append(HEX_DIGIT[(c & 0xF000) >> 12]);
  421. buffer.append(HEX_DIGIT[(c & 0x0F00) >> 8]);
  422. buffer.append(HEX_DIGIT[(c & 0x00F0) >> 4]);
  423. buffer.append(HEX_DIGIT[(c & 0x000F)]);
  424. count += 6;
  425. }
  426. }
  427. buffer.append('"');
  428. }
  429. return buffer.toString();
  430. }
  431. static final char[] HEX_DIGIT = {'0','1','2','3','4','5','6','7',
  432. '8','9','A','B','C','D','E','F'};
  433. }