All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdtaus.banking.Kontonummer Maven / Gradle / Ivy

There is a newer version: 1.16
Show newest version
/*
 *  jDTAUS Banking API
 *  Copyright (C) 2005 Christian Schulte
 *  
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
package org.jdtaus.banking;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * Unique identifier to a particular bank account at a german bank.
 * 

A Kontonummer is a positive integer with a maximum of ten digits.

* * @author Christian Schulte * @version $JDTAUS: Kontonummer.java 8661 2012-09-27 11:29:58Z schulte $ */ public final class Kontonummer extends Number implements Comparable { /** * Constant for the electronic format of a Kontonummer. *

The electronic format of a Kontonummer is a ten digit number with leading zeros omitted (e.g. 6789).

*/ public static final int ELECTRONIC_FORMAT = 4001; /** * Constant for the letter format of a Kontonummer. *

The letter format of a Kontonummer is a ten digit number with leading zeros omitted separated by spaces * between the first three digits and the second three digits, the second three digits and the third three digits, * and between the third three digits and the lastdigit (e.g. 123 456 789 0).

*/ public static final int LETTER_FORMAT = 4002; /** Maximum number of digits of a Kontonummer. */ public static final int MAX_DIGITS = 10; /** Maximum number of characters of a Kontonummer. */ public static final int MAX_CHARACTERS = 13; /** {@code 10^0..10^9}. */ private static final double[] EXP10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; /** Serial version UID for backwards compatibility with 1.0.x classes. */ private static final long serialVersionUID = 3117245365189973632L; /** Used to cache instances. */ private static volatile Reference cacheReference = new SoftReference( null ); /** * German account code. * @serial */ private long kto; /** * Creates a new {@code Kontonummer} instance. * * @param accountCode The number to create an instance from. * * @throws IllegalArgumentException if {@code accountCode} is negative, zero or greater than 9999999999. * * @see #checkKontonummer(Number) */ private Kontonummer( final Number accountCode ) { if ( !Kontonummer.checkKontonummer( accountCode ) ) { throw new IllegalArgumentException( accountCode.toString() ); } this.kto = accountCode.longValue(); } /** * Parses text from a string to produce a {@code Kontonummer}. *

The method attempts to parse text starting at the index given by {@code pos}. If parsing succeeds, then the * index of {@code pos} is updated to the index after the last character used (parsing does not necessarily use all * characters up to the end of the string), and the parsed value is returned. The updated {@code pos} can be used to * indicate the starting point for the next call to this method.

* * @param accountCode A Kontonummer in either electronic or letter format. * @param pos A {@code ParsePosition} object with index and error index information as described above. * * @return The parsed value, or {@code null} if the parse fails. * * @throws NullPointerException if either {@code accountCode} or {@code pos} is {@code null}. */ public static Kontonummer parse( final String accountCode, final ParsePosition pos ) { if ( accountCode == null ) { throw new NullPointerException( "accountCode" ); } if ( pos == null ) { throw new NullPointerException( "pos" ); } Kontonummer ret = null; boolean sawSpace = false; boolean failed = false; final ParsePosition fmtPos = new ParsePosition( 0 ); final int len = accountCode.length(); final int startIndex = pos.getIndex(); final int maxIndex = startIndex + MAX_CHARACTERS; final StringBuffer digits = new StringBuffer( MAX_DIGITS ); int mode = ELECTRONIC_FORMAT; int part = 0; int partStart = 0; int partEnd = 2; int digit = 0; int i = startIndex; for ( ; i < len && i < maxIndex && digits.length() < MAX_DIGITS; i++ ) { final char c = accountCode.charAt( i ); if ( Character.isDigit( c ) ) { sawSpace = false; if ( mode == LETTER_FORMAT ) { if ( digit < partStart || digit > partEnd ) { failed = true; } else { digits.append( c ); } } else { digits.append( c ); } digit++; } else if ( c == ' ' ) { if ( sawSpace || i == startIndex || ( mode == ELECTRONIC_FORMAT && digit != 3 ) ) { failed = true; } else { mode = LETTER_FORMAT; switch ( part ) { case 0: partStart = 3; partEnd = 5; break; case 1: partStart = 6; partEnd = 8; break; case 2: partStart = 9; partEnd = 9; break; default: failed = true; break; } part++; if ( digit < partStart || digit > partEnd ) { failed = true; } } sawSpace = true; } else { failed = true; } if ( failed ) { pos.setErrorIndex( i ); break; } } if ( !failed ) { final Number num = new DecimalFormat( "##########" ).parse( digits.toString(), fmtPos ); if ( num != null && fmtPos.getErrorIndex() == -1 ) { final String key = num.toString(); ret = (Kontonummer) getCache().get( key ); if ( ret == null ) { if ( !Kontonummer.checkKontonummer( num ) ) { pos.setErrorIndex( startIndex ); ret = null; } else { pos.setIndex( i ); ret = new Kontonummer( num ); getCache().put( key, ret ); } } else { pos.setIndex( i ); } } else { pos.setErrorIndex( startIndex ); } } return ret; } /** * Parses text from the beginning of the given string to produce a {@code Kontonummer}. *

Unlike the {@link #parse(String, ParsePosition)} method this method throws a {@code ParseException} if * {@code accountCode} cannot be parsed or is of invalid length.

* * @param accountCode A Kontonummer in either electronic or letter format. * * @return The parsed value. * * @throws NullPointerException if {@code accountCode} is {@code null}. * @throws ParseException if the parse fails or {@code accountCode} is of invalid length. */ public static Kontonummer parse( final String accountCode ) throws ParseException { if ( accountCode == null ) { throw new NullPointerException( "accountCode" ); } Kontonummer kto = (Kontonummer) getCache().get( accountCode ); if ( kto == null ) { final ParsePosition pos = new ParsePosition( 0 ); kto = Kontonummer.parse( accountCode, pos ); if ( kto == null || pos.getErrorIndex() != -1 || pos.getIndex() < accountCode.length() ) { throw new ParseException( accountCode, pos.getErrorIndex() != -1 ? pos.getErrorIndex() : pos.getIndex() ); } else { getCache().put( accountCode, kto ); } } return kto; } /** * Returns an instance for the Kontonummer identified by the given number. * * @param accountCode A number identifying a Kontonummer. * * @return An instance for {@code accountCode}. * * @throws NullPointerException if {@code accountCode} is {@code null}. * @throws IllegalArgumentException if {@code accountCode} is negative, zero or greater than 9999999999. * * @see #checkKontonummer(Number) */ public static Kontonummer valueOf( final Number accountCode ) { if ( accountCode == null ) { throw new NullPointerException( "accountCode" ); } final String key = accountCode.toString(); Kontonummer ret = (Kontonummer) getCache().get( key ); if ( ret == null ) { ret = new Kontonummer( accountCode ); getCache().put( key, ret ); } return ret; } /** * Parses text from the beginning of the given string to produce a {@code Kontonummer}. *

Unlike the {@link #parse(String)} method this method throws an {@code IllegalArgumentException} if * {@code accountCode} cannot be parsed or is of invalid length.

* * @param accountCode A Kontonummer in either electronic or letter format. * * @return The parsed value. * * @throws NullPointerException if {@code accountCode} is {@code null}. * @throws IllegalArgumentException if the parse fails or {@code accountCode} is of invalid length. */ public static Kontonummer valueOf( final String accountCode ) { try { return Kontonummer.parse( accountCode ); } catch ( final ParseException e ) { throw (IllegalArgumentException) new IllegalArgumentException( accountCode ).initCause( e ); } } /** * Checks a given number to conform to a Kontonummer. * * @param accountCode The number to check. * * @return {@code true} if {@code accountCode} is a valid Kontonummer; {@code false} if not. */ public static boolean checkKontonummer( final Number accountCode ) { boolean valid = accountCode != null; if ( valid ) { final long num = accountCode.longValue(); valid = num > 0L && num < 10000000000L; } return valid; } /** * Returns this Kontonummer as an int value. * * @return This Kontonummer as an int value. */ public int intValue() { return (int) this.kto; } /** * Returns this Kontonummer as a long value. * * @return This Kontonummer as a long value. */ public long longValue() { return this.kto; } /** * Returns this Kontonummer as a float value. * * @return This Kontonummer as a float value. */ public float floatValue() { return this.kto; } /** * Returns this Kontonummer as a double value. * * @return This Kontonummer as a double value. */ public double doubleValue() { return this.kto; } /** * Formats a Kontonummer and appends the resulting text to the given string buffer. * * @param style The style to use ({@code ELECTRONIC_FORMAT} or {@code LETTER_FORMAT}). * @param toAppendTo The buffer to which the formatted text is to be appended. * * @return The value passed in as {@code toAppendTo}. * * @throws NullPointerException if {@code toAppendTo} is {@code null}. * @throws IllegalArgumentException if {@code style} is neither {@code ELECTRONIC_FORMAT} nor {@code LETTER_FORMAT}. * * @see #ELECTRONIC_FORMAT * @see #LETTER_FORMAT */ public StringBuffer format( final int style, final StringBuffer toAppendTo ) { if ( toAppendTo == null ) { throw new NullPointerException( "toAppendTo" ); } if ( style != Kontonummer.ELECTRONIC_FORMAT && style != Kontonummer.LETTER_FORMAT ) { throw new IllegalArgumentException( Integer.toString( style ) ); } final int[] digits = Kontonummer.toDigits( this.kto ); for ( int i = digits.length - 1, lastDigit = 0; i >= 0; i-- ) { if ( digits[i] != 0 || lastDigit > 0 ) { toAppendTo.append( digits[i] ); lastDigit++; } if ( style == Kontonummer.LETTER_FORMAT && ( lastDigit == 3 || lastDigit == 6 || lastDigit == 9 ) ) { toAppendTo.append( ' ' ); } } return toAppendTo; } /** * Formats a Kontonummer to produce a string. Same as *
* {@link #format(int, StringBuffer) format(style, new StringBuffer()).toString()} *
* * @param style The style to use ({@code ELECTRONIC_FORMAT} or {@code LETTER_FORMAT}). * * @return The formatted string. * * @throws IllegalArgumentException if {@code style} is neither {@code ELECTRONIC_FORMAT} nor {@code LETTER_FORMAT}. * * @see #ELECTRONIC_FORMAT * @see #LETTER_FORMAT */ public String format( final int style ) { return this.format( style, new StringBuffer() ).toString(); } /** * Formats a Kontonummer to produce a string. Same as *
* {@link #format(int) kontonummer.format(ELECTRONIC_FORMAT)} *
* * @param kontonummer The {@code Kontonummer} instance to format. * * @return The formatted string. * * @throws NullPointerException if {@code kontonummer} is {@code null}. */ public static String toString( final Kontonummer kontonummer ) { if ( kontonummer == null ) { throw new NullPointerException( "kontonummer" ); } return kontonummer.format( ELECTRONIC_FORMAT ); } /** * Creates an array holding the digits of {@code number}. * * @param number The number to return the digits for. * * @return An array holding the digits of {@code number}. */ private static int[] toDigits( final long number ) { int i; int j; long subst; final int[] ret = new int[ MAX_DIGITS ]; for ( i = MAX_DIGITS - 1; i >= 0; i-- ) { for ( j = i + 1, subst = 0L; j < MAX_DIGITS; j++ ) { subst += ret[j] * EXP10[j]; } ret[i] = (int) Math.floor( ( number - subst ) / EXP10[i] ); } return ret; } /** * Creates a string representing the properties of the instance. * * @return A string representing the properties of the instance. */ private String internalString() { return new StringBuffer( 500 ).append( "{accountCode=" ).append( this.kto ).append( '}' ).toString(); } /** * Gets the current cache instance. * * @return Current cache instance. */ private static Map getCache() { Map cache = (Map) cacheReference.get(); if ( cache == null ) { cache = Collections.synchronizedMap( new HashMap( 1024 ) ); cacheReference = new SoftReference( cache ); } return cache; } /** * Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer * as this object is less than, equal to, or greater than the specified object.

* * @param o The Object to be compared. * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than * the specified object. * * @throws NullPointerException if {@code o} is {@code null}. * @throws ClassCastException if the specified object's type prevents it from being compared to this Object. */ public int compareTo( final Object o ) { if ( o == null ) { throw new NullPointerException( "o" ); } if ( !( o instanceof Kontonummer ) ) { throw new ClassCastException( o.getClass().getName() ); } int result = 0; final Kontonummer that = (Kontonummer) o; if ( !this.equals( that ) ) { result = this.kto > that.kto ? 1 : -1; } return result; } /** * Indicates whether some other object is equal to this one. * * @param o The reference object with which to compare. * * @return {@code true} if this object is the same as {@code o}; {@code false} otherwise. */ public boolean equals( final Object o ) { boolean equal = o == this; if ( !equal && o instanceof Kontonummer ) { equal = this.kto == ( (Kontonummer) o ).kto; } return equal; } /** * Returns a hash code value for this object. * * @return A hash code value for this object. */ public int hashCode() { return (int) ( this.kto ^ ( this.kto >>> 32 ) ); } /** * Returns a string representation of the object. * * @return A string representation of the object. */ public String toString() { return super.toString() + this.internalString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy