PageRenderTime 47ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/Automark/src/au/edu/mq/comp/common/SscanfFormat.java

https://bitbucket.org/stevecassidy/junit-grading
Java | 641 lines | 289 code | 62 blank | 290 comment | 102 complexity | 1344667769a29205194f19860001b8a4 MD5 | raw file
  1. package au.edu.mq.comp.common;
  2. //Code borrowed heavily from:
  3. //Jodd Team (jodd.org)
  4. public class SscanfFormat
  5. {
  6. protected int width;
  7. protected int precision;
  8. protected StringBuffer pre;
  9. protected char post;
  10. protected boolean leadingZeroes;
  11. protected boolean showPlus;
  12. protected boolean alternate;
  13. protected boolean showSpace;
  14. protected boolean leftAlign;
  15. protected boolean groupDigits;
  16. protected char fmt; // one of cdeEfgGiosxXos
  17. protected boolean countSignInLen;
  18. protected String format;
  19. protected String source;
  20. /**
  21. * Formats a number in a printf format, like C.
  22. *
  23. * @param s the format string following printf format string
  24. * The string has a prefix, a format code and a suffix. The prefix and suffix
  25. * become part of the formatted output. The format code directs the
  26. * formatting of the (single) parameter to be formatted. The code has the
  27. * following structure
  28. * <ul>
  29. * <li> a <b>%</b> (required)
  30. *
  31. * <li> a modifier (optional)
  32. * <dl>
  33. * <dt> + <dd> forces display of + for positive numbers
  34. * <dt> ~ <dd> do not count leading + or - in length
  35. * <dt> 0 <dd> show leading zeroes
  36. * <dt> - <dd> align left in the field
  37. * <dt> space <dd> prepend a space in front of positive numbers
  38. * <dt> # <dd> use "alternate" format. Add 0 or 0x for octal or hexadecimal numbers.
  39. * Don't suppress trailing zeroes in general floating point format.
  40. * <dt> , <dd> groups decimal values by thousands (for 'diuxXb' formats)
  41. * </dl>
  42. *
  43. * <li> an integer denoting field width (optional)
  44. *
  45. * <li> a period (<b>.</b>) followed by an integer denoting precision (optional)
  46. *
  47. * <li> a format descriptor (required)
  48. * <dl>
  49. * <dt>f <dd> floating point number in fixed format,
  50. * <dt>e, E <dd> floating point number in exponential notation (scientific format).
  51. * The E format results in an uppercase E for the exponent (1.14130E+003), the e
  52. * format in a lowercase e,
  53. * <dt>g, G <dd> floating point number in general format (fixed format for small
  54. * numbers, exponential format for large numbers). Trailing zeroes are suppressed.
  55. * The G format results in an uppercase E for the exponent (if any), the g format
  56. * in a lowercase e,.
  57. * <dt>d, i <dd> signed long and integer in decimal,
  58. * <dt>u <dd> unsigned long or integer in decimal,
  59. * <dt>x <dd> unsigned long or integer in hexadecimal,
  60. * <dt>o <dd> unsigned long or integer in octal,
  61. * <dt>b <dd> unsigned long or integer in binary,
  62. * <dt>s <dd> string,
  63. * <dt>c <dd> character,
  64. * <dt>l, L <dd> boolean in lower or upper case (for booleans and int/longs).
  65. * </dl>
  66. * </ul>
  67. */
  68. public SscanfFormat(String source, String format) {
  69. this.source = source;
  70. this.format = format;
  71. }
  72. public boolean prepareNextParseParam() {
  73. if(format == null)
  74. return false;
  75. width = 0;
  76. precision = -1;
  77. pre = new StringBuffer();
  78. post = 0;
  79. leadingZeroes = false;
  80. showPlus = false;
  81. alternate = false;
  82. showSpace = false;
  83. leftAlign = false;
  84. countSignInLen = true;
  85. fmt = ' ';
  86. int i = 0;
  87. int length = format.length();
  88. int parseState; // 0 = prefix, 1 = flags, 2 = width, 3 = precision, 4 = format, 5 = end
  89. // 0: parse string prefix upto first '%'.
  90. while (true) {
  91. if (i >= length) {
  92. return false;
  93. }
  94. char c = format.charAt(i);
  95. if (c != '%') {
  96. pre.append(c);
  97. i++;
  98. continue;
  99. }
  100. if (i >= length - 1) {
  101. throw new IllegalArgumentException("Format string can not end with '%'.");
  102. }
  103. if (format.charAt(i + 1) == '%') { // double '%%'
  104. pre.append('%');
  105. i += 2;
  106. continue;
  107. }
  108. parseState = 1; // single % founded
  109. i++;
  110. break;
  111. }
  112. // 1: parse flags
  113. flagsloop:
  114. while (parseState == 1) {
  115. if (i >= length) {
  116. parseState = 5;
  117. break;
  118. }
  119. char c = format.charAt(i);
  120. switch (c) {
  121. case ' ': showSpace = true; break;
  122. case '-': leftAlign = true; break;
  123. case '+': showPlus = true; break;
  124. case '0': leadingZeroes = true; break;
  125. case '#': alternate = true; break;
  126. case '~': countSignInLen = false; break;
  127. case ',': groupDigits = true; break;
  128. default:
  129. parseState = 2;
  130. break flagsloop;
  131. }
  132. i++;
  133. }
  134. // 2: parse width
  135. while (parseState == 2) {
  136. if (i >= length) {
  137. parseState = 5;
  138. break;
  139. }
  140. char c = format.charAt(i);
  141. if ((c >= '0') && (c <= '9')) {
  142. width = (width * 10) + format.charAt(i) - '0';
  143. i++;
  144. continue;
  145. }
  146. if (format.charAt(i) == '.') {
  147. parseState = 3;
  148. precision = 0;
  149. i++;
  150. } else {
  151. parseState = 4;
  152. }
  153. break;
  154. }
  155. // 3: parse precision
  156. while (parseState == 3) {
  157. if (i >= length) {
  158. parseState = 5;
  159. break;
  160. }
  161. char c = format.charAt(i);
  162. if ((c >= '0') && (c <= '9')) {
  163. precision = (precision * 10) + format.charAt(i) - '0';
  164. i++;
  165. continue;
  166. }
  167. parseState = 4;
  168. break;
  169. }
  170. // 4: parse format
  171. if (parseState == 4) {
  172. if (i < length) {
  173. fmt = format.charAt(i);
  174. i++;
  175. }
  176. }
  177. if(i < length){
  178. post = format.charAt(i);
  179. format = format.substring(i);
  180. } else {
  181. format = null;
  182. }
  183. return true;
  184. }
  185. // ---------------------------------------------------------------- public form methods
  186. /**
  187. * Formats a character into a string (like sprintf in C).
  188. */
  189. public Character parse(char value) {
  190. if (fmt != 'c') {
  191. throw new IllegalArgumentException("Invalid character format: '" + fmt + "' is not 'c'.");
  192. }
  193. int index = parseToStartOfFormat();
  194. if(index != -1) {
  195. Character c = Character.valueOf(source.charAt(index));
  196. if(index < source.length()-1)
  197. source = source.substring(index+1);
  198. return c;
  199. }
  200. return null;
  201. }
  202. /**
  203. * Formats a boolean into a string (like sprintf in C).
  204. */
  205. //public String form(boolean value) {
  206. //
  207. // if (fmt == 'l') {
  208. // return pad(value ? "true" : "false");
  209. // }
  210. // else if (fmt == 'L') {
  211. // return pad(value ? "TRUE" : "FALSE");
  212. // }
  213. // throw new IllegalArgumentException("Invalid boolean format: '" + fmt + "' is not one of 'lL'.");
  214. //}
  215. /**
  216. * Formats a double into a string (like sprintf in C).
  217. */
  218. //public String form(double x) {
  219. // String r;
  220. //
  221. // if (precision < 0) {
  222. // precision = 6;
  223. // }
  224. //
  225. // int s = 1;
  226. // if (x < 0) {
  227. // x = -x;
  228. // s = -1;
  229. // }
  230. // if (fmt == 'f') {
  231. // r = fixedFormat(x);
  232. // } else if (fmt == 'e' || fmt == 'E' || fmt == 'g' || fmt == 'G') {
  233. // r = expFormat(x);
  234. // } else {
  235. // throw new IllegalArgumentException("Invalid floating format: '" + fmt + "' is not one of 'feEgG'.");
  236. // }
  237. // return pad(sign(s, r));
  238. //}
  239. /**
  240. * Formats a long integer into a string (like sprintf in C).
  241. */
  242. public Object parse(long x) {
  243. Object retval = null;
  244. int s = 0;
  245. int index;
  246. index = parseToStartOfFormat();
  247. if(index == -1)
  248. return null;
  249. switch (fmt) {
  250. case 'd':
  251. case 'i': {
  252. int v = 0;
  253. if(source.charAt(index) == '-') {
  254. s = -1;
  255. ++index;
  256. } else {
  257. s = 1;
  258. }
  259. int sLen = source.length();
  260. boolean foundNumber = false;
  261. while(index < sLen) {
  262. char c = source.charAt(index);
  263. if(c >= '0' && c <= '9') {
  264. v = (v * 10) + c - '0';
  265. ++index;
  266. foundNumber = true;
  267. if(width > 0 && --width == 0)
  268. break;
  269. } else {
  270. break;
  271. }
  272. }
  273. if(foundNumber) {
  274. v *= s;
  275. retval = Integer.valueOf(v);
  276. }
  277. } break;
  278. case 'u': {
  279. int v = 0;
  280. int sLen = source.length();
  281. boolean foundNumber = false;
  282. while(index < sLen) {
  283. char c = source.charAt(index);
  284. if(c >= '0' && c <= '9') {
  285. v = (v * 10) + c - '0';
  286. ++index;
  287. foundNumber = true;
  288. if(width > 0 && --width == 0)
  289. break;
  290. } else {
  291. break;
  292. }
  293. }
  294. if(foundNumber) {
  295. retval = Integer.valueOf(v);
  296. }
  297. } break;
  298. case 'x':
  299. case 'X': {
  300. int v = 0;
  301. int sLen = source.length();
  302. boolean foundNumber = false;
  303. while(index < sLen) {
  304. char c = source.charAt(index);
  305. if(c >= '0' && c <= '9') {
  306. v = (v * 16) + c - '0';
  307. ++index;
  308. foundNumber = true;
  309. if(width > 0 && --width == 0)
  310. break;
  311. } else if(c >= 'a' && c <= 'f') {
  312. v = (v * 16) + c - 'a' + 10;
  313. ++index;
  314. foundNumber = true;
  315. if(width > 0 && --width == 0)
  316. break;
  317. } else if(c >= 'A' && c <= 'F') {
  318. v = (v * 16) + c - 'A' + 10;
  319. ++index;
  320. foundNumber = true;
  321. if(width > 0 && --width == 0)
  322. break;
  323. } else {
  324. break;
  325. }
  326. }
  327. if(foundNumber) {
  328. retval = Integer.valueOf(v);
  329. }
  330. } break;
  331. default:
  332. throw new IllegalArgumentException("Invalid number format: '" + fmt + "' is not one of 'diuoxX'.");
  333. }
  334. if(retval != null) {
  335. if(index < source.length())
  336. source = source.substring(index);
  337. }
  338. return retval;
  339. }
  340. /**
  341. * Formats an integer into a string (like sprintf in C).
  342. */
  343. public Object parse(int x) {
  344. return parse((long)x);
  345. }
  346. /**
  347. * Formats a string into a larger string (like sprintf in C).
  348. */
  349. public Object parse(String s) {
  350. if (fmt != 's') {
  351. throw new IllegalArgumentException("Invalid long format: '" + fmt + "' is not 's'.");
  352. }
  353. int index = parseToStartOfFormat();
  354. int startIndex = index;
  355. if(index == -1) {
  356. return null;
  357. }
  358. int sLen = source.length();
  359. while(index < sLen) {
  360. char c = source.charAt(index++);
  361. if(precision > 0) {
  362. if(--precision == 0)
  363. break;
  364. } else if(width > 0) {
  365. if(--width == 0)
  366. break;
  367. } else if(c == post) {
  368. --index;
  369. break;
  370. }
  371. }
  372. String retval = null;
  373. if(index != startIndex) {
  374. retval = source.substring(startIndex, index);
  375. }
  376. if(index < source.length())
  377. source = source.substring(index);
  378. return retval;
  379. }
  380. ///**
  381. //* Formats a double with exp format.
  382. //*/
  383. //protected String expFormat(double d) {
  384. // StringBuilder f = new StringBuilder();
  385. // int e = 0;
  386. // double dd = d;
  387. // double factor = 1;
  388. //
  389. // if (d != 0) {
  390. // while (dd > 10) {
  391. // e++;
  392. // factor /= 10;
  393. // dd /= 10;
  394. // }
  395. // while (dd < 1) {
  396. // e--;
  397. // factor *= 10;
  398. // dd *= 10;
  399. // }
  400. // }
  401. // if (((fmt == 'g') || (fmt == 'G')) && (e >= -4) && (e < precision)) {
  402. // return fixedFormat(d);
  403. // }
  404. //
  405. // d *= factor;
  406. // f.append(fixedFormat(d));
  407. //
  408. // if (fmt == 'e' || fmt == 'g') {
  409. // f.append('e');
  410. // } else {
  411. // f.append('E');
  412. // }
  413. //
  414. // StringBuilder p = new StringBuilder("000");
  415. // if (e >= 0) {
  416. // f.append('+');
  417. // p.append(e);
  418. // } else {
  419. // f.append('-');
  420. // p.append(-e);
  421. // }
  422. //
  423. // char[] data = new char[3];
  424. // p.getChars(p.length() - 3, p.length(), data, 0);
  425. // return f.append(data).toString();
  426. //}
  427. //
  428. ///**
  429. //* Formats a double with fixed format.
  430. //*/
  431. //protected String fixedFormat(double d) {
  432. // boolean removeTrailing = (fmt == 'G' || fmt == 'g') && !alternate;
  433. //
  434. // // remove trailing zeroes and decimal point
  435. // if (d > 0x7FFFFFFFFFFFFFFFL) {
  436. // return expFormat(d);
  437. // }
  438. // if (precision == 0) {
  439. // return (long) (d /*+ 0.5*/) + (removeTrailing ? "" : StringPool.DOT); // no rounding
  440. // }
  441. //
  442. // long whole = (long) d;
  443. // double fr = d - whole; // fractional part
  444. //
  445. // if (fr >= 1 || fr < 0) {
  446. // return expFormat(d);
  447. // }
  448. //
  449. // double factor = 1;
  450. // StringBuilder leadingZeroesStr = new StringBuilder();
  451. //
  452. // for (int i = 1; i <= precision && factor <= 0x7FFFFFFFFFFFFFFFL; i++) {
  453. // factor *= 10;
  454. // leadingZeroesStr.append('0');
  455. // }
  456. //
  457. // long l = (long) (factor * fr /*+ 0.5*/); // no rounding
  458. // if (l >= factor) {
  459. // l = 0;
  460. // whole++;
  461. // }
  462. //
  463. // String z = leadingZeroesStr.toString() + l;
  464. // z = '.' + z.substring(z.length() - precision, z.length());
  465. //
  466. // if (removeTrailing) {
  467. // int t = z.length() - 1;
  468. // while (t >= 0 && z.charAt(t) == '0') {
  469. // t--;
  470. // }
  471. // if (t >= 0 && z.charAt(t) == '.') {
  472. // t--;
  473. // }
  474. // z = z.substring(0, t + 1);
  475. // }
  476. // return whole + z;
  477. //}
  478. //
  479. ///**
  480. //* Pads the value with spaces and adds prefix and suffix.
  481. //*/
  482. //protected String pad(String value) {
  483. // String spaces = repeat(' ', width - value.length());
  484. // if (leftAlign) {
  485. // return pre + value + spaces;
  486. // } else {
  487. // return pre + spaces + value;
  488. // }
  489. //}
  490. //
  491. ///**
  492. //* Returns new string created by repeating a single character.
  493. //*/
  494. //protected static String repeat(char c, int n) {
  495. // if (n <= 0) {
  496. // return (StringPool.EMPTY);
  497. // }
  498. // char[] buffer = new char[n];
  499. // for (int i = 0; i < n; i++) {
  500. // buffer[i] = c;
  501. // }
  502. // return new String(buffer);
  503. //}
  504. //
  505. //protected String sign(int s, String r) {
  506. // String p = StringPool.EMPTY;
  507. //
  508. // if (s < 0) {
  509. // p = StringPool.DASH;
  510. // } else if (s > 0) {
  511. // if (showPlus) {
  512. // p = StringPool.PLUS;
  513. // } else if (showSpace) {
  514. // p = StringPool.SPACE;
  515. // }
  516. // } else {
  517. // if (fmt == 'o' && alternate && r.length() > 0 && r.charAt(0) != '0') {
  518. // p = "0";
  519. // } else if (fmt == 'x' && alternate) {
  520. // p = "0x";
  521. // } else if (fmt == 'X' && alternate) {
  522. // p = "0X";
  523. // }
  524. // }
  525. //
  526. // int w = 0;
  527. //
  528. // if (leadingZeroes) {
  529. // w = width;
  530. // } else if ((fmt == 'u' || fmt == 'd' || fmt == 'i' || fmt == 'x' || fmt == 'X' || fmt == 'o') && precision > 0) {
  531. // w = precision;
  532. // }
  533. //
  534. // if (countSignInLen) {
  535. // return p + repeat('0', w - p.length() - r.length()) + r;
  536. // } else {
  537. // return p + repeat('0', w - r.length()) + r;
  538. // }
  539. //}
  540. //
  541. ///**
  542. //* Groups numbers by inserting 'separator' after every group of 'size' digits,
  543. //* starting from the right.
  544. //*/
  545. //protected String groupDigits(String value, int size, char separator) {
  546. // if (groupDigits == false) {
  547. // return value;
  548. // }
  549. // StringBuilder r = new StringBuilder(value.length() + 10);
  550. // int ndx = 0;
  551. // int len = value.length() - 1;
  552. // int mod = len % size;
  553. // while (ndx < len) {
  554. // r.append(value.charAt(ndx));
  555. // if (mod == 0) {
  556. // r.append(separator);
  557. // mod = size;
  558. // }
  559. // mod--;
  560. // ndx++;
  561. // }
  562. // r.append(value.charAt(ndx));
  563. // return r.toString();
  564. //}
  565. private int parseToStartOfFormat() {
  566. int i;
  567. int sLen = source.length();
  568. int fLen = pre.length();
  569. for(i = 0; i < sLen && i < fLen; ++i) {
  570. if(source.charAt(i) != pre.charAt(i)) {
  571. return -1;
  572. }
  573. }
  574. if(i < sLen && i == fLen) {
  575. return i;
  576. }
  577. return -1;
  578. }
  579. }