PageRenderTime 327ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/ojc-core/encodersl/encoder-coco/src/com/sun/encoder/coco/runtime/CobolDataConverter.java

https://bitbucket.org/openesb/openesb-components
Java | 2554 lines | 1486 code | 242 blank | 826 comment | 291 complexity | 36f5586553d4da33ef0a550252a543bd MD5 | raw file
Possible License(s): AGPL-3.0
  1. /*
  2. * BEGIN_HEADER - DO NOT EDIT
  3. *
  4. * The contents of this file are subject to the terms
  5. * of the Common Development and Distribution License
  6. * (the "License"). You may not use this file except
  7. * in compliance with the License.
  8. *
  9. * You can obtain a copy of the license at
  10. * https://open-jbi-components.dev.java.net/public/CDDLv1.0.html.
  11. * See the License for the specific language governing
  12. * permissions and limitations under the License.
  13. *
  14. * When distributing Covered Code, include this CDDL
  15. * HEADER in each file and include the License file at
  16. * https://open-jbi-components.dev.java.net/public/CDDLv1.0.html.
  17. * If applicable add the following below this CDDL HEADER,
  18. * with the fields enclosed by brackets "[]" replaced with
  19. * your own identifying information: Portions Copyright
  20. * [year] [name of copyright owner]
  21. */
  22. /*
  23. * @(#)CobolDataConverter.java
  24. *
  25. * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved.
  26. *
  27. * END_HEADER - DO NOT EDIT
  28. */
  29. package com.sun.encoder.coco.runtime;
  30. import java.io.ByteArrayOutputStream;
  31. import java.io.IOException;
  32. import java.io.InputStream;
  33. import java.io.InputStreamReader;
  34. import java.io.OutputStream;
  35. import java.io.OutputStreamWriter;
  36. import java.io.UnsupportedEncodingException;
  37. import java.math.BigDecimal;
  38. import java.math.BigInteger;
  39. import java.nio.ByteBuffer;
  40. import java.nio.CharBuffer;
  41. import java.nio.charset.CharacterCodingException;
  42. import java.nio.charset.Charset;
  43. import java.util.Arrays;
  44. import java.util.Collections;
  45. import java.util.HashMap;
  46. import java.util.Map;
  47. import com.sun.encoder.coco.runtime.messages.ErrorManager;
  48. import com.sun.encoder.coco.runtime.messages.Message;
  49. import com.sun.encoder.coco.runtime.messages.MessageCatalog;
  50. /**
  51. * Principal class for converting between Copybook data and Java data
  52. * reprseentations.
  53. *
  54. * @author Noel Ang
  55. * @version $Revision: 1.4 $
  56. */
  57. public class CobolDataConverter {
  58. private static final int MAX_EXFLOAT_EXPONENT = 99;
  59. private static final byte LIT_BYTE = (byte) 0xFF;
  60. private static final byte ZONE_NYBBLE_SIGN_BYTEMASK = (byte) 0xF0;
  61. private static final byte ZONE_NYBBLE_VALUE_BYTEMASK = (byte) 0x0F;
  62. private static final byte PACKED_NYBBLE_SIGN_BYTEMASK = (byte) 0x0F;
  63. private static final byte POSITIVE_ZONE_SIGN = (byte) 0xC0;
  64. private static final byte NEGATIVE_ZONE_SIGN = (byte) 0xD0;
  65. private static final byte UNSIGNED_ZONE_SIGN = (byte) 0xF0;
  66. private static final byte POSITIVE_PACK_SIGN = 0x0C;
  67. private static final byte NEGATIVE_PACK_SIGN = 0x0D;
  68. private static final byte UNSIGNED_PACK_SIGN = 0x0F;
  69. private static final byte[] DBCSSPACE = {0x40, 0x40};
  70. private static final char[] SPACE = {' '};
  71. private static final char[] PLUS = {'+'};
  72. private static final char[] MINUS = {'-'};
  73. private static final Map<String, byte[]> mSpaceEncodings =
  74. Collections.synchronizedMap(new HashMap<String, byte[]>());
  75. private static final Map<String, byte[]> mPlusEncodings =
  76. Collections.synchronizedMap(new HashMap<String, byte[]>());
  77. private static final Map<String, byte[]> mMinusEncodings =
  78. Collections.synchronizedMap(new HashMap<String, byte[]>());
  79. private static final ErrorManager cErrorMgr =
  80. ErrorManager.getManager("OpenESB.encoder.COBOLCopybook." + CobolDataConverter.class.getName());
  81. private CobolDataConverter() {
  82. }
  83. /**
  84. * Write a value as a Cobol display usage item. Use for alphabetic,
  85. * alphanumeric, alphanumeric-edited, and numeric-edited items. If the
  86. * value's size is less than the item size, it is padded with trailing space
  87. * characters.
  88. *
  89. * @param outStream Outlet for data
  90. * @param data The value to write
  91. * @param spec Characteristics of the item;
  92. * see {@link CobolCharacteristics#toString()}
  93. * @param enc Encoding to use for the output
  94. *
  95. * @throws IOException if an I/O error occurs in writing the value,
  96. * or if the supplied writer does not use the
  97. * required character encoding
  98. */
  99. public static void encodeToDisplay(OutputStream outStream,
  100. String data,
  101. CobolCharacteristics specs,
  102. String enc)
  103. throws IOException {
  104. final int size;
  105. OutputStreamWriter writer = new OutputStreamWriter(outStream, enc);
  106. size = specs.getSize();
  107. if (data.length() > size) {
  108. Message msg = MessageCatalog.getMessage("CCCR3002");
  109. cErrorMgr.log(ErrorManager.Severity.WARN,
  110. null,
  111. msg.formatText(new Object[]{data, String.valueOf(size)}));
  112. data = data.substring(0, size);
  113. }
  114. writer.write(data);
  115. writer.flush();
  116. for (int i = 0, pad = size - data.length(); i < pad; i++) {
  117. writer.write(' ');
  118. }
  119. }
  120. /**
  121. * Write a value as an external floating point (display usage) item.
  122. *
  123. * @param outStream Outlet for data
  124. * @param data Value to write
  125. * @param picture Cobol picture string associated with the item
  126. * @param spec Characteristics of the item;
  127. * see {@link CobolCharacteristics#toString()}
  128. * @param enc Encoding to use for the output
  129. *
  130. * @throws IOException if an I/O error occurs in writing the value,
  131. * or if the supplied writer does not use the
  132. * required character encoding
  133. * @throws ArithmeticException if the data exponent is too large
  134. */
  135. public static void encodeToExternalFloat(OutputStream outStream,
  136. BigDecimal data,
  137. String picture,
  138. CobolCharacteristics specs,
  139. String enc)
  140. throws IOException {
  141. final StringBuffer value = new StringBuffer(data.unscaledValue().toString());
  142. final int scalePic = specs.getDecimalPosition();
  143. int scale = data.scale();
  144. int exponent = 0 - scale;
  145. int digitsPic = 0;
  146. int digits = value.length();
  147. OutputStreamWriter writer = new OutputStreamWriter(outStream, enc);
  148. /* calc picture's digit requirements */
  149. digitsPic = 0;
  150. for (int i = 0;
  151. i < picture.length() && picture.charAt(i) != 'E'; i++) {
  152. if (picture.charAt(i) == '9') {
  153. digitsPic++;
  154. }
  155. }
  156. /* remove sign from value */
  157. if (data.signum() == -1) {
  158. value.delete(0, 1);
  159. digits--;
  160. }
  161. // truncate if needed
  162. if (digitsPic < digits) {
  163. int snip = digits - digitsPic;
  164. value.delete(digits - snip, digits);
  165. scale = Math.max(0, scale - snip);
  166. exponent += snip;
  167. digits = value.length();
  168. }
  169. // pad if needed
  170. if (digits < digitsPic) {
  171. int snip = digitsPic - digits;
  172. for (int i = 0; i < snip; i++) {
  173. value.append('0');
  174. }
  175. scale += (scale > 0 ? snip : 0);
  176. exponent -= snip;
  177. digits = value.length();
  178. }
  179. // reconcile data and picture scales;
  180. // compensate for their decimal positions' misalignment
  181. exponent = scalePic + exponent;
  182. // adjust exponent now that all padding, truncation, and
  183. // decimal point insertion are completed
  184. int dotLoc = picture.indexOf('.');
  185. boolean haveExplicitDecimal = (dotLoc != -1);
  186. // picture offset - 1 == value offset
  187. if (haveExplicitDecimal) {
  188. value.insert(dotLoc - 1, '.');
  189. }
  190. // exponent range check
  191. if (Math.abs(exponent) > MAX_EXFLOAT_EXPONENT) {
  192. Message msg = MessageCatalog.getMessage("CCCR4009");
  193. String err = msg.formatText(new Object[]{
  194. String.valueOf(exponent)
  195. });
  196. cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
  197. throw new ArithmeticException(err);
  198. }
  199. // write sign
  200. boolean negValue = (data.signum() == -1);
  201. boolean needSignificandSign = !Character.isDigit(picture.charAt(0));
  202. boolean explicitSignificandSign = needSignificandSign && picture.charAt(0) == '+';
  203. if (needSignificandSign) {
  204. if (negValue && new BigDecimal(
  205. value.toString()).unscaledValue().intValue() != 0) {
  206. writer.write('-');
  207. } else if (explicitSignificandSign) {
  208. writer.write('+');
  209. }
  210. }
  211. // write significand and exponent
  212. boolean explicitExponentSign = picture.charAt(picture.indexOf('E') + 1) == '+';
  213. writer.write(value.toString() + 'E');
  214. if (exponent < 0) {
  215. writer.write('-');
  216. } else if (explicitExponentSign) {
  217. writer.write('+');
  218. }
  219. exponent = Math.abs(exponent);
  220. if (exponent < 10) {
  221. writer.write('0');
  222. }
  223. writer.write(Integer.toString(exponent));
  224. writer.flush();
  225. }
  226. /**
  227. * Write a value as a Cobol COMP-1 usage, floating point item.
  228. *
  229. * @param stream Outlet for data
  230. * @param val The value to write
  231. *
  232. * @throws IOException if an I/O error occurs in writing the value
  233. */
  234. public static void encodeToFloat(OutputStream stream,
  235. float val)
  236. throws IOException {
  237. int value = Float.floatToIntBits(val);
  238. byte[] bytes = new byte[]{
  239. (byte) ((value & 0xFF000000) >> 24),
  240. (byte) ((value & 0x00FF0000) >> 16),
  241. (byte) ((value & 0x0000FF00) >> 8),
  242. (byte) (value & 0x000000FF)
  243. };
  244. stream.write(bytes, 0, bytes.length);
  245. stream.flush();
  246. }
  247. /**
  248. * Write a value as a Cobol COMP-2 usage, floating point item.
  249. *
  250. * @param stream Outlet for data
  251. * @param val The value to write
  252. *
  253. * @throws IOException if an I/O error occurs in writing the value
  254. */
  255. public static void encodeToDouble(OutputStream stream,
  256. double val)
  257. throws IOException {
  258. long value = Double.doubleToLongBits(val);
  259. byte[] bytes = new byte[]{
  260. (byte) ((value & 0xFF00000000000000L) >> 56),
  261. (byte) ((value & 0x00FF000000000000L) >> 48),
  262. (byte) ((value & 0x0000FF0000000000L) >> 40),
  263. (byte) ((value & 0x000000FF00000000L) >> 32),
  264. (byte) ((value & 0x00000000FF000000L) >> 24),
  265. (byte) ((value & 0x0000000000FF0000L) >> 16),
  266. (byte) ((value & 0x000000000000FF00L) >> 8),
  267. (byte) (value & 0x00000000000000FFL)
  268. };
  269. stream.write(bytes, 0, bytes.length);
  270. stream.flush();
  271. }
  272. /**
  273. * Write a value as a Cobol display usage, zoned item. Use for external
  274. * decimal (zoned) items.
  275. *
  276. * @param stream Outlet for data
  277. * @param data The value to write
  278. * @param picture Cobol item picture for the value
  279. * @param spec Characteristics of the item;
  280. * see {@link CobolCharacteristics#toString()}
  281. * @param enc Encoding to use for the output
  282. *
  283. * @throws IOException if an I/O error occurs in writing the value
  284. */
  285. public static void encodeToZoned(OutputStream stream,
  286. BigDecimal data,
  287. String picture,
  288. CobolCharacteristics specs,
  289. String enc)
  290. throws IOException {
  291. final StringBuffer value = new StringBuffer(data.unscaledValue().toString());
  292. final boolean needSign = specs.isSigned();
  293. final boolean separateSign = specs.isSignSeparate();
  294. final boolean leadingSign = specs.isSignLeading();
  295. final int scalePic = specs.getDecimalPosition();
  296. final int scalingPositions = specs.getDecimalScalingPositions();
  297. int digitsPic;
  298. byte signByte = 0;
  299. int scale;
  300. // Count number of digits in the picture
  301. digitsPic = 0;
  302. for (int i = 0, len = picture.length(); i < len; i++) {
  303. if (picture.charAt(i) == '9') {
  304. digitsPic += 1;
  305. }
  306. }
  307. // Scale value to its picture
  308. scale = data.scale();
  309. scale =
  310. fitToNumericPicture(value,
  311. scale,
  312. digitsPic,
  313. scalePic,
  314. scalingPositions);
  315. // Remove negative sign for convenience
  316. if (data.signum() == -1) {
  317. value.delete(0, 1);
  318. }
  319. // Add sign
  320. if (needSign) {
  321. // Two ways to add it:
  322. // Prepend/append if separate sign requested,
  323. if (separateSign) {
  324. if (leadingSign) {
  325. switch (data.signum()) {
  326. case -1:
  327. value.insert(0, '-');
  328. break;
  329. case 0:
  330. value.insert(0, '+');
  331. break;
  332. case 1:
  333. value.insert(0, '+');
  334. break;
  335. default:
  336. }
  337. } else {
  338. switch (data.signum()) {
  339. case -1:
  340. value.append('-');
  341. break;
  342. case 0:
  343. value.append('+');
  344. break;
  345. case 1:
  346. value.append('+');
  347. break;
  348. default:
  349. }
  350. }
  351. } // Or add it to first/last digit's fist nybble if non-separate sign
  352. else {
  353. // for a non-separate, leading sign, embed it into the
  354. // first nybble of the first digit
  355. if (leadingSign) {
  356. signByte = (byte) Character.digit(value.charAt(0), 10);
  357. value.delete(0, 1);
  358. } // for a non-separate, trailing sign, embed it into the
  359. // first nybble of the last digit
  360. else {
  361. int last = value.length() - 1;
  362. signByte =
  363. (byte) Character.digit(value.charAt(last), 10);
  364. value.delete(last, last + 1);
  365. }
  366. switch (data.signum()) {
  367. case -1:
  368. signByte |= NEGATIVE_ZONE_SIGN;
  369. break;
  370. case 0:
  371. signByte |= POSITIVE_ZONE_SIGN;
  372. break;
  373. case 1:
  374. signByte |= POSITIVE_ZONE_SIGN;
  375. break;
  376. default:
  377. }
  378. }
  379. }
  380. // Write the data out
  381. if (needSign && !separateSign && leadingSign) {
  382. stream.write((int) signByte);
  383. stream.flush();
  384. }
  385. OutputStreamWriter writer = new OutputStreamWriter(stream, enc);
  386. writer.write(value.toString());
  387. writer.flush();
  388. if (needSign && !separateSign && !leadingSign) {
  389. stream.write((int) signByte);
  390. stream.flush();
  391. }
  392. }
  393. /**
  394. * Write a value as a Cobol display usage, zoned item. Use for internal
  395. * decimal (zoned) items. If the value's size is less than the item's size,
  396. * the item is padded with leading zeroes.
  397. *
  398. * @param stream Outlet for data
  399. * @param data The value to write
  400. * @param picture Cobol item picture
  401. * @param spec Characteristics of the item;
  402. * see {@link CobolCharacteristics#toString()}
  403. * @param enc Encoding to use for the output
  404. *
  405. * @throws IOException if an I/O error occurs in writing the value
  406. */
  407. public static void encodeToZoned(OutputStream stream,
  408. long data,
  409. String picture,
  410. CobolCharacteristics specs,
  411. String enc)
  412. throws IOException {
  413. // Legacy behaviour: when setting zoned values
  414. // (which may have scale > 0) using long or int (which cannot store
  415. // information about scale > 0), treat the input not as an integral,
  416. // but as floating point value. This means applying scale info
  417. // of the item to the input after it is converted to a BigDecimal.
  418. //
  419. // A proposal to clean up this mess by removing int- and long-based
  420. // getters and setters for zoned items has been rejected on grounds of
  421. // backward compatibility.
  422. BigDecimal decimal;
  423. decimal = new BigDecimal(Long.toString(data));
  424. decimal.movePointLeft(specs.getDecimalPosition());
  425. encodeToZoned(stream, decimal, picture, specs, enc);
  426. }
  427. /**
  428. * Write a value as a Cobol display usage, zoned item. Use for internal
  429. * decimal (zoned) items. If the value's size is less than the item's size,
  430. * the item is padded with leading zeroes.
  431. *
  432. * @param stream Outlet for data
  433. * @param data The value to write
  434. * @param picture Cobol item picture
  435. * @param spec Characteristics of the item;
  436. * see {@link CobolCharacteristics#toString()}
  437. * @param enc Encoding to use for the output
  438. *
  439. * @throws IOException if an I/O error occurs in writing the value
  440. */
  441. public static void encodeToZoned(OutputStream stream,
  442. int data,
  443. String picture,
  444. CobolCharacteristics specs,
  445. String enc)
  446. throws IOException {
  447. // Legacy behaviour: when setting zoned values
  448. // (which may have scale > 0) using long or int (which cannot store
  449. // information about scale > 0), treat the input not as an integral,
  450. // but as floating point value. This means applying scale info
  451. // of the item to the input after it is converted to a BigDecimal.
  452. //
  453. // A proposal to clean up this mess by removing int- and long-based
  454. // getters and setters for zoned items has been rejected on grounds of
  455. // backward compatibility.
  456. BigDecimal decimal;
  457. decimal = new BigDecimal(Integer.toString(data));
  458. decimal.movePointLeft(specs.getDecimalPosition());
  459. encodeToZoned(stream, decimal, picture, specs, enc);
  460. }
  461. /**
  462. * Write a value as a Cobol display-1 usage item. Use for DBCS items. If the
  463. * value's size is less than the item size, the item is padded with trailing
  464. * spaces.
  465. *
  466. * @param stream Outlet for data
  467. * @param data The value to write
  468. * @param size Item size
  469. *
  470. * @throws IllegalArgumentException if the data size exceeds the item size,
  471. * or the number of bytes in the data is
  472. * not a multiple of 2.
  473. * @throws IOException if an I/O error occurs in writing the
  474. * value
  475. */
  476. public static void encodeToDbcs(OutputStream stream,
  477. byte[] data,
  478. int size)
  479. throws IOException {
  480. int length = data.length;
  481. if ((length % 2) != 0) {
  482. Message msg = MessageCatalog.getMessage("CCCR4010");
  483. String err = msg.formatText(new Object[]{
  484. String.valueOf(length)
  485. });
  486. cErrorMgr.log(ErrorManager.Severity.ERROR,
  487. null,
  488. err);
  489. throw new IllegalArgumentException(err);
  490. }
  491. if (length > size) {
  492. Message msg = MessageCatalog.getMessage("CCCR3003");
  493. cErrorMgr.log(ErrorManager.Severity.WARN,
  494. null,
  495. msg.formatText(new Object[]{
  496. String.valueOf(length),
  497. String.valueOf(size)
  498. }));
  499. byte[] copy = new byte[size];
  500. System.arraycopy(data, 0, copy, 0, size);
  501. data = copy;
  502. }
  503. stream.write(data);
  504. for (int i = 0, pad = (size - length) / DBCSSPACE.length;
  505. i < pad; i++) {
  506. stream.write(DBCSSPACE, 0, DBCSSPACE.length);
  507. }
  508. stream.flush();
  509. }
  510. /**
  511. * Write a value as a Cobol 2-, 4- or 8-byte binary (usage BINARY/COMP)
  512. * item. The binary item width used depends on the number of digits in the
  513. * picture, as specified in IBM Cobol Reference:
  514. * <p/>
  515. * <pre>
  516. * 1-4 digits: 2 bytes
  517. * 5-9 digits: 4 bytes
  518. * 10-18 digits: 8 bytes
  519. * </pre>
  520. * <p/>
  521. * However, note that the width computation <em>is not derived from the
  522. * supplied picture</em>, it is taken from the pre-computed value encoded in
  523. * the characteristics bit vector <code>parms</code>.
  524. *
  525. * @param stream Outlet for data
  526. * @param data The value to write
  527. * @param picture Cobol item picture
  528. * @param spec Characteristics of the item;
  529. * see {@link CobolCharacteristics#toString()}
  530. *
  531. * @throws IOException if an I/O error occurs in writing the value
  532. */
  533. public static void encodeToBinary(OutputStream stream,
  534. BigDecimal data,
  535. String picture,
  536. CobolCharacteristics specs)
  537. throws IOException {
  538. final StringBuffer svalue = new StringBuffer(data.unscaledValue().toString());
  539. final int size = specs.getSize();
  540. final boolean signed = specs.isSigned();
  541. final int scalePic = specs.getDecimalPosition();
  542. final int scalingPositions = specs.getDecimalScalingPositions();
  543. int digitsPic;
  544. BigInteger value;
  545. byte[] valueBytes;
  546. int valueLen;
  547. // Remove sign info from value if item is unsigned
  548. if (!signed && data.signum() == -1) {
  549. svalue.delete(0, 1);
  550. }
  551. // Count number of digits in the picture
  552. digitsPic = 0;
  553. for (int i = 0, len = picture.length(); i < len; i++) {
  554. if (picture.charAt(i) == '9') {
  555. digitsPic += 1;
  556. }
  557. }
  558. fitToNumericPicture(svalue,
  559. data.scale(),
  560. digitsPic,
  561. scalePic,
  562. scalingPositions);
  563. value = new BigInteger(svalue.toString());
  564. valueBytes = value.toByteArray();
  565. valueLen = valueBytes.length;
  566. // Add byte padding
  567. if (size - valueLen > 0) {
  568. int padding = size - valueLen;
  569. byte[] newbytes = new byte[size];
  570. byte pad;
  571. int pos = 0;
  572. if (signed && data.signum() == -1) {
  573. pad = LIT_BYTE;
  574. } else {
  575. pad = 0;
  576. }
  577. while (padding-- > 0) {
  578. newbytes[pos] = pad;
  579. pos += 1;
  580. }
  581. for (int i = 0; i < valueLen; i++) {
  582. newbytes[pos] = valueBytes[i];
  583. pos += 1;
  584. }
  585. valueBytes = newbytes;
  586. }
  587. stream.write(valueBytes);
  588. }
  589. /**
  590. * Write a value as a Cobol 2-, 4- or 8-byte native binary (usage COMP5 or
  591. * INDEX) item. The binary item width used depends on the number of digits
  592. * in the picture, as specified in IBM Cobol Reference:
  593. * <p/>
  594. * <pre>
  595. * 1-4 digits: 2 bytes
  596. * 5-9 digits: 4 bytes
  597. * 10-18 digits: 8 bytes
  598. * </pre>
  599. * <p/>
  600. * However, note that the width computation <em>is not derived from the
  601. * supplied picture</em>, it is taken from the pre-computed value encoded in
  602. * the characteristics bit vector <code>parms</code>.
  603. *
  604. * @param stream Outlet for data
  605. * @param data The value to write
  606. * @param picture Cobol item picture
  607. * @param spec Characteristics of the item;
  608. * see {@link CobolCharacteristics#toString()}
  609. *
  610. * @throws IOException if an I/O error occurs in writing the value
  611. */
  612. public static void encodeToNativeBinary(OutputStream stream,
  613. BigDecimal data,
  614. String picture,
  615. CobolCharacteristics specs)
  616. throws IOException {
  617. final StringBuffer svalue = new StringBuffer(data.unscaledValue().toString());
  618. final int size = specs.getSize();
  619. final boolean signed = specs.isSigned();
  620. final int scalePic = specs.getDecimalPosition();
  621. final int scalingPositions = specs.getDecimalScalingPositions();
  622. int digitsPic;
  623. BigInteger value;
  624. byte[] valueBytes;
  625. int valueLen;
  626. // Remove sign info from value if item is unsigned
  627. if (!signed && data.signum() == -1) {
  628. svalue.delete(0, 1);
  629. }
  630. // Count number of digits in the picture
  631. digitsPic = 0;
  632. for (int i = 0, len = picture.length(); i < len; i++) {
  633. if (picture.charAt(i) == '9') {
  634. digitsPic += 1;
  635. }
  636. }
  637. fitToNumericNativePicture(svalue,
  638. data.scale(),
  639. digitsPic,
  640. scalePic,
  641. scalingPositions,
  642. size);
  643. value = new BigInteger(svalue.toString());
  644. valueBytes = value.toByteArray();
  645. valueLen = valueBytes.length;
  646. // Add byte padding
  647. if (size - valueLen > 0) {
  648. int padding = size - valueLen;
  649. byte[] newbytes = new byte[size];
  650. byte pad;
  651. int pos = 0;
  652. if (signed && value.signum() == -1) {
  653. pad = LIT_BYTE;
  654. } else {
  655. pad = 0;
  656. }
  657. while (padding-- > 0) {
  658. newbytes[pos] = pad;
  659. pos += 1;
  660. }
  661. for (int i = 0; i < valueLen; i++) {
  662. newbytes[pos] = valueBytes[i];
  663. pos += 1;
  664. }
  665. valueBytes = newbytes;
  666. }
  667. stream.write(valueBytes);
  668. }
  669. /**
  670. * Write a value as a Cobol 2-, 4- or 8-byte binary item (usage
  671. * BINARY/COMP). Delegates to
  672. * {@link #convertToBinary(OutputStream, BigDecimal, String, String)}
  673. * so see that method for additional details.
  674. *
  675. * @param stream Outlet for data
  676. * @param data The value to write
  677. * @param picture Cobol item picture
  678. * @param spec Characteristics of the item;
  679. * see {@link CobolCharacteristics#toString()}
  680. *
  681. * @throws IOException if an I/O error occurs in writing the value
  682. */
  683. public static void encodeToBinary(OutputStream stream,
  684. long data,
  685. String picture,
  686. CobolCharacteristics specs)
  687. throws IOException {
  688. // Legacy behaviour: when setting binary values
  689. // (which may have scale > 0) using long or int (which cannot store
  690. // information about scale > 0), treat the input not as an integral,
  691. // but as floating point value. This means applying scale info
  692. // of the item to the input after it is converted to a BigDecimal.
  693. //
  694. // A proposal to clean up this mess by removing int- and long-based
  695. // getters and setters for binary items has been rejected on grounds of
  696. // backward compatibility.
  697. BigDecimal decimal;
  698. decimal = new BigDecimal(Long.toString(data));
  699. decimal.movePointLeft(specs.getDecimalPosition());
  700. encodeToBinary(stream, decimal, picture, specs);
  701. }
  702. /**
  703. * Write a value as a Cobol 2-, 4- or 8-byte binary item (usage
  704. * BINARY/COMP). Delegates to
  705. * {@link #convertToBinary(OutputStream, BigDecimal, String, String)}
  706. * so see that method for additional details.
  707. *
  708. * @param stream Outlet for data
  709. * @param data The value to write
  710. * @param picture Cobol item picture
  711. * @param spec Characteristics of the item;
  712. * see {@link CobolCharacteristics#toString()}
  713. *
  714. * @throws IOException if an I/O error occurs in writing the value
  715. */
  716. public static void encodeToBinary(OutputStream stream,
  717. int data,
  718. String picture,
  719. CobolCharacteristics specs)
  720. throws IOException {
  721. // Legacy behaviour: when setting binary values
  722. // (which may have scale > 0) using long or int (which cannot store
  723. // information about scale > 0), treat the input not as an integral,
  724. // but as floating point value. This means applying scale info
  725. // of the item to the input after it is converted to a BigDecimal.
  726. //
  727. // A proposal to clean up this mess by removing int- and long-based
  728. // getters and setters for binary items has been rejected on grounds of
  729. // backward compatibility.
  730. BigDecimal decimal;
  731. decimal = new BigDecimal(Integer.toString(data));
  732. decimal.movePointLeft(specs.getDecimalPosition());
  733. encodeToBinary(stream, decimal, picture, specs);
  734. }
  735. /**
  736. * Write a value as a Cobol packed-decimal item. Use for internal decimal
  737. * items. Unlike the other variants of this method, this one can handle
  738. * values with non-zero decimal scaling.
  739. *
  740. * @param stream Outlet for data
  741. * @param data The value to write
  742. * @param picture Item picture
  743. * @param spec Characteristics of the item;
  744. * see {@link CobolCharacteristics#toString()}
  745. *
  746. * @throws IOException if an I/O error occurs in writing the value
  747. */
  748. public static void encodeToPacked(OutputStream stream,
  749. BigDecimal data,
  750. String picture,
  751. CobolCharacteristics specs)
  752. throws IOException {
  753. final StringBuffer value = new StringBuffer(data.unscaledValue().toString());
  754. final boolean signed = specs.isSigned();
  755. final int size = specs.getSize();
  756. final int scalePic = specs.getDecimalPosition();
  757. final int scalingPositions = specs.getDecimalScalingPositions();
  758. int digitsPic;
  759. int scale;
  760. // calc picture's digit requirements
  761. digitsPic = 0;
  762. for (int i = 0; i < picture.length(); i++) {
  763. if (picture.charAt(i) == '9') {
  764. digitsPic++;
  765. }
  766. }
  767. // Remove '-' for convenience
  768. if (data.signum() == -1) {
  769. value.delete(0, 1);
  770. }
  771. // Scale value to its picture
  772. scale = data.scale();
  773. scale = fitToNumericPicture(value,
  774. scale,
  775. digitsPic,
  776. scalePic,
  777. scalingPositions);
  778. // Packed decimal-specific truncation check
  779. int maxFitDigits = (size << 1) - 1; // reserve one for the
  780. //"sign" sign (neg, pos, unsigned)
  781. if (maxFitDigits < value.length()) {
  782. Message msg = MessageCatalog.getMessage("CCCR3004");
  783. String err = msg.formatText(new Object[]{
  784. data.toString(),
  785. String.valueOf((value.length() >> 1) + 1),
  786. String.valueOf(maxFitDigits)
  787. });
  788. cErrorMgr.log(ErrorManager.Severity.WARN, null, err);
  789. // truncate fraction
  790. if (scale > 0) {
  791. int truncsize = Math.max(0, scale - scalePic);
  792. value.delete(value.length() - truncsize, value.length());
  793. scale -= truncsize;
  794. }
  795. // truncate non-fraction
  796. if (maxFitDigits < value.length()) {
  797. int truncsize = Math.max(0, (value.length() - scale) - (digitsPic - scalePic));
  798. value.delete(0, truncsize);
  799. }
  800. }
  801. /*
  802. * if even number digits, need to add extra byte for sign:
  803. *
  804. * decimal = 12345
  805. * packed = 12|34|5s where s == sign nybble; extra byte not needed
  806. *
  807. * decimal = 1234
  808. * packed = 01|23|4s where s == sign nybble; extra byte needed
  809. */
  810. final boolean needPad = ((value.length() % 2) == 0);
  811. int current = 0;
  812. if (needPad) {
  813. byte pad = 0;
  814. byte digit =
  815. (byte) Character.digit(value.charAt(current++), 10);
  816. byte b = (byte) (pad | digit);
  817. stream.write(b);
  818. }
  819. for (int i = current; i < value.length(); i += 2) {
  820. byte digit1 = (byte) Character.digit(value.charAt(i), 10);
  821. byte digit2 = 0;
  822. if (i < (value.length() - 1)) { // if not last digit
  823. digit2 = (byte) Character.digit(value.charAt(i + 1), 10);
  824. } else if (!signed) {
  825. digit2 = UNSIGNED_PACK_SIGN;
  826. } else if ((new BigInteger(value.toString())).equals(
  827. BigInteger.ZERO)) {
  828. digit2 = POSITIVE_PACK_SIGN;
  829. } // else if ( Integer.valueOf( value.toString() ).intValue() == 0 ) {
  830. // digit2 = POSITIVE_PACK_SIGN;
  831. // }
  832. else {
  833. switch (data.signum()) {
  834. case -1:
  835. digit2 = NEGATIVE_PACK_SIGN;
  836. break;
  837. case 0:
  838. digit2 = POSITIVE_PACK_SIGN;
  839. break;
  840. case 1:
  841. digit2 = POSITIVE_PACK_SIGN;
  842. break;
  843. default:
  844. }
  845. }
  846. byte b = (byte) ((digit1 << 4) | digit2);
  847. stream.write(b);
  848. }
  849. }
  850. /**
  851. * Write a value as a Cobol packed-decimal item. Use for internal decimal
  852. * items.
  853. *
  854. * @param stream Outlet for data
  855. * @param data The value to write
  856. * @param picture Item picture
  857. * @param spec Characteristics of the item;
  858. * see {@link CobolCharacteristics#toString()}
  859. *
  860. * @throws IOException if an I/O error occurs in writing the value
  861. */
  862. public static void encodeToPacked(OutputStream stream,
  863. long data,
  864. String picture,
  865. CobolCharacteristics specs)
  866. throws IOException {
  867. // Legacy behaviour: when setting packed values
  868. // (which may have scale > 0) using long or int (which cannot store
  869. // information about scale > 0), treat the input not as an integral,
  870. // but as floating point value. This means applying scale info
  871. // of the item to the input after it is converted to a BigDecimal.
  872. //
  873. // A proposal to clean up this mess by removing int- and long-based
  874. // getters and setters for packed items has been rejected on grounds of
  875. // backward compatibility.
  876. BigDecimal decimal;
  877. decimal = new BigDecimal(Long.toString(data));
  878. decimal.movePointLeft(specs.getDecimalPosition());
  879. encodeToPacked(stream, decimal, picture, specs);
  880. }
  881. /**
  882. * Write a value as a Cobol packed-decimal item. Use for internal decimal
  883. * items.
  884. *
  885. * @param stream Outlet for data
  886. * @param data The value to write
  887. * @param picture Item picture
  888. * @param spec Characteristics of the item;
  889. * see {@link CobolCharacteristics#toString()}
  890. *
  891. * @throws IOException if an I/O error occurs in writing the value
  892. */
  893. public static void encodeToPacked(OutputStream stream,
  894. int data,
  895. String picture,
  896. CobolCharacteristics specs)
  897. throws IOException {
  898. // Legacy behaviour: when setting packed values
  899. // (which may have scale > 0) using long or int (which cannot store
  900. // information about scale > 0), treat the input not as an integral,
  901. // but as floating point value. This means applying scale info
  902. // of the item to the input after it is converted to a BigDecimal.
  903. //
  904. // A proposal to clean up this mess by removing int- and long-based
  905. // getters and setters for packed items has been rejected on grounds of
  906. // backward compatibility.
  907. BigDecimal decimal;
  908. decimal = new BigDecimal(Integer.toString(data));
  909. decimal.movePointLeft(specs.getDecimalPosition());
  910. encodeToPacked(stream, decimal, picture, specs);
  911. }
  912. /**
  913. * Write a value as a Cobol/EBCDIC index (4-byte) item.
  914. *
  915. * @param stream Outlet for data
  916. * @param data The value to write
  917. * @param spec Characteristics of the item;
  918. * see {@link CobolCharacteristics#toString()}
  919. *
  920. * @throws IOException if an I/O error occurs in writing the value
  921. */
  922. public static void encodeToIndex(OutputStream stream,
  923. int data,
  924. CobolCharacteristics specs)
  925. throws IOException {
  926. BigDecimal value = new BigDecimal(data);
  927. encodeToNativeBinary(stream, value, "999999999", specs);
  928. }
  929. /**
  930. * Convert the data from the input stream into to a Java integer (int)
  931. * value. Use for numeric items that can fit 4-byte signed storage.
  932. *
  933. * @param stream Data inlet as a byte stream
  934. * @param picture Cobol item picture
  935. * @param spec Characteristics of the item;
  936. * see {@link CobolCharacteristics#toString()}
  937. * @param enc Encoding to use to read character data
  938. *
  939. * @return Data as an int
  940. *
  941. * @throws IllegalArgumentException if an int value cannot be produced given
  942. * the data item information
  943. * @throws IOException if an I/O error occured in reading
  944. * input
  945. */
  946. public static int decodeToInt(InputStream stream,
  947. String picture,
  948. CobolCharacteristics specs,
  949. String enc)
  950. throws IOException {
  951. int result;
  952. int category = specs.getPicCategory();
  953. int usage = specs.getUsage();
  954. if (CobolCharacteristics.PIC_NUM == category) {
  955. switch (usage) {
  956. case CobolCharacteristics.USAGE_DISPLAY: {
  957. String value = readNumberDisplay(stream, specs, enc);
  958. result = Integer.parseInt(value);
  959. break;
  960. }
  961. case CobolCharacteristics.USAGE_BINARY:
  962. case CobolCharacteristics.USAGE_COMP:
  963. case CobolCharacteristics.USAGE_COMP4: {
  964. BigDecimal value = readNumberBinary(stream, specs);
  965. result = value.unscaledValue().intValue();
  966. break;
  967. }
  968. case CobolCharacteristics.USAGE_INDEX: {
  969. BigDecimal value = readNumberBinary(stream, specs);
  970. result = value.unscaledValue().intValue();
  971. break;
  972. }
  973. case CobolCharacteristics.USAGE_PACKED:
  974. case CobolCharacteristics.USAGE_COMP3: {
  975. BigDecimal value = readNumberPacked(stream, specs);
  976. result = value.unscaledValue().intValue();
  977. break;
  978. }
  979. case CobolCharacteristics.USAGE_COMP5: {
  980. BigDecimal value = readNumberBinary(stream, specs);
  981. result = value.unscaledValue().intValue();
  982. break;
  983. }
  984. default:
  985. Message msg = MessageCatalog.getMessage("CCCR4011");
  986. String err = msg.toString();
  987. cErrorMgr.log(ErrorManager.Severity.ERROR,
  988. null,
  989. err);
  990. throw new IllegalArgumentException(err);
  991. }
  992. } else {
  993. Message msg = MessageCatalog.getMessage("CCCR4013");
  994. String err = msg.formatText(new Object[]{compressedPic(picture)});
  995. cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
  996. throw new IllegalArgumentException(err);
  997. }
  998. return result;
  999. }
  1000. /**
  1001. * Convert the data from the input stream into a Java long integer (long)
  1002. * value. Use for numeric items exceeding 4-byte signed storage
  1003. * capability.
  1004. *
  1005. * @param stream Data inlet as a byte stream
  1006. * @param picture Cobol item picture
  1007. * @param spec Characteristics of the item;
  1008. * see {@link CobolCharacteristics#toString()}
  1009. * @param enc Encoding to use to read character data
  1010. *
  1011. * @return Data as an long
  1012. *
  1013. * @throws IllegalArgumentException if a long value cannot be produced given
  1014. * the data item information
  1015. * @throws IOException an I/O error occured in reading input
  1016. */
  1017. public static long decodeTolong(InputStream stream,
  1018. String picture,
  1019. CobolCharacteristics specs,
  1020. String enc)
  1021. throws IOException {
  1022. long result;
  1023. int category = specs.getPicCategory();
  1024. int usage = specs.getUsage();
  1025. if (CobolCharacteristics.PIC_NUM == category) {
  1026. switch (usage) {
  1027. case CobolCharacteristics.USAGE_DISPLAY: {
  1028. String value = readNumberDisplay(stream, specs, enc);
  1029. result = Long.parseLong(value);
  1030. break;
  1031. }
  1032. case CobolCharacteristics.USAGE_BINARY:
  1033. case CobolCharacteristics.USAGE_COMP:
  1034. case CobolCharacteristics.USAGE_COMP4: {
  1035. BigDecimal value = readNumberBinary(stream, specs);
  1036. result = value.unscaledValue().longValue();
  1037. break;
  1038. }
  1039. case CobolCharacteristics.USAGE_PACKED:
  1040. case CobolCharacteristics.USAGE_COMP3: {
  1041. BigDecimal value = readNumberPacked(stream, specs);
  1042. result = value.unscaledValue().longValue();
  1043. break;
  1044. }
  1045. case CobolCharacteristics.USAGE_COMP5: {
  1046. BigDecimal value = readNumberBinary(stream, specs);
  1047. result = value.unscaledValue().longValue();
  1048. break;
  1049. }
  1050. case CobolCharacteristics.USAGE_INDEX: {
  1051. BigDecimal value = readNumberBinary(stream, specs);
  1052. result = value.unscaledValue().longValue();
  1053. break;
  1054. }
  1055. default:
  1056. Message msg = MessageCatalog.getMessage("CCCR4012");
  1057. String err = msg.toString();
  1058. cErrorMgr.log(ErrorManager.Severity.ERROR,
  1059. null,
  1060. err);
  1061. throw new IllegalArgumentException(err);
  1062. }
  1063. } else {
  1064. Message msg = MessageCatalog.getMessage("CCCR4013");
  1065. String err = msg.formatText(new Object[]{compressedPic(picture)});
  1066. cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
  1067. throw new IllegalArgumentException(err);
  1068. }
  1069. return result;
  1070. }
  1071. /**
  1072. * Convert the data from the input stream into a Java float. Use for COMP-1
  1073. * items.
  1074. *
  1075. * @param stream Byte Data inlet
  1076. *
  1077. * @return Data as float
  1078. *
  1079. * @throws IOException if an I/O error occurs while reading the input,
  1080. * including having insufficient data
  1081. */
  1082. public static float decodeToFloat(InputStream stream)
  1083. throws IOException {
  1084. final int NEED = 4;
  1085. final byte[] bytebuf = new byte[NEED];
  1086. int got = stream.read(bytebuf);
  1087. if (got != NEED) {
  1088. Message msg = MessageCatalog.getMessage("CCCR4014");
  1089. String err = msg.formatText(new Object[]{
  1090. "COMP-1",
  1091. String.valueOf(NEED),
  1092. String.valueOf(Math.max(0, got))
  1093. });
  1094. cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
  1095. throw new IOException(err);
  1096. }
  1097. int intBits;
  1098. intBits = (((int) bytebuf[0]) << 24) & 0xFF000000;
  1099. intBits |= (((int) bytebuf[1]) << 16) & 0x00FF0000;
  1100. intBits |= (((int) bytebuf[2]) << 8) & 0x0000FF00;
  1101. intBits |= bytebuf[3] & 0x000000FF;
  1102. return Float.intBitsToFloat(intBits);
  1103. }
  1104. /**
  1105. * Convert the data from the input stream into a Java double. Use for COMP-2
  1106. * items.
  1107. *
  1108. * @param stream Byte Data inlet
  1109. *
  1110. * @return Data as double
  1111. *
  1112. * @throws IOException if an I/O error occurs while reading the input,
  1113. * including having insufficient data
  1114. */
  1115. public static double decodeToDouble(InputStream stream)
  1116. throws IOException {
  1117. final int NEED = 8;
  1118. final byte[] bytebuf = new byte[NEED];
  1119. int got = stream.read(bytebuf);
  1120. if (got != NEED) {
  1121. Message msg = MessageCatalog.getMessage("CCCR4014");
  1122. String err = msg.formatText(new Object[]{
  1123. "COMP-2",
  1124. String.valueOf(NEED),
  1125. String.valueOf(Math.max(0, got))
  1126. });
  1127. cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
  1128. throw new IOException(err);
  1129. }
  1130. long longBits;
  1131. longBits = (((long) bytebuf[0]) << 56) & 0xFF00000000000000L;
  1132. longBits |= (((long) bytebuf[1]) << 48) & 0x00FF000000000000L;
  1133. longBits |= (((long) bytebuf[2]) << 40) & 0x0000FF0000000000L;
  1134. longBits |= (((long) bytebuf[3]) << 32) & 0x000000FF00000000L;
  1135. longBits |= (((long) bytebuf[4]) << 24) & 0x00000000FF000000L;
  1136. longBits |= (((long) bytebuf[5]) << 16) & 0x0000000000FF0000L;
  1137. longBits |= (((long) bytebuf[6]) << 8) & 0x000000000000FF00L;
  1138. longBits |= bytebuf[7] & 0x00000000000000FFL;
  1139. return Double.longBitsToDouble(longBits);
  1140. }
  1141. /**
  1142. * Convert the data from the input stream into a Java byte array. Use for
  1143. * DBCS items.
  1144. *
  1145. * @param stream Data inlet as a byte stream
  1146. * @param picture Cobol item picture
  1147. * @param spec Characteristics of the item;
  1148. * see {@link CobolCharacteristics#toString()}
  1149. *
  1150. * @return Data as an array of bytes, with each element containing a half (a
  1151. * byte) of a double-byte character value
  1152. *
  1153. * @throws IOException if an I/O error occured in reading
  1154. * input
  1155. * @throws IllegalArgumentException if the Cobol item category specified is
  1156. * not DBCS, or a DBCS value cannot be read
  1157. * given the data item information
  1158. */
  1159. public static byte[] decodeTobytes(InputStream stream,
  1160. String picture,
  1161. CobolCharacteristics specs)
  1162. throws IOException {
  1163. byte[] result = null;
  1164. int category = specs.getPicCategory();
  1165. if (CobolCharacteristics.PIC_DBCS == category) {
  1166. result = readDBCS(stream, specs);
  1167. } else {
  1168. Message msg = MessageCatalog.getMessage("CCCR4015");
  1169. String err = msg.formatText(new Object[]{compressedPic(picture)});
  1170. cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
  1171. throw new IllegalArgumentException(err);
  1172. }
  1173. return result;
  1174. }
  1175. /**
  1176. * Convert the data from the input stream into a Java string value. Use for
  1177. * alphabetic, alphanumeric, alphanumeric-edited, and numeric-edited items.
  1178. *
  1179. * @param stream Data inlet as a byte stream
  1180. * @param reader Data inlet as a character reader
  1181. * @param picture Cobol item picture
  1182. * @param spec Characteristics of the item;
  1183. * see {@link CobolCharacteristics#toString()}
  1184. * @param enc Encoding to use to read character data
  1185. *
  1186. * @return Data as a string
  1187. *
  1188. * @throws IllegalArgumentException if a string value cannot be produced
  1189. * given the data item information
  1190. * @throws IOException an I/O error occured in reading input
  1191. */
  1192. public static String decodeToString(InputStream stream,
  1193. String picture,
  1194. CobolCharacteristics specs,
  1195. String enc)
  1196. throws IOException {
  1197. String result = null;
  1198. int category = specs.getPicCategory();
  1199. // Switch back to old implementation. Will improve it later
  1200. // if necessary.
  1201. //
  1202. // ESR 104580
  1203. // Profiling shows that creating a new InputStreamReader
  1204. // from InputStream is very expensive, in particular on
  1205. // HP/UX 11iV2. According to CocoCodeGen.java, the InputStreamReader
  1206. // argument, reader, is created from the InputStream argument,
  1207. // stream, using the encoding represented by the String argument,
  1208. // enc. Therefore there is really no need to create a new
  1209. // InputStreamReader from InputStream.
  1210. //
  1211. // Relevant code in CocoCodeGen.java is
  1212. //
  1213. // emitter.emit("mEncoding = \"cp037\"; // initial default");
  1214. // ...
  1215. // emitter.emit("mWriter =new OutputStreamWriter(mOutputStream, mEncoding);");
  1216. // emitter.emit("mReader = new InputStreamReader(mInputStream, mEncoding);");
  1217. // and
  1218. // emitter.emit( "mEncoding = enc.name();" );
  1219. // emitter.emit( "mWriter = new OutputStreamWriter(mOutputStream, mEncoding);" );
  1220. // emitter.emit( "mReader = new InputStreamReader(mInputStream, mEncoding);" );
  1221. // and
  1222. // emitter.emit("return CobolDataConverter2.convertTo"+javaName+"(mRoot.mInputStream, mRoot.mReader, m"+wood.getJavaName()+
  1223. // "Picture, m"+wood.getJavaName()+"Mask, mRoot.mEncoding);");
  1224. switch (category) {
  1225. case CobolCharacteristics.PIC_ALPHA:
  1226. //// ESR 104580
  1227. result = readAlpha(stream, specs, enc);
  1228. //result = readAlpha( reader, specs, enc );
  1229. break;
  1230. case CobolCharacteristics.PIC_ALPHANUM:
  1231. //// ESR 104580
  1232. result = readAlphanum(stream, specs, enc);
  1233. //result = readAlphanum( reader, specs, enc );
  1234. break;
  1235. case CobolCharacteristics.PIC_ALPHANUME:
  1236. //// ESR 104580
  1237. result = readAlphanumEdited(stream, specs, enc);
  1238. //result = readAlphanumEdited( reader, specs, enc );
  1239. break;
  1240. case CobolCharacteristics.PIC_NUME:
  1241. //// ESR 104580
  1242. result = readNumEdited(stream, specs, enc);
  1243. //result = readNumEdited( reader, picture, specs, enc );
  1244. break;
  1245. case CobolCharacteristics.PIC_DBCS:
  1246. result = readDBCS(stream, specs, enc);
  1247. break;
  1248. default:
  1249. Message msg = MessageCatalog.getMessage("CCCR4016");
  1250. String err = msg.formatText(new Object[]{compressedPic(picture)});
  1251. cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
  1252. throw new IllegalArgumentException(err);
  1253. }
  1254. return result;
  1255. }
  1256. /**
  1257. * Convert the data from the input stream into a Java BigDecimal. Use for
  1258. * internal and external floating point items, and internal decimal
  1259. * exceeding capabilities of 8-byte signed storage.
  1260. *
  1261. * @param stream Data inlet as a byte stream
  1262. * @param picture Cobol item picture
  1263. * @param spec Characteristics of the item;
  1264. * see {@link CobolCharacteristics#toString()}
  1265. * @param enc Encoding to use to read character data
  1266. *
  1267. * @return Data as a BigDecimal
  1268. *
  1269. * @throws IllegalArgumentException if a BigDecimal value cannot be produced
  1270. * given the data item information
  1271. * @throws IOException an I/O error occured in reading input
  1272. */
  1273. public static BigDecimal decodeToBigDecimal(InputStream stream,
  1274. String picture,
  1275. CobolCharacteristics specs,
  1276. String enc)
  1277. throws IOException {
  1278. BigDecimal result = null;
  1279. int category = specs.getPicCategory();
  1280. int usage = specs.getUsage();
  1281. if (CobolCharacteristics.PIC_EXFLOAT == category) {
  1282. result = readExFloat(stream, specs, enc);
  1283. } else if (CobolCharacteristics.PIC_NUM == category) {
  1284. switch (usage) {
  1285. case CobolCharacteristics.USAGE_DISPLAY:
  1286. result =
  1287. new BigDecimal(readNumberDisplay(stream, specs, enc));
  1288. break;
  1289. case CobolCharacteristics.USAGE_BINARY:
  1290. case CobolCharacteristics.USAGE_COMP:
  1291. case CobolCharacteristics.USAGE_COMP4:
  1292. result = readNumberBinary(stream, specs);
  1293. break;
  1294. case CobolCharacteristics.USAGE_PACKED:
  1295. case CobolCharacteristics.USAGE_COMP3:
  1296. result = readNumberPacked(stream, specs);
  1297. break;
  1298. case CobolCharacteristics.USAGE_COMP5:
  1299. result = readNumberBinary(stream, specs);
  1300. break;
  1301. case CobolCharacteristics.USAGE_INDEX:
  1302. result = readNumberBinary(stream, specs);
  1303. break;
  1304. default:
  1305. Message msg = MessageCatalog.getMessage("CCCR4017");
  1306. String err = msg.formatText(new Object[]{compressedPic(picture)});
  1307. cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
  1308. throw new IllegalArgumentException(err);
  1309. }
  1310. } else {
  1311. Message msg = MessageCatalog.getMessage("CCCR4018");
  1312. String err = msg.formatText(new Object[]{compressedPic(picture)});
  1313. cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
  1314. throw new IllegalArgumentException(err);
  1315. }
  1316. return result;
  1317. }
  1318. public static boolean isSameEncoding(String enc1, String enc2) {
  1319. boolean same = false;
  1320. if (enc1 != null && enc2 != null) {
  1321. if (enc1.equalsIgnoreCase(enc2)) {
  1322. same = true;
  1323. } else {
  1324. try {
  1325. Charset Lcs1 = Charset.forName(enc1);
  1326. Charset Lcs2 = Charset.forName(enc2);
  1327. same = Lcs1.equals(Lcs2);
  1328. } catch (Exception e) {
  1329. same = false;
  1330. }
  1331. }
  1332. }
  1333. return same;
  1334. }
  1335. /**
  1336. * Get the byte sequence for a "space" character in the indicated encoding
  1337. *
  1338. * @param enc Charset encoding
  1339. *
  1340. * @throws CharacterCodingException
  1341. * if the specified encoding cannot encode the space character
  1342. * @throws UnsupportedEncodingException
  1343. * if the specified encoding is not supported
  1344. */
  1345. public static byte[] getSpace(String enc)
  1346. throws CharacterCodingException,
  1347. UnsupportedEncodingException {
  1348. byte[] LspaceBytes;
  1349. synchronized (mSpaceEncodings) {
  1350. LspaceBytes = (byte[]) mSpaceEncodings.get(enc);
  1351. }
  1352. if (LspaceBytes == null) {
  1353. LspaceBytes = charsToBytes(SPACE, enc);
  1354. synchronized (mSpaceEncodings) {
  1355. mSpaceEncodings.put(enc, LspaceBytes);
  1356. }
  1357. }
  1358. return LspaceBytes;
  1359. }
  1360. /**
  1361. * Get the byte sequence for a "plus" character in the indicated encoding
  1362. *
  1363. * @param enc Charset encoding
  1364. *
  1365. * @throws CharacterCodingException
  1366. * if the specified encoding cannot encode the plus character
  1367. * @throws UnsupportedEncodingException
  1368. * if the specified encoding is not supported
  1369. */
  1370. public static byte[] getPlus(String enc)
  1371. throws CharacterCodingException,
  1372. UnsupportedEncodingException {
  1373. byte[] LplusBytes;
  1374. synchronized (mPlusEncodings) {
  1375. LplusBytes = (byte[]) mPlusEncodings.get(enc);
  1376. }
  1377. if (LplusBytes == null) {
  1378. LplusBytes = charsToBytes(PLUS, enc);
  1379. synchronized (mPlusEncodings) {
  1380. mPlusEncodings.put(enc, LplusBytes);
  1381. }
  1382. }
  1383. return LplusBytes;
  1384. }
  1385. /**
  1386. * Get the byte sequence for a "minus" character in the indicated encoding
  1387. *
  1388. * @param enc Charset encoding
  1389. *
  1390. * @throws CharacterCodingException
  1391. * if the specified encoding cannot encode the minus character
  1392. * @throws UnsupportedEncodingException
  1393. * if the specified encoding is not supported
  1394. */
  1395. public static byte[] getMinus(String enc)
  1396. throws CharacterCodingException,
  1397. UnsupportedEncodingException {
  1398. byte[] LminusBytes;
  1399. synchronized (mMinusEncodings) {
  1400. LminusBytes = (byte[]) mMinusEncodings.get(enc);
  1401. }
  1402. if (LminusBytes == null) {
  1403. LminusBytes = charsToBytes(MINUS, enc);
  1404. synchronized (mMinusEncodings) {
  1405. mMinusEncodings.put(enc, LminusBytes);
  1406. }
  1407. }
  1408. return LminusBytes;
  1409. }
  1410. private static String readAlpha(InputStream stream,
  1411. CobolCharacteristics spec,
  1412. String enc)
  1413. throws IOException {
  1414. byte[] buf = new byte[spec.getSize()];
  1415. int got = stream.read(buf, 0, buf.length);
  1416. if ((got == -1) || (got != buf.length)) {
  1417. return null;
  1418. }
  1419. return new String(buf, enc);
  1420. }
  1421. /**
  1422. * Read (Cobol alphanumeric) data from the input stream into a string.
  1423. *
  1424. * @param stream Data inlet
  1425. * @param spec Characteristics of the item;
  1426. * see {@link CobolCharacteristics#toString()}
  1427. * @param enc Encoding to use when reading data
  1428. *
  1429. * @return string representation of the consumed data, or null if there is
  1430. * no or insufficient data to process
  1431. *
  1432. * @throws IOException if an I/O error occurs attempting to read
  1433. * from the input stream, or if the specified
  1434. * encoding is not supported
  1435. */
  1436. private static String readAlphanum(InputStream stream,
  1437. CobolCharacteristics spec,
  1438. String enc)
  1439. throws IOException {
  1440. byte[] buf = new byte[spec.getSize()];
  1441. int want = buf.length;
  1442. int got = stream.read(buf, 0, want);
  1443. if ((got == -1) || (got != want)) {
  1444. return null;
  1445. }
  1446. return new String(buf, enc);
  1447. }
  1448. /**
  1449. * Read (Cobol alphanumeric-edited) data from the input stream.
  1450. *
  1451. * @param stream Data inlet
  1452. * @param spec Characteristics of the item;
  1453. * see {@link CobolCharacteristics#toString()}
  1454. * @param enc Encoding to use when reading data
  1455. *
  1456. * @return string representation of the consumed data, or null if there is
  1457. * no or insufficient data to process
  1458. *
  1459. * @throws IOException if an I/O error occurs attempting to read
  1460. * from the input stream, or if the specified
  1461. * encoding is not supported
  1462. */
  1463. private static String readAlphanumEdited(InputStream stream,
  1464. CobolCharacteristics spec,
  1465. String enc)
  1466. throws IOException {
  1467. /*
  1468. * Edited items are supported as follows:
  1469. * Consume ANY characters, up to the picture's length.
  1470. * If there's not enough data, pad with spaces.
  1471. * The data I get is somebody else's problem.
  1472. */
  1473. byte[] buf = new byte[spec.getSize()];
  1474. int want = buf.length;
  1475. int got = stream.read(buf, 0, want);
  1476. if ((got == -1) || (got != want)) {
  1477. return null;
  1478. }
  1479. return new String(buf, enc);
  1480. }
  1481. /**
  1482. * Read (Cobol numeric-edited) data from the input stream.
  1483. *
  1484. * @param stream Data inlet
  1485. * @param picture Cobol (numeric-edited) picture
  1486. * @param spec Characteristics of the item;
  1487. * see {@link CobolCharacteristics#toString()}
  1488. * @param enc Encoding to use when reading data
  1489. *
  1490. * @return string representation of the consumed data, or null if there is
  1491. * no or insufficient data to process
  1492. *
  1493. * @throws IOException if an I/O error occurs attempting to read
  1494. * from the input stream, or if the specified
  1495. * encoding is not supported
  1496. */
  1497. private static String readNumEdited(InputStream stream,
  1498. CobolCharacteristics spec,
  1499. String enc)
  1500. throws IOException {
  1501. /*
  1502. * Edited items are supported as follows:
  1503. * Consume ANY characters, up to the picture's length.
  1504. * If there's not enough data, pad (leading) with zeroes.
  1505. * The data I get is somebody else's problem
  1506. */
  1507. byte[] buf = new byte[spec.getSize()];
  1508. int want = buf.length;
  1509. int got = stream.read(buf, 0, want);
  1510. if ((got == -1) || (got != want)) {
  1511. return null;
  1512. }
  1513. return new String(buf, enc);
  1514. }
  1515. /**
  1516. * Read (Cobol external floating-point) data from the input stream.
  1517. *
  1518. * @param stream Data inlet
  1519. * @param spec Characteristics of the item;
  1520. * see {@link CobolCharacteristics#toString()}
  1521. * @param enc Encoding to use when reading data
  1522. *
  1523. * @return BigDecimal representation of the consumed data, or null if there
  1524. * is no or insufficient data to process
  1525. *
  1526. * @throws IOException if an I/O error occurs attempting to read
  1527. * from the input stream, or if the specified
  1528. * encoding is not supported
  1529. */
  1530. private static BigDecimal readExFloat(InputStream stream,
  1531. CobolCharacteristics spec,
  1532. String enc)
  1533. throws IOException {
  1534. InputStreamReader reader = createReader(stream, enc);
  1535. /* get long data (usage display) */
  1536. int count = spec.getSize();
  1537. char[] data = new char[count];
  1538. int got = reader.read(data);
  1539. if (got != count) {
  1540. return null;
  1541. }
  1542. /* convert to BigDecimal */
  1543. BigDecimal value = null;
  1544. if (validateExFloat(data)) {
  1545. value = new BigDecimal(new String(data));
  1546. }
  1547. return value;
  1548. }
  1549. /**
  1550. * Compares character data with an external floating point picture. The
  1551. * picture must be a valid external floating point picture (e.g.,
  1552. * "+9V99E+99".
  1553. *
  1554. * @param data Character data (i.e., usage display) to evaluate against the
  1555. * picture.
  1556. *
  1557. * @return true if the data is valid for the given picture, otherwise false
  1558. * is returned
  1559. */
  1560. private static boolean validateExFloat(char[] data) {
  1561. StringBuffer buf = new StringBuffer(String.valueOf(data));
  1562. int len = buf.length();
  1563. boolean haveExponentSign;
  1564. boolean haveSignificandSign;
  1565. if (len < 3) {
  1566. return false;
  1567. }
  1568. int charindex = buf.indexOf("E");
  1569. if (charindex == -1 || len == charindex + 1) {
  1570. return false;
  1571. }
  1572. char signchar = buf.charAt(charindex + 1);
  1573. haveExponentSign = (signchar == '-' || signchar == '+');
  1574. if (!haveExponentSign && !Character.isDigit(signchar)) {
  1575. return false;
  1576. }
  1577. char startchar = buf.charAt(0);
  1578. haveSignificandSign = (startchar == '+' || startchar == '-');
  1579. if (!haveSignificandSign && !Character.isDigit(startchar)) {
  1580. return false;
  1581. }
  1582. if (haveExponentSign && charindex + 2 == len) {
  1583. return false;
  1584. }
  1585. if (haveSignificandSign && charindex == 1) {
  1586. return false;
  1587. }
  1588. for (int i = 1; i < charindex; i++) {
  1589. if (!Character.isDigit(buf.charAt(i))) {
  1590. return false;
  1591. }
  1592. }
  1593. for (int i = charindex + 2; i < len; i++) {
  1594. if (!Character.isDigit(buf.charAt(i))) {
  1595. return false;
  1596. }
  1597. }
  1598. return true;
  1599. }
  1600. /**
  1601. * Read DBCS data from the input stream.
  1602. *
  1603. * @param stream Data inlet
  1604. * @param picture Cobol (DBCS) picture
  1605. * @param spec Characteristics of the item;
  1606. * see {@link CobolCharacteristics#toString()}
  1607. *
  1608. * @return byte array representation of the consumed data, or null if there
  1609. * is no or insufficient data to process
  1610. *
  1611. * @throws IOException if an I/O error occurs attempting to read
  1612. * from the input stream
  1613. */
  1614. private static byte[] readDBCS(InputStream stream,
  1615. CobolCharacteristics spec)
  1616. throws IOException {
  1617. int need = spec.getSize();
  1618. byte[] bytebuf = new byte[need];
  1619. int got = stream.read(bytebuf);
  1620. if (got != need) {
  1621. return null;
  1622. }
  1623. return bytebuf;
  1624. }
  1625. private static String readDBCS(InputStream stream,
  1626. CobolCharacteristics spec, String enc) throws IOException {
  1627. byte[] buf = new byte[spec.getSize()];
  1628. int got = stream.read(buf, 0, buf.length);
  1629. if ((got == -1) || (got != buf.length)) {
  1630. return null;
  1631. }
  1632. return new String(buf, enc);
  1633. }
  1634. /**
  1635. * Read (Cobol numeric) data from the input stream stored with DISPLAY usage
  1636. * (one character position per byte).
  1637. *
  1638. * @param stream Data inlet
  1639. * @param spec Characteristics of the item;
  1640. * see {@link CobolCharacteristics#toString()}
  1641. * @param enc Encoding to use when reading data
  1642. *
  1643. * @return string representation of the consumed data, or null if there is
  1644. * no or insufficient data to process. The representation returned
  1645. * DOES NOT HAVE DECIMAL POINT INFORMATION.
  1646. *
  1647. * @throws IOException if an I/O error occurs attempting to read
  1648. * from the input stream
  1649. */
  1650. private static String readNumberDisplay(InputStream stream,
  1651. CobolCharacteristics spec,
  1652. String enc)
  1653. throws IOException {
  1654. /* count number of digits (== bytes) to process as value */
  1655. int countDigits = spec.getSize();
  1656. /* signed data? */
  1657. boolean isSigned = spec.isSigned();
  1658. /* get value */
  1659. long[] value = new long[1];
  1660. String[] number = new String[1];
  1661. int read = readZonedNumber(stream,
  1662. spec,
  1663. value,
  1664. isSigned,
  1665. countDigits,
  1666. number,
  1667. enc);
  1668. if (read != countDigits) {
  1669. return null;
  1670. }
  1671. int dec_pos = spec.getDecimalPosition();
  1672. if (dec_pos > 0) {
  1673. BigDecimal bd =
  1674. new BigDecimal(BigInteger.valueOf(value[0]), dec_pos);
  1675. // double scaled_value = value[0];
  1676. // for ( int i = 0; i < dec_pos; i++ )
  1677. // scaled_value = scaled_value / 10;
  1678. // return Double.toString(scaled_value);
  1679. return bd.toString();
  1680. } else {
  1681. return Long.toString(value[ 0]);
  1682. }
  1683. }
  1684. /**
  1685. * Read (Cobol numeric) data from the input stream stored with BINARY
  1686. * usage.
  1687. *
  1688. * @param stream Data inlet
  1689. * @param spec Characteristics of the item;
  1690. * see {@link CobolCharacteristics#toString()}
  1691. *
  1692. * @return BigDecimal representation of the consumed data, or null if there
  1693. * is no or insufficient data to process.
  1694. *
  1695. * @throws IOException if an I/O error occurs attempting to read
  1696. * from the input stream
  1697. */
  1698. private static BigDecimal readNumberBinary(InputStream stream,
  1699. CobolCharacteristics spec)
  1700. throws IOException {
  1701. // decimal position and scaling
  1702. int decPos = spec.getDecimalPosition();
  1703. int scalingDigits = spec.getDecimalScalingPositions();
  1704. /* compute bytes to get */
  1705. int numBytes = spec.getSize();
  1706. /* get the data */
  1707. BigDecimal decimal;
  1708. long[] value = new long[1];
  1709. int got = 0;
  1710. switch (numBytes) {
  1711. case 2:
  1712. got = readBinaryNumber(stream, value, 2);
  1713. break;
  1714. case 4:
  1715. got = readBinaryNumber(stream, value, 4);
  1716. break;
  1717. case 8:
  1718. got = readBinaryNumber(stream, value, 8);
  1719. break;
  1720. default:
  1721. got = -1;
  1722. }
  1723. if (got != numBytes) {
  1724. return null;
  1725. }
  1726. decimal = new BigDecimal(Long.toString(value[ 0]));
  1727. decimal = decimal.movePointLeft(decPos);
  1728. decimal = decimal.movePointRight(decPos > 0 ? 0 : scalingDigits);
  1729. return decimal;
  1730. }
  1731. /**
  1732. * Read (Cobol numeric) packed-decimal data from the input stream.
  1733. *
  1734. * @param stream Data inlet
  1735. * @param spec Characteristics of the item;
  1736. * see {@link CobolCharacteristics#toString()}
  1737. *
  1738. * @return BigDecimal representation of the consumed data, or null if there
  1739. * is no or insufficient data to process.
  1740. *
  1741. * @throws IOException if an I/O error occurs attempting to read
  1742. * from the input stream
  1743. */
  1744. private static BigDecimal readNumberPacked(InputStream stream,
  1745. CobolCharacteristics spec)
  1746. throws IOException {
  1747. // decimal position and scaling
  1748. int decPos = spec.getDecimalPosition();
  1749. int scalingDigits = spec.getDecimalScalingPositions();
  1750. /* compute bytes to get */
  1751. int numBytes = spec.getSize();
  1752. /* get the data */
  1753. BigDecimal decimal;
  1754. long[] value = new long[1];
  1755. int got = readPackedNumber(stream, value, numBytes);
  1756. if (got != numBytes) {
  1757. return null;
  1758. }
  1759. decimal = new BigDecimal(Long.toString(value[ 0]));
  1760. decimal = decimal.movePointLeft(decPos);
  1761. decimal = decimal.movePointRight(decPos > 0 ? 0 : scalingDigits);
  1762. return decimal;
  1763. }
  1764. /**
  1765. * Read (Cobol numeric) data from the input stream and interpret it as a
  1766. * zoned decimal. The representation returned DOES NOT HAVE DECIMAL POINT
  1767. * INFORMATION.
  1768. *
  1769. * @param stream Data inlet
  1770. * @param spec Characteristics of the item; see
  1771. * {@link CobolCharacteristics#toString()}
  1772. * @param data Array to hold the read value; only the first element of the
  1773. * array is used.
  1774. * @param signed True to indicate sign indicator is part of the value (not
  1775. * necessarily a separate character); false to indicate the
  1776. * value is unsigned
  1777. * @param count The number of bytes to process from the stream as a zoned
  1778. * decimal value
  1779. * @param enc Encoding to use when reading data
  1780. *
  1781. * @return The number of bytes consumed to process the data, or -1 if there
  1782. * was no or insufficient data to process
  1783. *
  1784. * @throws IOException if an I/O error occurs attempting to read
  1785. * from the input stream
  1786. */
  1787. private static int readZonedNumber(InputStream stream,
  1788. CobolCharacteristics spec,
  1789. long[] data,
  1790. boolean signed,
  1791. int count,
  1792. String[] number,
  1793. String enc)
  1794. throws IOException {
  1795. boolean separateSign = spec.isSignSeparate();
  1796. boolean leadingSign = spec.isSignLeading();
  1797. byte[] plus = getPlus(enc);
  1798. byte[] minus = getMinus(enc);
  1799. byte[] buf = new byte[count];
  1800. int got = 0;
  1801. int sign = 0;
  1802. /* get value (number) */
  1803. got = stream.read(buf);
  1804. if (got != count) {
  1805. return (-1);
  1806. }
  1807. /* process separate sign */
  1808. if (separateSign) {
  1809. byte[] signbytes = getSeparateSign(leadingSign, buf, plus, minus);
  1810. if (signbytes == null) {
  1811. return got;
  1812. } else if (Arrays.equals(plus, signbytes)) {
  1813. sign = 1;
  1814. buf = stripSign(leadingSign, buf, plus);
  1815. } else if (Arrays.equals(minus, signbytes)) {
  1816. sign = -1;
  1817. buf = stripSign(leadingSign, buf, minus);
  1818. }
  1819. } else if (signed) { /* process sign nybble */
  1820. byte signByte = (leadingSign ? buf[0] : buf[got - 1]);
  1821. byte signBits = (byte) (signByte & ZONE_NYBBLE_SIGN_BYTEMASK);
  1822. byte valBits = (byte) (signByte & ZONE_NYBBLE_VALUE_BYTEMASK);
  1823. int value = (int) valBits;
  1824. if (value > 9) {
  1825. return 0;
  1826. }
  1827. if (leadingSign) {
  1828. buf[0] =
  1829. (byte) (valBits | (byte) ZONE_NYBBLE_SIGN_BYTEMASK);
  1830. } else {
  1831. buf[got - 1] =
  1832. (byte) (valBits | (byte) ZONE_NYBBLE_SIGN_BYTEMASK);
  1833. }
  1834. switch (signBits) {
  1835. case POSITIVE_ZONE_SIGN:
  1836. sign = 1;
  1837. break;
  1838. case UNSIGNED_ZONE_SIGN:
  1839. sign = 1;
  1840. break;
  1841. case NEGATIVE_ZONE_SIGN:
  1842. sign = -1;
  1843. break;
  1844. default:
  1845. return 0;
  1846. }
  1847. } else { /* no sign */
  1848. sign = 1;
  1849. }
  1850. String svalue = new String(buf, enc);
  1851. number[0] = svalue;
  1852. data[0] = Long.parseLong(svalue) * sign;
  1853. return got;
  1854. }
  1855. /**
  1856. * Read (Cobol numeric) data from the input stream and interpret it as a
  1857. * binary value. The represenation returned DOES NOT HAVE DECIMAL POINT
  1858. * INFORMATION.
  1859. *
  1860. * @param stream Data inlet
  1861. * @param data Array to hold the read value; only the first element of the
  1862. * array is used.
  1863. * @param count The number of bytes to process from the stream as a binary
  1864. * value
  1865. *
  1866. * @return The number of bytes consumed to process the data, or -1 if there
  1867. * was no or insufficient data to process
  1868. *
  1869. * @throws IOException if an I/O error occurs attempting to read
  1870. * from the input stream
  1871. */
  1872. private static int readBinaryNumber(InputStream stream,
  1873. long[] data,
  1874. int count)
  1875. throws IOException {
  1876. BigInteger value;
  1877. byte[] bytes = new byte[count];
  1878. int got = 0;
  1879. /* get value (number) */
  1880. got = stream.read(bytes);
  1881. if (got != count) {
  1882. return (-1);
  1883. }
  1884. value = new BigInteger(bytes);
  1885. data[ 0] = value.longValue();
  1886. return got;
  1887. }
  1888. /**
  1889. * Read (Cobol numeric) data from the input stream and interpret it as a
  1890. * packed-decimal value.
  1891. *
  1892. * @param stream Data inlet
  1893. * @param data Array to hold the read value; only the first element of the
  1894. * array is used.
  1895. * @param count The number of bytes to process from the stream as a
  1896. * packed-decimal value
  1897. *
  1898. * @return The number of bytes consumed to process the data, or -1 if there
  1899. * was no or insufficient data to process
  1900. *
  1901. * @throws IOException if an I/O error occurs attempting to read
  1902. * from the input stream
  1903. */
  1904. private static int readPackedNumber(InputStream stream,
  1905. long[] data,
  1906. int count)
  1907. throws IOException {
  1908. byte[] bytes = new byte[count];
  1909. int got = 0;
  1910. /* get value (number) */
  1911. got = stream.read(bytes);
  1912. if (got != count) {
  1913. Message msg = MessageCatalog.getMessage("CCCR4014");
  1914. cErrorMgr.log(ErrorManager.Severity.ERROR,
  1915. null,
  1916. msg.formatText(new Object[]{
  1917. "COMP-3",
  1918. String.valueOf(count),
  1919. String.valueOf(Math.max(0, got))
  1920. }));
  1921. return (-1);
  1922. }
  1923. /* get sign */
  1924. byte signByte =
  1925. (byte) (bytes[count - 1] & PACKED_NYBBLE_SIGN_BYTEMASK);
  1926. /* decode value */
  1927. count -= 1;
  1928. long value = 0L;
  1929. for (int i = 0; i < count; i++) {
  1930. int digit = (((int) bytes[i]) & 0x000000F0) >> 4;
  1931. if (digit < 0 || digit > 9) {
  1932. Message msg = MessageCatalog.getMessage("CCCR4019");
  1933. cErrorMgr.log(ErrorManager.Severity.ERROR,
  1934. null,
  1935. msg.formatText(new Object[]{
  1936. "COMP-3",
  1937. String.valueOf((char) digit),
  1938. String.valueOf(digit),
  1939. String.valueOf(i),
  1940. Integer.toHexString(bytes[i])
  1941. }));
  1942. return (-1);
  1943. }
  1944. value += digit;
  1945. value *= 10;
  1946. digit = (int) bytes[i] & 0x0000000F;
  1947. if (digit < 0 || digit > 9) {
  1948. Message msg = MessageCatalog.getMessage("CCCR4020");
  1949. cErrorMgr.log(ErrorManager.Severity.ERROR,
  1950. null,
  1951. msg.formatText(new Object[]{
  1952. "COMP-3",
  1953. String.valueOf((char) digit),
  1954. String.valueOf(digit),
  1955. String.valueOf(i),
  1956. Integer.toHexString(bytes[i])
  1957. }));
  1958. return (-1);
  1959. }
  1960. value += digit;
  1961. value *= 10;
  1962. }
  1963. int digit = (((int) bytes[count]) & 0x000000F0) >> 4;
  1964. if (digit < 0 || digit > 9) {
  1965. Message msg = MessageCatalog.getMessage("CCCR4019");
  1966. cErrorMgr.log(ErrorManager.Severity.ERROR,
  1967. null,
  1968. msg.formatText(new Object[]{
  1969. "COMP-3",
  1970. String.valueOf((char) digit),
  1971. String.valueOf(digit),
  1972. String.valueOf(count),
  1973. Integer.toHexString(bytes[count])
  1974. }));
  1975. return (-1);
  1976. }
  1977. value += digit;
  1978. if (signByte == NEGATIVE_PACK_SIGN) {
  1979. value *= (-1);
  1980. }
  1981. data[ 0] = value;
  1982. return got;
  1983. }
  1984. // collapse all contiguous picture symbols into X(n) notation
  1985. private static String compressedPic(String pic) {
  1986. StringBuffer buf = new StringBuffer(pic);
  1987. char lastSymbol = buf.charAt(0);
  1988. int occurs = 1;
  1989. for (int i = 1; i < buf.length(); i++) {
  1990. char symbol = buf.charAt(i);
  1991. // External floating point, e.g., 9V9E+99
  1992. // On detecting it, stop trying to compress the pic
  1993. if (symbol == 'E') {
  1994. break;
  1995. }
  1996. int lastpos = buf.length() - 1;
  1997. if (lastSymbol == symbol) {
  1998. occurs++;
  1999. }
  2000. if (lastSymbol != symbol || i == lastpos) {
  2001. if (occurs > 1) {
  2002. int cutpos =
  2003. i - occurs + 1 + (lastSymbol == symbol ? 1 : 0);
  2004. buf.delete(cutpos, (lastSymbol == symbol ? i + 1 : i));
  2005. buf.insert(cutpos, ')');
  2006. buf.insert(cutpos, occurs);
  2007. buf.insert(cutpos, '(');
  2008. occurs = 1;
  2009. i = buf.indexOf(")", cutpos) + 1;
  2010. }
  2011. lastSymbol = symbol;
  2012. }
  2013. }
  2014. return buf.toString();
  2015. }
  2016. // copies the value in buf to a new array, sans sign
  2017. private static byte[] stripSign(boolean leadingSign,
  2018. byte[] buf,
  2019. byte[] sign) {
  2020. byte[] newbuf = new byte[buf.length - sign.length];
  2021. System.arraycopy(buf,
  2022. (leadingSign ? sign.length : 0),
  2023. newbuf,
  2024. 0,
  2025. newbuf.length);
  2026. return newbuf;
  2027. }
  2028. // Evaluates what sign the value in buf contains; if it's positive signed,
  2029. // returns the plus array, otherwise returns the minus array
  2030. private static byte[] getSeparateSign(boolean leadingSign,
  2031. byte[] buf,
  2032. byte[] plus,
  2033. byte[] minus) {
  2034. int maxsignlen = Math.max(plus.length, minus.length);
  2035. if (buf.length <= maxsignlen) {
  2036. return null;
  2037. }
  2038. byte[] bbuf = new byte[maxsignlen];
  2039. if (leadingSign) {
  2040. System.arraycopy(buf,
  2041. 0,
  2042. bbuf,
  2043. 0,
  2044. maxsignlen);
  2045. } else {
  2046. int startOffset = Math.max(0, buf.length - maxsignlen - 1);
  2047. int endOffset = Math.min(buf.length - startOffset, maxsignlen);
  2048. System.arraycopy(buf, startOffset, bbuf, 0, endOffset);
  2049. }
  2050. if (Arrays.equals(plus, bbuf)) {
  2051. return plus;
  2052. } else if (Arrays.equals(minus, bbuf)) {
  2053. return minus;
  2054. } else {
  2055. return null;
  2056. }
  2057. }
  2058. // Returns the bytes representation of a string of characters in the
  2059. // requested encoding
  2060. private static byte[] charsToBytes(char[] chars, String enc)
  2061. throws CharacterCodingException,
  2062. UnsupportedEncodingException {
  2063. Charset Lcs;
  2064. byte[] Ldecoded;
  2065. try {
  2066. Lcs = Charset.forName(enc);
  2067. } catch (Exception e) {
  2068. Lcs = null;
  2069. }
  2070. if (Lcs != null) {
  2071. ByteArrayOutputStream Lbs;
  2072. ByteBuffer Lbb;
  2073. Lbb = Lcs.newEncoder().encode(CharBuffer.wrap(chars));
  2074. Lbs = new ByteArrayOutputStream(Lbb.limit() - Lbb.position());
  2075. while (Lbb.hasRemaining()) {
  2076. Lbs.write(Lbb.get());
  2077. }
  2078. Ldecoded = Lbs.toByteArray();
  2079. } else {
  2080. String Lchars = new String(chars);
  2081. Ldecoded = Lchars.getBytes(enc);
  2082. }
  2083. return Ldecoded;
  2084. }
  2085. // Create InputStreamReader wrapped around the stream, using the
  2086. // specified encoding
  2087. private static InputStreamReader createReader(InputStream stream,
  2088. String enc)
  2089. throws UnsupportedEncodingException {
  2090. InputStreamReader reader = new InputStreamReader(stream, enc);
  2091. return reader;
  2092. }
  2093. /**
  2094. * Truncate or pad a numeric value according to rules for internal decimal,
  2095. * zoned decimal, and binary numeric items. The results of any truncation or
  2096. * padding are reflected in the value, which is supplied to this method in a
  2097. * buffer.
  2098. *
  2099. * @param value Text buffer containing the unscaled value
  2100. * @param scale Scale (number of digits to the right of the
  2101. * decimal point) of the numeric value contained in
  2102. * <code>value</code>
  2103. * @param digitsPic Number of digits for this item
  2104. * @param scalePic Position of the decimal point for this item
  2105. * @param scalingPositions Scaling positions for this item
  2106. *
  2107. * @return The scale of the value in the buffer, after it is fitted in the
  2108. * picture
  2109. *
  2110. * @see #fitToNumericPicture(StringBuffer, int, int, int, int)
  2111. */
  2112. private static int fitToNumericPicture(StringBuffer value,
  2113. int scale,
  2114. int digitsPic,
  2115. int scalePic,
  2116. int scalingPositions) {
  2117. final boolean isNegativeValue;
  2118. // remove negative sign if present, it complicates offset calcs
  2119. if (isNegativeValue = (value.charAt(0) == '-')) {
  2120. value.delete(0, 1);
  2121. }
  2122. // Scaling decimal positions (PIC symbol P):
  2123. if (scalingPositions > 0) {
  2124. // Remove side of value not used
  2125. // if scaling to the right of the point, remove digits on left of it
  2126. if (scalePic > 0) {
  2127. int truncsize = Math.max(0, value.length() - scale);
  2128. value.delete(0, truncsize);
  2129. } else {
  2130. value.delete(value.length() - scale, value.length());
  2131. scale = 0;
  2132. }
  2133. if (value.length() == 0) {
  2134. value.append('0');
  2135. }
  2136. // Pad the value to match the width of the picture
  2137. // including the implied scaling positions
  2138. int addsize = Math.max(0,
  2139. (digitsPic + scalingPositions) - value.length());
  2140. if (scalePic > 0) {
  2141. for (int i = 0; i < addsize; i++) {
  2142. value.append('0');
  2143. }
  2144. scale += addsize;
  2145. } else {
  2146. for (int i = 0; i < addsize; i++) {
  2147. value.insert(0, '0');
  2148. }
  2149. }
  2150. // Truncate the value
  2151. int truncsize = Math.max(
  2152. 0, value.length() - (digitsPic + scalingPositions));
  2153. if (scalePic > 0) {
  2154. value.delete(value.length() - truncsize, value.length());
  2155. scale -= truncsize;
  2156. } else {
  2157. value.delete(0, truncsize);
  2158. }
  2159. // Value's digits are all lined up to the picture's
  2160. // Chop off all the digits corresponding to the scaling positions
  2161. if (scalePic > 0) {
  2162. value.delete(0, scalingPositions);
  2163. scale -= scalingPositions;
  2164. } else {
  2165. value.delete(value.length() - scalingPositions,
  2166. value.length());
  2167. }
  2168. }
  2169. // No scaling decimal (may or may not have PIC symbol V)
  2170. if (scalingPositions == 0) {
  2171. // Truncate or pad the value
  2172. int diff;
  2173. // non-fraction
  2174. diff = (digitsPic - scalePic) - (value.length() - scale);
  2175. if (diff < 1) {
  2176. value.delete(0, Math.abs(diff));
  2177. } else {
  2178. for (int i = 0; i < diff; i++) {
  2179. value.insert(0, '0');
  2180. }
  2181. }
  2182. // fraction
  2183. diff = scalePic - scale;
  2184. if (diff < 0) {
  2185. value.delete(value.length() + diff, value.length());
  2186. scale += diff;
  2187. } else {
  2188. scale += diff;
  2189. for (int i = 0; i < diff; i++) {
  2190. value.append('0');
  2191. }
  2192. }
  2193. }
  2194. // restore negative sign if it was removed
  2195. if (isNegativeValue) {
  2196. value.insert(0, '-');
  2197. }
  2198. return scale;
  2199. }
  2200. /**
  2201. * Truncate or pad a numeric value according to rules for native binary
  2202. * (COMP-5) items. The results of any truncation or padding are reflected in
  2203. * the value, which is supplied to this method in a buffer.
  2204. *
  2205. * @param value Text buffer containing the unscaled value
  2206. * @param scale Scale (number of digits to the right of the
  2207. * decimal point) of the numeric value contained in
  2208. * <code>value</code>
  2209. * @param digitsPic Number of digits for this item
  2210. * @param scalePic Position of the decimal point for this item
  2211. * @param scalingPositions Scaling positions for this item
  2212. * @param size Storage capacity (in bytes) for this item
  2213. *
  2214. * @return The scale of the value in the buffer, after it is fitted in the
  2215. * picture
  2216. *
  2217. * @see #fitToNumericPicture(StringBuffer, int, int, int, int)
  2218. */
  2219. private static int fitToNumericNativePicture(StringBuffer value,
  2220. int scale,
  2221. int digitsPic,
  2222. int scalePic,
  2223. int scalingPositions,
  2224. int size) {
  2225. final boolean isNegativeValue;
  2226. // remove negative sign if present, it complicates offset calcs
  2227. if (isNegativeValue = (value.charAt(0) == '-')) {
  2228. value.delete(0, 1);
  2229. }
  2230. // Scaling decimal positions (PIC symbol P):
  2231. if (scalingPositions > 0) {
  2232. // Remove side of value not used
  2233. // if scaling to the right of the point, remove digits on left of it
  2234. if (scalePic > 0) {
  2235. int truncsize = Math.max(0, value.length() - scale);
  2236. value.delete(0, truncsize);
  2237. } else {
  2238. value.delete(value.length() - scale, value.length());
  2239. scale = 0;
  2240. }
  2241. if (value.length() == 0) {
  2242. value.append('0');
  2243. }
  2244. // Pad the value to match the width of the picture
  2245. // including the implied scaling positions
  2246. int addsize = Math.max(
  2247. 0, (digitsPic + scalingPositions) - value.length());
  2248. if (scalePic > 0) {
  2249. for (int i = 0; i < addsize; i++) {
  2250. value.append('0');
  2251. }
  2252. scale += addsize;
  2253. } else {
  2254. for (int i = 0; i < addsize; i++) {
  2255. value.insert(0, '0');
  2256. }
  2257. }
  2258. // Value's digits are all lined up to the picture's
  2259. // Chop off all the digits corresponding to the scaling positions
  2260. if (scalePic > 0) {
  2261. value.delete(0, scalingPositions);
  2262. scale -= scalingPositions;
  2263. } else {
  2264. value.delete(value.length() - scalingPositions,
  2265. value.length());
  2266. }
  2267. }
  2268. // No scaling decimal (may or may not have PIC symbol V)
  2269. if (scalingPositions == 0) {
  2270. // Truncate or pad the value
  2271. int diff;
  2272. // non-fraction
  2273. diff = (digitsPic - scalePic) - (value.length() - scale);
  2274. if (diff < 1) {
  2275. value.delete(0, Math.abs(diff));
  2276. } else {
  2277. for (int i = 0; i < diff; i++) {
  2278. value.insert(0, '0');
  2279. }
  2280. }
  2281. // fraction
  2282. diff = scalePic - scale;
  2283. if (diff < 0) {
  2284. value.delete(value.length() + diff, value.length());
  2285. scale += diff;
  2286. } else {
  2287. scale += diff;
  2288. for (int i = 0; i < diff; i++) {
  2289. value.append('0');
  2290. }
  2291. }
  2292. }
  2293. // restore negative sign if it was removed
  2294. if (isNegativeValue) {
  2295. value.insert(0, '-');
  2296. }
  2297. // Truncate the unscaled value, if necessary, starting from the most
  2298. // significant byte, to fit it into the storage capacity
  2299. BigInteger unscaledValue = new BigInteger(value.toString());
  2300. byte[] unscaledBytes = unscaledValue.toByteArray();
  2301. if (unscaledBytes.length > size) {
  2302. int diff = unscaledBytes.length - size;
  2303. byte[] truncBytes = new byte[size];
  2304. System.arraycopy(unscaledBytes, diff, truncBytes, 0, size);
  2305. unscaledValue = new BigInteger(truncBytes);
  2306. value.delete(0, value.length());
  2307. value.append(unscaledValue.toString());
  2308. }
  2309. return scale;
  2310. }
  2311. }
  2312. // EOF $RCSfile: CobolDataConverter.java,v $