Java: Convert String to packed decimal
Asked Answered
O

4

5

new here!

Situation: I'm working on a project which needs to communicate with an AS/400 server. My task is to basically handle the requests which will be sent to the AS/400 server. To do this, all the user input should be in EDCDIC bytes.

Problem:
I have managed to convert packed decimals to String with the code below, found on this forum:

public class PackedDecimal {
    public static long parse(byte[] pdIn) throws Exception {
        // Convert packed decimal to long
        final int PlusSign = 0x0C; // Plus sign
        final int MinusSign = 0x0D; // Minus
        final int NoSign = 0x0F; // Unsigned
        final int DropHO = 0xFF; // AND mask to drop HO sign bits
        final int GetLO = 0x0F; // Get only LO digit
        long val = 0; // Value to return

        for (int i = 0; i < pdIn.length; i++) {
            int aByte = pdIn[i] & DropHO; // Get next 2 digits & drop sign bits
            if (i == pdIn.length - 1) { // last digit?
                int digit = aByte >> 4; // First get digit
                val = val * 10 + digit;
                // System.out.println("digit=" + digit + ", val=" + val);
                int sign = aByte & GetLO; // now get sign
                if (sign == MinusSign)
                    val = -val;
                else {
                    // Do we care if there is an invalid sign?
                    if (sign != PlusSign && sign != NoSign)
                        throw new Exception("OC7");
                }
            } else {
                int digit = aByte >> 4; // HO first
                val = val * 10 + digit;
                // System.out.println("digit=" + digit + ", val=" + val);
                digit = aByte & GetLO; // now LO
                val = val * 10 + digit;
                // System.out.println("digit=" + digit + ", val=" + val);
            }
        }
        return val;
    } // end parse()
      // Test the above

    public static void main(String[] args) throws Exception {
        byte[] pd = new byte[] { 0x19, 0x2C }; // 192
        System.out.println(PackedDecimal.parse(pd));
        pd = new byte[] { (byte) 0x98, 0x44, 0x32, 0x3D }; // -9844323
        System.out.println(PackedDecimal.parse(pd));
        pd = new byte[] { (byte) 0x98, 0x44, 0x32 }; // invalid sign
        System.out.println(PackedDecimal.parse(pd));
    }
}

My problem now is I have to convert these String values again to EBCDIC bytes so that the AS/400 server would understand it. I'm planning to do something like constructing a request (raw bytes) using the format specified in the Silverlake documentation. Once the request is built, I plan to manually change values inside that request using a POJO which stores my request (with setters and getters) so I could just go like request.setField1("Stuff".getBytes(Charset.forName("Cp1047"))).

I don't have that much experience with bits, bytes and nibbles. I hope someone could help me out.

In our code, there's a packed decimal we found which consists of 5 bytes. It goes something like = {00 00 00 00 0F}. I convert this using the method I got from the code above and the value I got was 0. Now, I would like to convert this 0 back to its original form with its original byte size 5.

Outgeneral answered 26/6, 2012 at 15:7 Comment(3)
Do you want the packed decimal as a byte array, like your input, or do you want the actual n bytes as an actual packed decimal, which would be a String in Java?Althaalthea
@GilbertLeBlanc: I would want the output to be a byte array as well since I would be passing this to the AS/400 server.Outgeneral
I don't really understand how you are trying to communicate with the as/400. I'm not totally sure about packed decimals, but so far I could avoid doing bit/byte-stuff or even some codepage issues, when communicating between java or php and our as/400, by using stuff like ILE-calls or SQL. How do you put your information on the as/400?Overthrust
A
2

Here's my version of a long to packed decimal method.

public class PackedDecimal {

    public static byte[] format(long number, int bytes) {
        byte[] b = new byte[bytes];

        final byte minusSign = 0x0D; // Minus
        final byte noSign = 0x0F; // Unsigned

        String s = Long.toString(number);
        int length = s.length();
        boolean isNegative = false;

        if (s.charAt(0) == '-') {
            isNegative = true;
            s = s.substring(1);
            length--;
        }

        int extraBytes = length - bytes + 1;

        if (extraBytes < 0) {
            // Pad extra byte positions with zero
            for (int i = 0; i < -extraBytes; i++) {
                b[i] = 0x00;
            }
        } else if (extraBytes > 0) {
            // Truncate the high order digits of the number to fit
            s = s.substring(extraBytes);
            length -= extraBytes;
            extraBytes = 0;
        }

        // Translate the string digits into bytes
        for (int i = 0; i < length; i++) {
            String digit = s.substring(i, i + 1);
            b[i - extraBytes] = Byte.valueOf(digit);
        }

        // Add the sign byte
        if (isNegative) {
            b[bytes - 1] = minusSign;
        } else {
            b[bytes - 1] = noSign;
        }

        return b;
    }

    public static void main(String[] args) {
        long number = -456L;
        byte[] b = PackedDecimal.format(number, 5);
        System.out.println("Number: " + number + ", packed: " + byteToString(b));

        number = 0L;
        b = PackedDecimal.format(number, 5);
        System.out.println("Number: " + number + ", packed: " + byteToString(b));

        number = 5823L;
        b = PackedDecimal.format(number, 5);
        System.out.println("Number: " + number + ", packed: " + byteToString(b));

        number = 123456L;
        b = PackedDecimal.format(number, 5);
        System.out.println("Number: " + number + ", packed: " + byteToString(b));
    }

    public static String byteToString(byte[] b) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < b.length; i++) {
            sb.append("0x");
            sb.append(Integer.toHexString((int) b[i]).toUpperCase());
            sb.append(" ");
        }
        return sb.toString();
    }

}

And here are the test results.

Number: -456, packed: 0x0 0x4 0x5 0x6 0xD 
Number: 0, packed: 0x0 0x0 0x0 0x0 0xF 
Number: 5823, packed: 0x5 0x8 0x2 0x3 0xF 
Number: 123456, packed: 0x3 0x4 0x5 0x6 0xF 
Althaalthea answered 26/6, 2012 at 18:7 Comment(4)
Oh, by the way, is this possible without the int bytes parameter? I mean, is there anyway I can retrieve the byte array size from the converted String itself?Outgeneral
@Miguel Portugal: You could, but normally packed decimal fields on an IBM computer are of a fixed size, irregardless of the content. A packed decimal field is almost always an even number of bytes.Althaalthea
@GilbertLeBlanc: In the byteToString method, the example code uses the notation of 0x plus the integer value of the byte. 0x typically denotes a hexadecimal value, so any value greater than 9 is going to be misleading (e.g. 0x13 and 0x15 in the output is not correct). Changing the second append to sb.append(Integer.toHexString(b[i])); would be more appropriate.Surrogate
@jr: You're correct. I don't remember what I was thinking 3 years ago.Althaalthea
B
4

The IBM Toolbox for Java and JTOpen library provides data conversion classes for exactly this purpose.

Bartolomeo answered 26/6, 2012 at 15:45 Comment(1)
Some of the people in my team are already working on this. My group was assigned to do it without the use of JTOpen and IBM toolbox etc. :POutgeneral
A
2

Here's my version of a long to packed decimal method.

public class PackedDecimal {

    public static byte[] format(long number, int bytes) {
        byte[] b = new byte[bytes];

        final byte minusSign = 0x0D; // Minus
        final byte noSign = 0x0F; // Unsigned

        String s = Long.toString(number);
        int length = s.length();
        boolean isNegative = false;

        if (s.charAt(0) == '-') {
            isNegative = true;
            s = s.substring(1);
            length--;
        }

        int extraBytes = length - bytes + 1;

        if (extraBytes < 0) {
            // Pad extra byte positions with zero
            for (int i = 0; i < -extraBytes; i++) {
                b[i] = 0x00;
            }
        } else if (extraBytes > 0) {
            // Truncate the high order digits of the number to fit
            s = s.substring(extraBytes);
            length -= extraBytes;
            extraBytes = 0;
        }

        // Translate the string digits into bytes
        for (int i = 0; i < length; i++) {
            String digit = s.substring(i, i + 1);
            b[i - extraBytes] = Byte.valueOf(digit);
        }

        // Add the sign byte
        if (isNegative) {
            b[bytes - 1] = minusSign;
        } else {
            b[bytes - 1] = noSign;
        }

        return b;
    }

    public static void main(String[] args) {
        long number = -456L;
        byte[] b = PackedDecimal.format(number, 5);
        System.out.println("Number: " + number + ", packed: " + byteToString(b));

        number = 0L;
        b = PackedDecimal.format(number, 5);
        System.out.println("Number: " + number + ", packed: " + byteToString(b));

        number = 5823L;
        b = PackedDecimal.format(number, 5);
        System.out.println("Number: " + number + ", packed: " + byteToString(b));

        number = 123456L;
        b = PackedDecimal.format(number, 5);
        System.out.println("Number: " + number + ", packed: " + byteToString(b));
    }

    public static String byteToString(byte[] b) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < b.length; i++) {
            sb.append("0x");
            sb.append(Integer.toHexString((int) b[i]).toUpperCase());
            sb.append(" ");
        }
        return sb.toString();
    }

}

And here are the test results.

Number: -456, packed: 0x0 0x4 0x5 0x6 0xD 
Number: 0, packed: 0x0 0x0 0x0 0x0 0xF 
Number: 5823, packed: 0x5 0x8 0x2 0x3 0xF 
Number: 123456, packed: 0x3 0x4 0x5 0x6 0xF 
Althaalthea answered 26/6, 2012 at 18:7 Comment(4)
Oh, by the way, is this possible without the int bytes parameter? I mean, is there anyway I can retrieve the byte array size from the converted String itself?Outgeneral
@Miguel Portugal: You could, but normally packed decimal fields on an IBM computer are of a fixed size, irregardless of the content. A packed decimal field is almost always an even number of bytes.Althaalthea
@GilbertLeBlanc: In the byteToString method, the example code uses the notation of 0x plus the integer value of the byte. 0x typically denotes a hexadecimal value, so any value greater than 9 is going to be misleading (e.g. 0x13 and 0x15 in the output is not correct). Changing the second append to sb.append(Integer.toHexString(b[i])); would be more appropriate.Surrogate
@jr: You're correct. I don't remember what I was thinking 3 years ago.Althaalthea
N
1

I came across a similar Problem...

The class form the first post to decode/parse the PackedDecimals worked fine... but the code in Gilbert Le Blancs answer did not produce a valid output.

so i fixed his code...

public class PackedDecimal {
    private static final int PlusSign = 0x0C; // Plus sign
    private static final int MinusSign = 0x0D; // Minus
    private static final int NoSign = 0x0F; // Unsigned
    private static final int DropHO = 0xFF; // AND mask to drop HO sign bits
    private static final int GetLO = 0x0F; // Get only LO digit

    public static long parse(byte[] pdIn) throws Exception {
        long val = 0; // Value to return

        for (int i = 0; i < pdIn.length; i++) {
            int aByte = pdIn[i] & DropHO; // Get next 2 digits & drop sign bits
            if (i == pdIn.length - 1) { // last digit?
                int digit = aByte >> 4; // First get digit
                val = val * 10 + digit;
                log("digit=" + digit + ", val=" + val);
                int sign = aByte & GetLO; // now get sign
                if (sign == MinusSign)
                    val = -val;
                else {
                    // Do we care if there is an invalid sign?
                    if (sign != PlusSign && sign != NoSign) {
                        System.out.println();
                        for (int x = 0; x < pdIn.length; x++) {
                            System.out.print(Integer.toString(pdIn[x] & 0x000000ff, 16));
                        }
                        System.out.println();
                        throw new Exception("OC7");
                    }
                }
            } else {
                int digit = aByte >> 4; // HO first
                val = val * 10 + digit;
                log("digit=" + digit + ", val=" + val);
                digit = aByte & GetLO; // now LO
                val = val * 10 + digit;
                log("digit=" + digit + ", val=" + val);
            }
        }
        return val;
    }

    public static byte[] format(long number, int byteCount) {
        byte[] bytes = new byte[byteCount];

        String data = Long.toString(number);
        int length = data.length();
        boolean isNegative = false;

        if (data.charAt(0) == '-') {
            isNegative = true;
            data = data.substring(1);
            length--;
        }

        if (length % 2 == 0) {
            data = "0" + data;
            length++;
        }

        int neededBytes = (int) (((length + 1) / 2f) + 0.5f);

        int extraBytes = neededBytes - byteCount;
        if (extraBytes < 0) {
            // Pad extra byte positions with zero
            for (int i = 0; i < -extraBytes; i++) {
                bytes[i] = 0x00;
            }
        } else if (extraBytes > 0) {
            // Truncate the high order digits of the number to fit
            data = data.substring(extraBytes);
            length -= extraBytes;
            extraBytes = 0;
        }

        // Translate the string digits into bytes
        for (int pos = 0; pos <= length - 1; pos++) {
            String digit = data.substring(pos, pos + 1);
            int now = (pos / 2) - extraBytes;

            if (pos % 2 == 0) { // High
                bytes[now] = (byte) (Byte.valueOf(digit) << 4);
                log("HIGH " + digit);
            } else { // Low
                bytes[now] = (byte) (bytes[now] | (Byte.valueOf(digit) & 0x0f));
                log("LOW  " + digit);
            }
        }

        // Add the sign byte
        if (isNegative) {
            bytes[byteCount - 1] = (byte) (bytes[byteCount - 1] | MinusSign);
        } else {
            bytes[byteCount - 1] = (byte) (bytes[byteCount - 1] | PlusSign);
        }

        return bytes;
    }

    private static void log(String string) {
        // System.out.println(string);
    }

    public static void main(String[] args) throws Exception {
        long price;
        byte[] format;

        price = 44981;
        format = PackedDecimal.format(price, 5);
        System.out.println("Input: " + price);
        System.out.println("Bytes: " + byteToString(format));
        System.out.println("Result: " + PackedDecimal.parse(format));
        System.out.println("---------");

        price = 4498;
        format = PackedDecimal.format(price, 4);
        System.out.println("Input: " + price);
        System.out.println("Bytes: " + byteToString(format));
        System.out.println("Result: " + PackedDecimal.parse(format));
        System.out.println("---------");

        price = 1337;
        format = PackedDecimal.format(price, 3);
        System.out.println("Input: " + price);
        System.out.println("Bytes: " + byteToString(format));
        System.out.println("Result: " + PackedDecimal.parse(format));
        System.out.println("---------");
    }

    public static String byteToString(byte[] b) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < b.length; i++) {
            int curByte = b[i] & 0xFF;
            sb.append("0x");
            if (curByte <= 0x0F) {
                sb.append("0");
            }
            sb.append(Integer.toString(curByte, 16));
            sb.append(" ");
        }
        return sb.toString().trim();
    }
}

The results:

Input: 44981
Bytes: 0x00 0x00 0x44 0x98 0x1c
Result: 44981
---------
Input: 4498
Bytes: 0x00 0x04 0x49 0x8c
Result: 4498
---------
Input: 1337
Bytes: 0x01 0x33 0x7c
Result: 1337
---------

Have fun & enjoy

Noseband answered 8/4, 2013 at 14:41 Comment(2)
Thanks Dodge. How about converting Decimal numbers (with float values) into COBOL byte array and vice verse?Freestyle
@Freestyle i'm sorry but i have no idea. also i'm out of that topic for a long time now.Noseband
P
0

Here is My simple Solution : it Converts String to positive packed numbers.

Utility Class :

public class StringToPacked {

    public static byte[] stringToPacked(String number){
        int length = number.length();
        int remainder = length % 2;
        if(remainder==0)number ="0" + number;
        int quo = length / 2;
        byte result[] = new byte[quo+1];
        for(int i=0,j=0;i<quo;i++,j=j+2){
            int a = Integer.parseInt(number.substring(j,j+1));
            int b = Integer.parseInt(number.substring(j+1,j+2));
            a = a<<4;
            int c = a|b;
            result[i]=(byte)c;
        }
        int a = Integer.parseInt(number.substring(number.length()-1,number.length()));
        a = a<<4;
        int b = 12;
        int c = a|b;
        result[quo]=(byte)c;
        return result;
    }

    public static String displayHex(byte number[]){
        char array[] ="0123456789ABCDF".toCharArray();
        char result[]=new char[2*number.length];
        for(int i=0;i<number.length;i++){
            result[2*i]=array[number[i]>>4 & 0x0f];
            result[2*i+1]=array[number[i] & 0x0f];
        }
        String okay = new String(result);
        return okay;
    }
}

Main Class :

public class Packedtest {
     public static void main(String[] args) {
        // TODO Auto-generated method stub 

        String number = "123456";
        System.out.println(StringToPacked.stringToPacked(number));
        byte b1[]=StringToPacked.stringToPacked(number);
        String C1 =StringToPacked.displayHex(b1);
        System.out.println(number);
        System.out.println(C1);

        number = "12";
        System.out.println(StringToPacked.stringToPacked(number));
        b1=StringToPacked.stringToPacked(number);
        C1 =StringToPacked.displayHex(b1);
        System.out.println(number);
        System.out.println(C1);

        number = "123";
        System.out.println(StringToPacked.stringToPacked(number));
        b1=StringToPacked.stringToPacked(number);
        C1 =StringToPacked.displayHex(b1);
        System.out.println(number);
        System.out.println(C1);

        number = "1234567";
        System.out.println(StringToPacked.stringToPacked(number));
        b1=StringToPacked.stringToPacked(number);
        C1 =StringToPacked.displayHex(b1);
        System.out.println(number);
        System.out.println(C1);

        number = "12345678";
        System.out.println(StringToPacked.stringToPacked(number));
        b1=StringToPacked.stringToPacked(number);
        C1 =StringToPacked.displayHex(b1);
        System.out.println(number);
        System.out.println(C1);
    }

Results :

[B@19e0bfd
123456
0123456C
[B@139a55
12
012C
[B@1db9742
123
123C
[B@106d69c
1234567
1234567C
[B@52e922
12345678
012345678C
Pantelegraph answered 2/7, 2015 at 20:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.