/ojc-core/encodersl/encoder-coco/src/com/sun/encoder/coco/runtime/CobolDataConverter.java
Java | 2554 lines | 1486 code | 242 blank | 826 comment | 291 complexity | 36f5586553d4da33ef0a550252a543bd MD5 | raw file
Possible License(s): AGPL-3.0
Large files files are truncated, but you can click here to view the full file
- /*
- * BEGIN_HEADER - DO NOT EDIT
- *
- * The contents of this file are subject to the terms
- * of the Common Development and Distribution License
- * (the "License"). You may not use this file except
- * in compliance with the License.
- *
- * You can obtain a copy of the license at
- * https://open-jbi-components.dev.java.net/public/CDDLv1.0.html.
- * See the License for the specific language governing
- * permissions and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL
- * HEADER in each file and include the License file at
- * https://open-jbi-components.dev.java.net/public/CDDLv1.0.html.
- * If applicable add the following below this CDDL HEADER,
- * with the fields enclosed by brackets "[]" replaced with
- * your own identifying information: Portions Copyright
- * [year] [name of copyright owner]
- */
- /*
- * @(#)CobolDataConverter.java
- *
- * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved.
- *
- * END_HEADER - DO NOT EDIT
- */
- package com.sun.encoder.coco.runtime;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.UnsupportedEncodingException;
- import java.math.BigDecimal;
- import java.math.BigInteger;
- import java.nio.ByteBuffer;
- import java.nio.CharBuffer;
- import java.nio.charset.CharacterCodingException;
- import java.nio.charset.Charset;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Map;
- import com.sun.encoder.coco.runtime.messages.ErrorManager;
- import com.sun.encoder.coco.runtime.messages.Message;
- import com.sun.encoder.coco.runtime.messages.MessageCatalog;
- /**
- * Principal class for converting between Copybook data and Java data
- * reprseentations.
- *
- * @author Noel Ang
- * @version $Revision: 1.4 $
- */
- public class CobolDataConverter {
- private static final int MAX_EXFLOAT_EXPONENT = 99;
- private static final byte LIT_BYTE = (byte) 0xFF;
- private static final byte ZONE_NYBBLE_SIGN_BYTEMASK = (byte) 0xF0;
- private static final byte ZONE_NYBBLE_VALUE_BYTEMASK = (byte) 0x0F;
- private static final byte PACKED_NYBBLE_SIGN_BYTEMASK = (byte) 0x0F;
- private static final byte POSITIVE_ZONE_SIGN = (byte) 0xC0;
- private static final byte NEGATIVE_ZONE_SIGN = (byte) 0xD0;
- private static final byte UNSIGNED_ZONE_SIGN = (byte) 0xF0;
- private static final byte POSITIVE_PACK_SIGN = 0x0C;
- private static final byte NEGATIVE_PACK_SIGN = 0x0D;
- private static final byte UNSIGNED_PACK_SIGN = 0x0F;
- private static final byte[] DBCSSPACE = {0x40, 0x40};
- private static final char[] SPACE = {' '};
- private static final char[] PLUS = {'+'};
- private static final char[] MINUS = {'-'};
- private static final Map<String, byte[]> mSpaceEncodings =
- Collections.synchronizedMap(new HashMap<String, byte[]>());
- private static final Map<String, byte[]> mPlusEncodings =
- Collections.synchronizedMap(new HashMap<String, byte[]>());
- private static final Map<String, byte[]> mMinusEncodings =
- Collections.synchronizedMap(new HashMap<String, byte[]>());
- private static final ErrorManager cErrorMgr =
- ErrorManager.getManager("OpenESB.encoder.COBOLCopybook." + CobolDataConverter.class.getName());
- private CobolDataConverter() {
- }
- /**
- * Write a value as a Cobol display usage item. Use for alphabetic,
- * alphanumeric, alphanumeric-edited, and numeric-edited items. If the
- * value's size is less than the item size, it is padded with trailing space
- * characters.
- *
- * @param outStream Outlet for data
- * @param data The value to write
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- * @param enc Encoding to use for the output
- *
- * @throws IOException if an I/O error occurs in writing the value,
- * or if the supplied writer does not use the
- * required character encoding
- */
- public static void encodeToDisplay(OutputStream outStream,
- String data,
- CobolCharacteristics specs,
- String enc)
- throws IOException {
- final int size;
- OutputStreamWriter writer = new OutputStreamWriter(outStream, enc);
- size = specs.getSize();
- if (data.length() > size) {
- Message msg = MessageCatalog.getMessage("CCCR3002");
- cErrorMgr.log(ErrorManager.Severity.WARN,
- null,
- msg.formatText(new Object[]{data, String.valueOf(size)}));
- data = data.substring(0, size);
- }
- writer.write(data);
- writer.flush();
- for (int i = 0, pad = size - data.length(); i < pad; i++) {
- writer.write(' ');
- }
- }
- /**
- * Write a value as an external floating point (display usage) item.
- *
- * @param outStream Outlet for data
- * @param data Value to write
- * @param picture Cobol picture string associated with the item
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- * @param enc Encoding to use for the output
- *
- * @throws IOException if an I/O error occurs in writing the value,
- * or if the supplied writer does not use the
- * required character encoding
- * @throws ArithmeticException if the data exponent is too large
- */
- public static void encodeToExternalFloat(OutputStream outStream,
- BigDecimal data,
- String picture,
- CobolCharacteristics specs,
- String enc)
- throws IOException {
- final StringBuffer value = new StringBuffer(data.unscaledValue().toString());
- final int scalePic = specs.getDecimalPosition();
- int scale = data.scale();
- int exponent = 0 - scale;
- int digitsPic = 0;
- int digits = value.length();
- OutputStreamWriter writer = new OutputStreamWriter(outStream, enc);
- /* calc picture's digit requirements */
- digitsPic = 0;
- for (int i = 0;
- i < picture.length() && picture.charAt(i) != 'E'; i++) {
- if (picture.charAt(i) == '9') {
- digitsPic++;
- }
- }
- /* remove sign from value */
- if (data.signum() == -1) {
- value.delete(0, 1);
- digits--;
- }
- // truncate if needed
- if (digitsPic < digits) {
- int snip = digits - digitsPic;
- value.delete(digits - snip, digits);
- scale = Math.max(0, scale - snip);
- exponent += snip;
- digits = value.length();
- }
- // pad if needed
- if (digits < digitsPic) {
- int snip = digitsPic - digits;
- for (int i = 0; i < snip; i++) {
- value.append('0');
- }
- scale += (scale > 0 ? snip : 0);
- exponent -= snip;
- digits = value.length();
- }
- // reconcile data and picture scales;
- // compensate for their decimal positions' misalignment
- exponent = scalePic + exponent;
- // adjust exponent now that all padding, truncation, and
- // decimal point insertion are completed
- int dotLoc = picture.indexOf('.');
- boolean haveExplicitDecimal = (dotLoc != -1);
- // picture offset - 1 == value offset
- if (haveExplicitDecimal) {
- value.insert(dotLoc - 1, '.');
- }
- // exponent range check
- if (Math.abs(exponent) > MAX_EXFLOAT_EXPONENT) {
- Message msg = MessageCatalog.getMessage("CCCR4009");
- String err = msg.formatText(new Object[]{
- String.valueOf(exponent)
- });
- cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
- throw new ArithmeticException(err);
- }
- // write sign
- boolean negValue = (data.signum() == -1);
- boolean needSignificandSign = !Character.isDigit(picture.charAt(0));
- boolean explicitSignificandSign = needSignificandSign && picture.charAt(0) == '+';
- if (needSignificandSign) {
- if (negValue && new BigDecimal(
- value.toString()).unscaledValue().intValue() != 0) {
- writer.write('-');
- } else if (explicitSignificandSign) {
- writer.write('+');
- }
- }
- // write significand and exponent
- boolean explicitExponentSign = picture.charAt(picture.indexOf('E') + 1) == '+';
- writer.write(value.toString() + 'E');
- if (exponent < 0) {
- writer.write('-');
- } else if (explicitExponentSign) {
- writer.write('+');
- }
- exponent = Math.abs(exponent);
- if (exponent < 10) {
- writer.write('0');
- }
- writer.write(Integer.toString(exponent));
- writer.flush();
- }
- /**
- * Write a value as a Cobol COMP-1 usage, floating point item.
- *
- * @param stream Outlet for data
- * @param val The value to write
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToFloat(OutputStream stream,
- float val)
- throws IOException {
- int value = Float.floatToIntBits(val);
- byte[] bytes = new byte[]{
- (byte) ((value & 0xFF000000) >> 24),
- (byte) ((value & 0x00FF0000) >> 16),
- (byte) ((value & 0x0000FF00) >> 8),
- (byte) (value & 0x000000FF)
- };
- stream.write(bytes, 0, bytes.length);
- stream.flush();
- }
- /**
- * Write a value as a Cobol COMP-2 usage, floating point item.
- *
- * @param stream Outlet for data
- * @param val The value to write
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToDouble(OutputStream stream,
- double val)
- throws IOException {
- long value = Double.doubleToLongBits(val);
- byte[] bytes = new byte[]{
- (byte) ((value & 0xFF00000000000000L) >> 56),
- (byte) ((value & 0x00FF000000000000L) >> 48),
- (byte) ((value & 0x0000FF0000000000L) >> 40),
- (byte) ((value & 0x000000FF00000000L) >> 32),
- (byte) ((value & 0x00000000FF000000L) >> 24),
- (byte) ((value & 0x0000000000FF0000L) >> 16),
- (byte) ((value & 0x000000000000FF00L) >> 8),
- (byte) (value & 0x00000000000000FFL)
- };
- stream.write(bytes, 0, bytes.length);
- stream.flush();
- }
- /**
- * Write a value as a Cobol display usage, zoned item. Use for external
- * decimal (zoned) items.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param picture Cobol item picture for the value
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- * @param enc Encoding to use for the output
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToZoned(OutputStream stream,
- BigDecimal data,
- String picture,
- CobolCharacteristics specs,
- String enc)
- throws IOException {
- final StringBuffer value = new StringBuffer(data.unscaledValue().toString());
- final boolean needSign = specs.isSigned();
- final boolean separateSign = specs.isSignSeparate();
- final boolean leadingSign = specs.isSignLeading();
- final int scalePic = specs.getDecimalPosition();
- final int scalingPositions = specs.getDecimalScalingPositions();
- int digitsPic;
- byte signByte = 0;
- int scale;
- // Count number of digits in the picture
- digitsPic = 0;
- for (int i = 0, len = picture.length(); i < len; i++) {
- if (picture.charAt(i) == '9') {
- digitsPic += 1;
- }
- }
- // Scale value to its picture
- scale = data.scale();
- scale =
- fitToNumericPicture(value,
- scale,
- digitsPic,
- scalePic,
- scalingPositions);
- // Remove negative sign for convenience
- if (data.signum() == -1) {
- value.delete(0, 1);
- }
- // Add sign
- if (needSign) {
- // Two ways to add it:
- // Prepend/append if separate sign requested,
- if (separateSign) {
- if (leadingSign) {
- switch (data.signum()) {
- case -1:
- value.insert(0, '-');
- break;
- case 0:
- value.insert(0, '+');
- break;
- case 1:
- value.insert(0, '+');
- break;
- default:
- }
- } else {
- switch (data.signum()) {
- case -1:
- value.append('-');
- break;
- case 0:
- value.append('+');
- break;
- case 1:
- value.append('+');
- break;
- default:
- }
- }
- } // Or add it to first/last digit's fist nybble if non-separate sign
- else {
- // for a non-separate, leading sign, embed it into the
- // first nybble of the first digit
- if (leadingSign) {
- signByte = (byte) Character.digit(value.charAt(0), 10);
- value.delete(0, 1);
- } // for a non-separate, trailing sign, embed it into the
- // first nybble of the last digit
- else {
- int last = value.length() - 1;
- signByte =
- (byte) Character.digit(value.charAt(last), 10);
- value.delete(last, last + 1);
- }
- switch (data.signum()) {
- case -1:
- signByte |= NEGATIVE_ZONE_SIGN;
- break;
- case 0:
- signByte |= POSITIVE_ZONE_SIGN;
- break;
- case 1:
- signByte |= POSITIVE_ZONE_SIGN;
- break;
- default:
- }
- }
- }
- // Write the data out
- if (needSign && !separateSign && leadingSign) {
- stream.write((int) signByte);
- stream.flush();
- }
- OutputStreamWriter writer = new OutputStreamWriter(stream, enc);
- writer.write(value.toString());
- writer.flush();
- if (needSign && !separateSign && !leadingSign) {
- stream.write((int) signByte);
- stream.flush();
- }
- }
- /**
- * Write a value as a Cobol display usage, zoned item. Use for internal
- * decimal (zoned) items. If the value's size is less than the item's size,
- * the item is padded with leading zeroes.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param picture Cobol item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- * @param enc Encoding to use for the output
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToZoned(OutputStream stream,
- long data,
- String picture,
- CobolCharacteristics specs,
- String enc)
- throws IOException {
- // Legacy behaviour: when setting zoned values
- // (which may have scale > 0) using long or int (which cannot store
- // information about scale > 0), treat the input not as an integral,
- // but as floating point value. This means applying scale info
- // of the item to the input after it is converted to a BigDecimal.
- //
- // A proposal to clean up this mess by removing int- and long-based
- // getters and setters for zoned items has been rejected on grounds of
- // backward compatibility.
- BigDecimal decimal;
- decimal = new BigDecimal(Long.toString(data));
- decimal.movePointLeft(specs.getDecimalPosition());
- encodeToZoned(stream, decimal, picture, specs, enc);
- }
- /**
- * Write a value as a Cobol display usage, zoned item. Use for internal
- * decimal (zoned) items. If the value's size is less than the item's size,
- * the item is padded with leading zeroes.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param picture Cobol item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- * @param enc Encoding to use for the output
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToZoned(OutputStream stream,
- int data,
- String picture,
- CobolCharacteristics specs,
- String enc)
- throws IOException {
- // Legacy behaviour: when setting zoned values
- // (which may have scale > 0) using long or int (which cannot store
- // information about scale > 0), treat the input not as an integral,
- // but as floating point value. This means applying scale info
- // of the item to the input after it is converted to a BigDecimal.
- //
- // A proposal to clean up this mess by removing int- and long-based
- // getters and setters for zoned items has been rejected on grounds of
- // backward compatibility.
- BigDecimal decimal;
- decimal = new BigDecimal(Integer.toString(data));
- decimal.movePointLeft(specs.getDecimalPosition());
- encodeToZoned(stream, decimal, picture, specs, enc);
- }
- /**
- * Write a value as a Cobol display-1 usage item. Use for DBCS items. If the
- * value's size is less than the item size, the item is padded with trailing
- * spaces.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param size Item size
- *
- * @throws IllegalArgumentException if the data size exceeds the item size,
- * or the number of bytes in the data is
- * not a multiple of 2.
- * @throws IOException if an I/O error occurs in writing the
- * value
- */
- public static void encodeToDbcs(OutputStream stream,
- byte[] data,
- int size)
- throws IOException {
- int length = data.length;
- if ((length % 2) != 0) {
- Message msg = MessageCatalog.getMessage("CCCR4010");
- String err = msg.formatText(new Object[]{
- String.valueOf(length)
- });
- cErrorMgr.log(ErrorManager.Severity.ERROR,
- null,
- err);
- throw new IllegalArgumentException(err);
- }
- if (length > size) {
- Message msg = MessageCatalog.getMessage("CCCR3003");
- cErrorMgr.log(ErrorManager.Severity.WARN,
- null,
- msg.formatText(new Object[]{
- String.valueOf(length),
- String.valueOf(size)
- }));
- byte[] copy = new byte[size];
- System.arraycopy(data, 0, copy, 0, size);
- data = copy;
- }
- stream.write(data);
- for (int i = 0, pad = (size - length) / DBCSSPACE.length;
- i < pad; i++) {
- stream.write(DBCSSPACE, 0, DBCSSPACE.length);
- }
- stream.flush();
- }
- /**
- * Write a value as a Cobol 2-, 4- or 8-byte binary (usage BINARY/COMP)
- * item. The binary item width used depends on the number of digits in the
- * picture, as specified in IBM Cobol Reference:
- * <p/>
- * <pre>
- * 1-4 digits: 2 bytes
- * 5-9 digits: 4 bytes
- * 10-18 digits: 8 bytes
- * </pre>
- * <p/>
- * However, note that the width computation <em>is not derived from the
- * supplied picture</em>, it is taken from the pre-computed value encoded in
- * the characteristics bit vector <code>parms</code>.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param picture Cobol item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToBinary(OutputStream stream,
- BigDecimal data,
- String picture,
- CobolCharacteristics specs)
- throws IOException {
- final StringBuffer svalue = new StringBuffer(data.unscaledValue().toString());
- final int size = specs.getSize();
- final boolean signed = specs.isSigned();
- final int scalePic = specs.getDecimalPosition();
- final int scalingPositions = specs.getDecimalScalingPositions();
- int digitsPic;
- BigInteger value;
- byte[] valueBytes;
- int valueLen;
- // Remove sign info from value if item is unsigned
- if (!signed && data.signum() == -1) {
- svalue.delete(0, 1);
- }
- // Count number of digits in the picture
- digitsPic = 0;
- for (int i = 0, len = picture.length(); i < len; i++) {
- if (picture.charAt(i) == '9') {
- digitsPic += 1;
- }
- }
- fitToNumericPicture(svalue,
- data.scale(),
- digitsPic,
- scalePic,
- scalingPositions);
- value = new BigInteger(svalue.toString());
- valueBytes = value.toByteArray();
- valueLen = valueBytes.length;
- // Add byte padding
- if (size - valueLen > 0) {
- int padding = size - valueLen;
- byte[] newbytes = new byte[size];
- byte pad;
- int pos = 0;
- if (signed && data.signum() == -1) {
- pad = LIT_BYTE;
- } else {
- pad = 0;
- }
- while (padding-- > 0) {
- newbytes[pos] = pad;
- pos += 1;
- }
- for (int i = 0; i < valueLen; i++) {
- newbytes[pos] = valueBytes[i];
- pos += 1;
- }
- valueBytes = newbytes;
- }
- stream.write(valueBytes);
- }
- /**
- * Write a value as a Cobol 2-, 4- or 8-byte native binary (usage COMP5 or
- * INDEX) item. The binary item width used depends on the number of digits
- * in the picture, as specified in IBM Cobol Reference:
- * <p/>
- * <pre>
- * 1-4 digits: 2 bytes
- * 5-9 digits: 4 bytes
- * 10-18 digits: 8 bytes
- * </pre>
- * <p/>
- * However, note that the width computation <em>is not derived from the
- * supplied picture</em>, it is taken from the pre-computed value encoded in
- * the characteristics bit vector <code>parms</code>.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param picture Cobol item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToNativeBinary(OutputStream stream,
- BigDecimal data,
- String picture,
- CobolCharacteristics specs)
- throws IOException {
- final StringBuffer svalue = new StringBuffer(data.unscaledValue().toString());
- final int size = specs.getSize();
- final boolean signed = specs.isSigned();
- final int scalePic = specs.getDecimalPosition();
- final int scalingPositions = specs.getDecimalScalingPositions();
- int digitsPic;
- BigInteger value;
- byte[] valueBytes;
- int valueLen;
- // Remove sign info from value if item is unsigned
- if (!signed && data.signum() == -1) {
- svalue.delete(0, 1);
- }
- // Count number of digits in the picture
- digitsPic = 0;
- for (int i = 0, len = picture.length(); i < len; i++) {
- if (picture.charAt(i) == '9') {
- digitsPic += 1;
- }
- }
- fitToNumericNativePicture(svalue,
- data.scale(),
- digitsPic,
- scalePic,
- scalingPositions,
- size);
- value = new BigInteger(svalue.toString());
- valueBytes = value.toByteArray();
- valueLen = valueBytes.length;
- // Add byte padding
- if (size - valueLen > 0) {
- int padding = size - valueLen;
- byte[] newbytes = new byte[size];
- byte pad;
- int pos = 0;
- if (signed && value.signum() == -1) {
- pad = LIT_BYTE;
- } else {
- pad = 0;
- }
- while (padding-- > 0) {
- newbytes[pos] = pad;
- pos += 1;
- }
- for (int i = 0; i < valueLen; i++) {
- newbytes[pos] = valueBytes[i];
- pos += 1;
- }
- valueBytes = newbytes;
- }
- stream.write(valueBytes);
- }
- /**
- * Write a value as a Cobol 2-, 4- or 8-byte binary item (usage
- * BINARY/COMP). Delegates to
- * {@link #convertToBinary(OutputStream, BigDecimal, String, String)}
- * so see that method for additional details.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param picture Cobol item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToBinary(OutputStream stream,
- long data,
- String picture,
- CobolCharacteristics specs)
- throws IOException {
- // Legacy behaviour: when setting binary values
- // (which may have scale > 0) using long or int (which cannot store
- // information about scale > 0), treat the input not as an integral,
- // but as floating point value. This means applying scale info
- // of the item to the input after it is converted to a BigDecimal.
- //
- // A proposal to clean up this mess by removing int- and long-based
- // getters and setters for binary items has been rejected on grounds of
- // backward compatibility.
- BigDecimal decimal;
- decimal = new BigDecimal(Long.toString(data));
- decimal.movePointLeft(specs.getDecimalPosition());
- encodeToBinary(stream, decimal, picture, specs);
- }
- /**
- * Write a value as a Cobol 2-, 4- or 8-byte binary item (usage
- * BINARY/COMP). Delegates to
- * {@link #convertToBinary(OutputStream, BigDecimal, String, String)}
- * so see that method for additional details.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param picture Cobol item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToBinary(OutputStream stream,
- int data,
- String picture,
- CobolCharacteristics specs)
- throws IOException {
- // Legacy behaviour: when setting binary values
- // (which may have scale > 0) using long or int (which cannot store
- // information about scale > 0), treat the input not as an integral,
- // but as floating point value. This means applying scale info
- // of the item to the input after it is converted to a BigDecimal.
- //
- // A proposal to clean up this mess by removing int- and long-based
- // getters and setters for binary items has been rejected on grounds of
- // backward compatibility.
- BigDecimal decimal;
- decimal = new BigDecimal(Integer.toString(data));
- decimal.movePointLeft(specs.getDecimalPosition());
- encodeToBinary(stream, decimal, picture, specs);
- }
- /**
- * Write a value as a Cobol packed-decimal item. Use for internal decimal
- * items. Unlike the other variants of this method, this one can handle
- * values with non-zero decimal scaling.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param picture Item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToPacked(OutputStream stream,
- BigDecimal data,
- String picture,
- CobolCharacteristics specs)
- throws IOException {
- final StringBuffer value = new StringBuffer(data.unscaledValue().toString());
- final boolean signed = specs.isSigned();
- final int size = specs.getSize();
- final int scalePic = specs.getDecimalPosition();
- final int scalingPositions = specs.getDecimalScalingPositions();
- int digitsPic;
- int scale;
- // calc picture's digit requirements
- digitsPic = 0;
- for (int i = 0; i < picture.length(); i++) {
- if (picture.charAt(i) == '9') {
- digitsPic++;
- }
- }
- // Remove '-' for convenience
- if (data.signum() == -1) {
- value.delete(0, 1);
- }
- // Scale value to its picture
- scale = data.scale();
- scale = fitToNumericPicture(value,
- scale,
- digitsPic,
- scalePic,
- scalingPositions);
- // Packed decimal-specific truncation check
- int maxFitDigits = (size << 1) - 1; // reserve one for the
- //"sign" sign (neg, pos, unsigned)
- if (maxFitDigits < value.length()) {
- Message msg = MessageCatalog.getMessage("CCCR3004");
- String err = msg.formatText(new Object[]{
- data.toString(),
- String.valueOf((value.length() >> 1) + 1),
- String.valueOf(maxFitDigits)
- });
- cErrorMgr.log(ErrorManager.Severity.WARN, null, err);
- // truncate fraction
- if (scale > 0) {
- int truncsize = Math.max(0, scale - scalePic);
- value.delete(value.length() - truncsize, value.length());
- scale -= truncsize;
- }
- // truncate non-fraction
- if (maxFitDigits < value.length()) {
- int truncsize = Math.max(0, (value.length() - scale) - (digitsPic - scalePic));
- value.delete(0, truncsize);
- }
- }
- /*
- * if even number digits, need to add extra byte for sign:
- *
- * decimal = 12345
- * packed = 12|34|5s where s == sign nybble; extra byte not needed
- *
- * decimal = 1234
- * packed = 01|23|4s where s == sign nybble; extra byte needed
- */
- final boolean needPad = ((value.length() % 2) == 0);
- int current = 0;
- if (needPad) {
- byte pad = 0;
- byte digit =
- (byte) Character.digit(value.charAt(current++), 10);
- byte b = (byte) (pad | digit);
- stream.write(b);
- }
- for (int i = current; i < value.length(); i += 2) {
- byte digit1 = (byte) Character.digit(value.charAt(i), 10);
- byte digit2 = 0;
- if (i < (value.length() - 1)) { // if not last digit
- digit2 = (byte) Character.digit(value.charAt(i + 1), 10);
- } else if (!signed) {
- digit2 = UNSIGNED_PACK_SIGN;
- } else if ((new BigInteger(value.toString())).equals(
- BigInteger.ZERO)) {
- digit2 = POSITIVE_PACK_SIGN;
- } // else if ( Integer.valueOf( value.toString() ).intValue() == 0 ) {
- // digit2 = POSITIVE_PACK_SIGN;
- // }
- else {
- switch (data.signum()) {
- case -1:
- digit2 = NEGATIVE_PACK_SIGN;
- break;
- case 0:
- digit2 = POSITIVE_PACK_SIGN;
- break;
- case 1:
- digit2 = POSITIVE_PACK_SIGN;
- break;
- default:
- }
- }
- byte b = (byte) ((digit1 << 4) | digit2);
- stream.write(b);
- }
- }
- /**
- * Write a value as a Cobol packed-decimal item. Use for internal decimal
- * items.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param picture Item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToPacked(OutputStream stream,
- long data,
- String picture,
- CobolCharacteristics specs)
- throws IOException {
- // Legacy behaviour: when setting packed values
- // (which may have scale > 0) using long or int (which cannot store
- // information about scale > 0), treat the input not as an integral,
- // but as floating point value. This means applying scale info
- // of the item to the input after it is converted to a BigDecimal.
- //
- // A proposal to clean up this mess by removing int- and long-based
- // getters and setters for packed items has been rejected on grounds of
- // backward compatibility.
- BigDecimal decimal;
- decimal = new BigDecimal(Long.toString(data));
- decimal.movePointLeft(specs.getDecimalPosition());
- encodeToPacked(stream, decimal, picture, specs);
- }
- /**
- * Write a value as a Cobol packed-decimal item. Use for internal decimal
- * items.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param picture Item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToPacked(OutputStream stream,
- int data,
- String picture,
- CobolCharacteristics specs)
- throws IOException {
- // Legacy behaviour: when setting packed values
- // (which may have scale > 0) using long or int (which cannot store
- // information about scale > 0), treat the input not as an integral,
- // but as floating point value. This means applying scale info
- // of the item to the input after it is converted to a BigDecimal.
- //
- // A proposal to clean up this mess by removing int- and long-based
- // getters and setters for packed items has been rejected on grounds of
- // backward compatibility.
- BigDecimal decimal;
- decimal = new BigDecimal(Integer.toString(data));
- decimal.movePointLeft(specs.getDecimalPosition());
- encodeToPacked(stream, decimal, picture, specs);
- }
- /**
- * Write a value as a Cobol/EBCDIC index (4-byte) item.
- *
- * @param stream Outlet for data
- * @param data The value to write
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- *
- * @throws IOException if an I/O error occurs in writing the value
- */
- public static void encodeToIndex(OutputStream stream,
- int data,
- CobolCharacteristics specs)
- throws IOException {
- BigDecimal value = new BigDecimal(data);
- encodeToNativeBinary(stream, value, "999999999", specs);
- }
- /**
- * Convert the data from the input stream into to a Java integer (int)
- * value. Use for numeric items that can fit 4-byte signed storage.
- *
- * @param stream Data inlet as a byte stream
- * @param picture Cobol item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- * @param enc Encoding to use to read character data
- *
- * @return Data as an int
- *
- * @throws IllegalArgumentException if an int value cannot be produced given
- * the data item information
- * @throws IOException if an I/O error occured in reading
- * input
- */
- public static int decodeToInt(InputStream stream,
- String picture,
- CobolCharacteristics specs,
- String enc)
- throws IOException {
- int result;
- int category = specs.getPicCategory();
- int usage = specs.getUsage();
- if (CobolCharacteristics.PIC_NUM == category) {
- switch (usage) {
- case CobolCharacteristics.USAGE_DISPLAY: {
- String value = readNumberDisplay(stream, specs, enc);
- result = Integer.parseInt(value);
- break;
- }
- case CobolCharacteristics.USAGE_BINARY:
- case CobolCharacteristics.USAGE_COMP:
- case CobolCharacteristics.USAGE_COMP4: {
- BigDecimal value = readNumberBinary(stream, specs);
- result = value.unscaledValue().intValue();
- break;
- }
- case CobolCharacteristics.USAGE_INDEX: {
- BigDecimal value = readNumberBinary(stream, specs);
- result = value.unscaledValue().intValue();
- break;
- }
- case CobolCharacteristics.USAGE_PACKED:
- case CobolCharacteristics.USAGE_COMP3: {
- BigDecimal value = readNumberPacked(stream, specs);
- result = value.unscaledValue().intValue();
- break;
- }
- case CobolCharacteristics.USAGE_COMP5: {
- BigDecimal value = readNumberBinary(stream, specs);
- result = value.unscaledValue().intValue();
- break;
- }
- default:
- Message msg = MessageCatalog.getMessage("CCCR4011");
- String err = msg.toString();
- cErrorMgr.log(ErrorManager.Severity.ERROR,
- null,
- err);
- throw new IllegalArgumentException(err);
- }
- } else {
- Message msg = MessageCatalog.getMessage("CCCR4013");
- String err = msg.formatText(new Object[]{compressedPic(picture)});
- cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
- throw new IllegalArgumentException(err);
- }
- return result;
- }
- /**
- * Convert the data from the input stream into a Java long integer (long)
- * value. Use for numeric items exceeding 4-byte signed storage
- * capability.
- *
- * @param stream Data inlet as a byte stream
- * @param picture Cobol item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- * @param enc Encoding to use to read character data
- *
- * @return Data as an long
- *
- * @throws IllegalArgumentException if a long value cannot be produced given
- * the data item information
- * @throws IOException an I/O error occured in reading input
- */
- public static long decodeTolong(InputStream stream,
- String picture,
- CobolCharacteristics specs,
- String enc)
- throws IOException {
- long result;
- int category = specs.getPicCategory();
- int usage = specs.getUsage();
- if (CobolCharacteristics.PIC_NUM == category) {
- switch (usage) {
- case CobolCharacteristics.USAGE_DISPLAY: {
- String value = readNumberDisplay(stream, specs, enc);
- result = Long.parseLong(value);
- break;
- }
- case CobolCharacteristics.USAGE_BINARY:
- case CobolCharacteristics.USAGE_COMP:
- case CobolCharacteristics.USAGE_COMP4: {
- BigDecimal value = readNumberBinary(stream, specs);
- result = value.unscaledValue().longValue();
- break;
- }
- case CobolCharacteristics.USAGE_PACKED:
- case CobolCharacteristics.USAGE_COMP3: {
- BigDecimal value = readNumberPacked(stream, specs);
- result = value.unscaledValue().longValue();
- break;
- }
- case CobolCharacteristics.USAGE_COMP5: {
- BigDecimal value = readNumberBinary(stream, specs);
- result = value.unscaledValue().longValue();
- break;
- }
- case CobolCharacteristics.USAGE_INDEX: {
- BigDecimal value = readNumberBinary(stream, specs);
- result = value.unscaledValue().longValue();
- break;
- }
- default:
- Message msg = MessageCatalog.getMessage("CCCR4012");
- String err = msg.toString();
- cErrorMgr.log(ErrorManager.Severity.ERROR,
- null,
- err);
- throw new IllegalArgumentException(err);
- }
- } else {
- Message msg = MessageCatalog.getMessage("CCCR4013");
- String err = msg.formatText(new Object[]{compressedPic(picture)});
- cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
- throw new IllegalArgumentException(err);
- }
- return result;
- }
- /**
- * Convert the data from the input stream into a Java float. Use for COMP-1
- * items.
- *
- * @param stream Byte Data inlet
- *
- * @return Data as float
- *
- * @throws IOException if an I/O error occurs while reading the input,
- * including having insufficient data
- */
- public static float decodeToFloat(InputStream stream)
- throws IOException {
- final int NEED = 4;
- final byte[] bytebuf = new byte[NEED];
- int got = stream.read(bytebuf);
- if (got != NEED) {
- Message msg = MessageCatalog.getMessage("CCCR4014");
- String err = msg.formatText(new Object[]{
- "COMP-1",
- String.valueOf(NEED),
- String.valueOf(Math.max(0, got))
- });
- cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
- throw new IOException(err);
- }
- int intBits;
- intBits = (((int) bytebuf[0]) << 24) & 0xFF000000;
- intBits |= (((int) bytebuf[1]) << 16) & 0x00FF0000;
- intBits |= (((int) bytebuf[2]) << 8) & 0x0000FF00;
- intBits |= bytebuf[3] & 0x000000FF;
- return Float.intBitsToFloat(intBits);
- }
- /**
- * Convert the data from the input stream into a Java double. Use for COMP-2
- * items.
- *
- * @param stream Byte Data inlet
- *
- * @return Data as double
- *
- * @throws IOException if an I/O error occurs while reading the input,
- * including having insufficient data
- */
- public static double decodeToDouble(InputStream stream)
- throws IOException {
- final int NEED = 8;
- final byte[] bytebuf = new byte[NEED];
- int got = stream.read(bytebuf);
- if (got != NEED) {
- Message msg = MessageCatalog.getMessage("CCCR4014");
- String err = msg.formatText(new Object[]{
- "COMP-2",
- String.valueOf(NEED),
- String.valueOf(Math.max(0, got))
- });
- cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
- throw new IOException(err);
- }
- long longBits;
- longBits = (((long) bytebuf[0]) << 56) & 0xFF00000000000000L;
- longBits |= (((long) bytebuf[1]) << 48) & 0x00FF000000000000L;
- longBits |= (((long) bytebuf[2]) << 40) & 0x0000FF0000000000L;
- longBits |= (((long) bytebuf[3]) << 32) & 0x000000FF00000000L;
- longBits |= (((long) bytebuf[4]) << 24) & 0x00000000FF000000L;
- longBits |= (((long) bytebuf[5]) << 16) & 0x0000000000FF0000L;
- longBits |= (((long) bytebuf[6]) << 8) & 0x000000000000FF00L;
- longBits |= bytebuf[7] & 0x00000000000000FFL;
- return Double.longBitsToDouble(longBits);
- }
- /**
- * Convert the data from the input stream into a Java byte array. Use for
- * DBCS items.
- *
- * @param stream Data inlet as a byte stream
- * @param picture Cobol item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- *
- * @return Data as an array of bytes, with each element containing a half (a
- * byte) of a double-byte character value
- *
- * @throws IOException if an I/O error occured in reading
- * input
- * @throws IllegalArgumentException if the Cobol item category specified is
- * not DBCS, or a DBCS value cannot be read
- * given the data item information
- */
- public static byte[] decodeTobytes(InputStream stream,
- String picture,
- CobolCharacteristics specs)
- throws IOException {
- byte[] result = null;
- int category = specs.getPicCategory();
- if (CobolCharacteristics.PIC_DBCS == category) {
- result = readDBCS(stream, specs);
- } else {
- Message msg = MessageCatalog.getMessage("CCCR4015");
- String err = msg.formatText(new Object[]{compressedPic(picture)});
- cErrorMgr.log(ErrorManager.Severity.ERROR, null, err);
- throw new IllegalArgumentException(err);
- }
- return result;
- }
- /**
- * Convert the data from the input stream into a Java string value. Use for
- * alphabetic, alphanumeric, alphanumeric-edited, and numeric-edited items.
- *
- * @param stream Data inlet as a byte stream
- * @param reader Data inlet as a character reader
- * @param picture Cobol item picture
- * @param spec Characteristics of the item;
- * see {@link CobolCharacteristics#toString()}
- * @param enc Encoding to use to read character data
- *
- * @return Data as a string
- *
- * @throws IllegalArgumentException if a string value cannot be produced
- * given the data item information
- * @throws IOException an I/O error occured in reading input
- */
- public static String decodeToString(InputStream stream,
- String picture,
- CobolCharacteristics specs,
- String enc)
- throws IOException {
- String result = null;
- int category = specs.getPicCategory();
- // Switch back to old implementation. Will improve it later
- // if necessary.
- //
- // ESR 104580
- // Profiling shows that creating a new InputStreamReader
- // from InputStream is very expensive, in particular on
- // HP/UX 11iV2. According to CocoCodeGen.java, the InputStreamReader
- // argument, reader, is created from the InputStream argument,
- // stream, using the encoding represented by the String argument,
- // enc. Therefore there is really no need to create a new
- // InputStreamReader from InputStream.
- //
- // Relevant code in CocoCodeGen.java is
- //
- // emitter.emit("mEncoding = \"cp037\"; // initial default");
- // ...
- // emitter.emit("mWriter =new OutputStreamWriter(mOutputStream, mEncoding);");
- // emitter.emit("mReader = new InputStreamReader(mInputStream, mEncoding);");
- // and
- // emitter.emit( "mEncoding = enc.name();" );
- // emitter.emit( …
Large files files are truncated, but you can click here to view the full file