Sunday, April 13, 2008

OverpunchFormat - Java class


OverpunchFormat is a subclass of DecimalFormat that formats decimal numbers into NSF Overpunch Format.


The purpose of an Overpunch format is to encode the sign (+/-) into the least significant (right-most) digit via an encoding scheme. Also the values are typically dollar amounts, and therefore are integers representing cents (1/100 dollar). The use of DecimalFormat's setMultipler method will make quick work of this scaling.

Get the Java source code for OverpunchFormat.java.

OverpunchFormat is a subclass of DecimalFormat that formats decimal numbers into NSF Overpunch Format.

The purpose of an Overpunch format is to encode the sign (+/-) into the least significant (right-most) digit via an encoding scheme. Also the values are typically dollar amounts, and therefore are integers representing cents (1/100 dollar). The use of DecimalFormat's setMultipler method will make quick work of this scaling.


//package name.koontz.util;

import java.io.Serializable;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.text.FieldPosition;
//import java.lang.Number;
//import java.lang.StringBuffer;
import java.text.*;


/**
* OverpunchFormat is a subclass of DecimalFormat that formats
* decimal numbers into NSF Overpunch Format.
*
* The purpose of an Overpunch format is to encode the sign (+/-) into the
* least significant (right-most) digit via an encoding scheme.  Also the values are
* typically dollar amounts, and therefore are integers representing cents
* (1/100 dollar). The use of the setMultipler method will make quick work of
* this scaling.
*
*

*
* Holy hanging chad! Batman, the bat computer has detected an
* overpunched field, what could it mean?
*
* Well, Robin, it means the Joker has saved a byte in transmition cost, but it
* will take the bat computer only 23 instructions to figure out what this sign
* could be...
*

*

*
*
* OverpunchFormat does not support locales, itself!  So do not expect Kanji
* characters in your overpunched output.
*
*
*

Overpunch Encoding

*


*             Least
*          Significant   Positive      Negative
*             Digit     Overpunch     Overpunch
*               0           {             }
*               1           A             J
*               2           B             K
*               3           C             L
*               4           D             M
*               5           E             N
*               6           F             O
*               7           G             P
*               8           H             Q
*               9           J             R
*
*

*
* Example:
*

* -1575  =>  157N
* +1200  =>  120{
*

*
*
* If OverpunchFormat.parse(String, ParsePosition) fails to parse a string, it
* returns null, leaves the ParsePosition index unchanged, and sets the
* ParsePosition error index.
*
* A OverpunchFormat pattern contains only a postive pattern (no negative subpattern).
* Since the whole concept of an overpunched value is to
* encode the sign within the last digit, there will be no negative subpattern.
*
*
* Note:  DecimalFormat JVM 1.2.2 bugs Sun Bug Id 4322804, 4254220, 4243108
*
*
* @see Format
* @see NumberFormat
* @see DecimalFormat
* @see ParsePosition
*/
public class OverpunchFormat extends java.text.DecimalFormat
    implements Cloneable, Serializable  {

    // array of overpunch replacement characters
    private static final char[] positiveDigit = {'{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'};
    //                          overpunch digit   0    1    2    3    4    5    6    7    8    9
    private static final char[] negativeDigit = {'}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R'};


    public OverpunchFormat() {
        super();
    }

    public OverpunchFormat(String pattern) throws IllegalArgumentException  {
        super(pattern);
        //
    }

    /**
     * Returns an instance of Number with a value matching the given string.
     *
     *
     * @param text                the string to be parsed
     * @param parsePosition       on entry, where to begin parsing; on exit, just past the last parsed character. If parsing fails, the index will not move and the error index will be set.
     * @return                    the parsed value, or null if the parse fails
     */
    public Number parse(String text, ParsePosition parsePosition) {
        //
        int sign = 1;  // or -1 if negative
        char lastDigit;
        char overpunchChar = '{';
        int overpunchIndex = 0;

        // start at the parsePosition within text - look for a non-numeric char
        // that's the overpunch char
        if ( parsePosition.getIndex() < text.length() ) {
            for (int i = parsePosition.getIndex(); i < text.length(); i++) {
                char c = text.charAt(i);
                if ( Character.getType(c) == Character.DECIMAL_DIGIT_NUMBER ) {
                    continue;
                } else {
                    // found first non digit char - that's the overpunch
                    overpunchChar = c;
                    overpunchIndex = i;
                    break;
                }
            }
        }

        // reverse the overpunch

        switch (overpunchChar) {
        case  '}':
             sign = -1;
             lastDigit = '0';
             break;
        case  '{':
             sign = 1;
             lastDigit = '0';
             break;
        case  'A':
             sign = 1;
             lastDigit = '1';
             break;
        case  'B':
             sign = 1;
             lastDigit = '2';
             break;
        case  'C':
             sign = 1;
             lastDigit = '3';
             break;
        case  'D':
             sign = 1;
             lastDigit = '4';
             break;
        case  'E':
             sign = 1;
             lastDigit = '5';
             break;
        case  'F':
             sign = 1;
             lastDigit = '6';
             break;
        case  'G':
             sign = 1;
             lastDigit = '7';
             break;
        case  'H':
             sign = 1;
             lastDigit = '8';
             break;
        case  'I':
             sign = 1;
             lastDigit = '9';
             break;
        case  'J':
             sign = -1;
             lastDigit = '1';
             break;
        case  'K':
             sign = -1;
             lastDigit = '2';
             break;
        case  'L':
             sign = -1;
             lastDigit = '3';
             break;
        case  'M':
             sign = -1;
             lastDigit = '4';
             break;
        case  'N':
             sign = -1;
             lastDigit = '5';
             break;
        case  'O':
             sign = -1;
             lastDigit = '6';
             break;
        case  'P':
             sign = -1;
             lastDigit = '7';
             break;
        case  'Q':
             sign = -1;
             lastDigit = '8';
             break;
        case  'R':
             sign = -1;
             lastDigit = '9';
             break;
        default:
             // not the overpunch char - error
             parsePosition.setErrorIndex(overpunchIndex);
             return null;  // error return null

        }

        // replace with the overpunch char in text with the digit
        StringBuffer sb = new StringBuffer(text);

        sb.setCharAt(overpunchIndex, lastDigit);

        // now call the super's function
        Number n = super.parse(sb.toString(), parsePosition);

        if ( n instanceof Double ) {
            double d = n.doubleValue();
            d = d * sign;
            n = new Double(d);
        } else if ( n instanceof Long ) {
            long l = n.longValue();
            l = l * sign;
            n = new Long(l);
        } else {
            ;  // nothing
        }

        return n;
    }

    /**
     * Formats a long to produce a string.
     *
     * @param number               the long to format
     * @param result               where the text is to be appended
     * @param fieldPosition        On input: an alignment field, if desired. On output: the offsets of the alignment field.
     * @return                     The value passed in as the result parameter
     */
    public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) {
//System.out.println("OverpunchFormat.format|long number");
//System.out.println("OverpunchFormat.format( "+number+", "+result.toString()+", "+fieldPosition+" )");
        //
        StringBuffer buf = new StringBuffer(result.length()+20);
        long abs = Math.abs(number);          // get the absolute value
//System.out.println("    abs = "+abs);

        // call DecmialFormat to format all but the overpunch
        buf = super.format(abs, result, fieldPosition);
//System.out.println("    super.format() returns buf = " + buf.toString());
        // now we must workout the overpunch
        //
        // get last digit of the string
        char overpunchChar = buf.charAt(buf.length()-1);
//System.out.println("    buf.length()-1 = " + (buf.length()-1));
//System.out.println("    overpunchChar = " + overpunchChar);
        // convert last digit to index into the overpunch (+/-) array
        int overpunchIndex = Character.getNumericValue(overpunchChar);
//System.out.println("    overpunchIndex = " + overpunchIndex);
        // get overpunch char
        if ( number < 0 ) {
            overpunchChar = negativeDigit[overpunchIndex];
        } else {
            overpunchChar = positiveDigit[overpunchIndex];
        }
//System.out.println("    overpunchChar = " + overpunchChar);
        // replace the lastChar in the string with new lastChar
        buf.deleteCharAt(buf.length()-1);
        buf.append(overpunchChar);
//System.out.println("    buf = " + buf.toString());

        return buf;
    }

    /**
     * Formats a double to produce a string.
     *
     * @param number               the double to format
     * @param result               where the text is to be appended
     * @param fieldPosition        On input: an alignment field, if desired. On output: the offsets of the alignment field.
     * @return                     The value passed in as the result parameter
     */
    public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
//System.out.println("OverpunchFormat.format|double number");
//System.out.println("OverpunchFormat.format( "+number+", "+result.toString()+", "+fieldPosition+" )");
        //
        StringBuffer buf = new StringBuffer(result.length()+20);
        double abs = Math.abs(number);          // get the absolute value
//System.out.println("    abs = "+abs);

        // call DecmialFormat to format all but the overpunch
        buf = super.format(abs, result, fieldPosition);
//System.out.println("    super.format() returns buf = " + buf.toString());

        // now we must workout the overpunch
        //
        // get last digit of the string
        char overpunchChar = buf.charAt(buf.length()-1);
//System.out.println("    buf.length()-1 = " + (buf.length()-1));
//System.out.println("    overpunchChar = " + overpunchChar);

        // convert last digit to index into the overpunch (+/-) array
        int overpunchIndex = Character.getNumericValue(overpunchChar);
//System.out.println("    overpunchIndex = " + overpunchIndex);

        // get overpunch char
        if ( number < 0 ) {
            overpunchChar = negativeDigit[overpunchIndex];
        } else {
            overpunchChar = positiveDigit[overpunchIndex];
        }
//System.out.println("    overpunchChar = " + overpunchChar);

        // replace the lastChar in the string with new lastChar
        buf.deleteCharAt(buf.length()-1);
        buf.append(overpunchChar);
//System.out.println("    buf = " + buf.toString());


        return buf;
    }



    /**
     *  The main program for the class - a test method.
     *
     *@param  args  the text to parse
     */
    public static void main(String[] args) {

        OverpunchFormat of = new OverpunchFormat();
        of.applyPattern("0000000"); // fixed field zero padded
        of.setMultiplier(100);      // imply 2 decimal places

        if (args.length < 2) {
            System.out.println("Usage: java OverpunchFormat 'pattern' 'value'");
            System.out.println("Examples format():");
            System.out.println("  9876      -> " +of.format(9876l));
            System.out.println("    -1.35   -> " +of.format(-1.35));
            System.out.println("      .2389 -> " +of.format(0.2389));
            System.out.println("note; fixed field zero padding, implied decimal place, rounding and encoding");

            StringBuffer sb = new StringBuffer("x=012345F dollars ");
            ParsePosition pos = new ParsePosition(2);
            System.out.println("Examples parse():");
            System.out.println("  before call:" + sb);
            System.out.println("  parse(sb, pos) where pos =" + pos);
            System.out.println("  parse returns a Number = " + of.parse(sb.toString(), pos));
            System.out.println("  and sets ParsePosition.index = " + pos.getIndex() + " & errorIndex = " + pos.getErrorIndex());


            System.exit(99);
        } else {
            StringBuffer sb = new StringBuffer("OverpunchFormat of "+ args[1] +" is ");
            of.applyPattern(args[0]);
            of.format(Double.valueOf(args[1]), sb, new FieldPosition(NumberFormat.FRACTION_FIELD) );

            System.out.println(sb.toString());

        }

    }


}


Post a Comment