EMMA Coverage Report (generated Tue Dec 09 03:51:57 CET 2014)
[all classes][org.jdtaus.banking]

COVERAGE SUMMARY FOR SOURCE FILE [Kontonummer.java]

nameclass, %method, %block, %line, %
Kontonummer.java100% (1/1)76%  (16/21)87%  (576/661)86%  (126.7/147)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Kontonummer100% (1/1)76%  (16/21)87%  (576/661)86%  (126.7/147)
doubleValue (): double 0%   (0/1)0%   (0/4)0%   (0/1)
floatValue (): float 0%   (0/1)0%   (0/4)0%   (0/1)
intValue (): int 0%   (0/1)0%   (0/4)0%   (0/1)
toString (Kontonummer): String 0%   (0/1)0%   (0/11)0%   (0/3)
valueOf (String): Kontonummer 0%   (0/1)0%   (0/12)0%   (0/3)
Kontonummer (Number): void 100% (1/1)62%  (10/16)80%  (4/5)
valueOf (Number): Kontonummer 100% (1/1)83%  (24/29)88%  (7/8)
format (int, StringBuffer): StringBuffer 100% (1/1)83%  (54/65)83%  (10/12)
parse (String, ParsePosition): Kontonummer 100% (1/1)91%  (193/213)91%  (61/67)
parse (String): Kontonummer 100% (1/1)91%  (50/55)90%  (9/10)
equals (Object): boolean 100% (1/1)92%  (23/25)95%  (3.8/4)
checkKontonummer (Number): boolean 100% (1/1)96%  (24/25)97%  (4.9/5)
<static initializer> 100% (1/1)100% (49/49)100% (2/2)
compareTo (Object): int 100% (1/1)100% (38/38)100% (9/9)
format (int): String 100% (1/1)100% (8/8)100% (1/1)
getCache (): Map 100% (1/1)100% (19/19)100% (5/5)
hashCode (): int 100% (1/1)100% (9/9)100% (1/1)
internalString (): String 100% (1/1)100% (13/13)100% (1/1)
longValue (): long 100% (1/1)100% (3/3)100% (1/1)
toDigits (long): int [] 100% (1/1)100% (48/48)100% (6/6)
toString (): String 100% (1/1)100% (11/11)100% (1/1)

1/*
2 *  jDTAUS Banking API
3 *  Copyright (C) 2005 Christian Schulte
4 *  <cs@schulte.it>
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2.1 of the License, or any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 *
20 */
21package org.jdtaus.banking;
22 
23import java.lang.ref.Reference;
24import java.lang.ref.SoftReference;
25import java.text.DecimalFormat;
26import java.text.ParseException;
27import java.text.ParsePosition;
28import java.util.Collections;
29import java.util.HashMap;
30import java.util.Map;
31 
32/**
33 * Unique identifier to a particular bank account at a german bank.
34 * <p>A Kontonummer is a positive integer with a maximum of ten digits.</p>
35 *
36 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
37 * @version $JDTAUS: Kontonummer.java 8661 2012-09-27 11:29:58Z schulte $
38 */
39public final class Kontonummer extends Number implements Comparable
40{
41 
42    /**
43     * Constant for the electronic format of a Kontonummer.
44     * <p>The electronic format of a Kontonummer is a ten digit number with leading zeros omitted (e.g. 6789).</p>
45     */
46    public static final int ELECTRONIC_FORMAT = 4001;
47 
48    /**
49     * Constant for the letter format of a Kontonummer.
50     * <p>The letter format of a Kontonummer is a ten digit number with leading zeros omitted separated by spaces
51     * between the first three digits and the second three digits, the second three digits and the third three digits,
52     * and between the third three digits and the lastdigit (e.g. 123 456 789 0).</p>
53     */
54    public static final int LETTER_FORMAT = 4002;
55 
56    /** Maximum number of digits of a Kontonummer. */
57    public static final int MAX_DIGITS = 10;
58 
59    /** Maximum number of characters of a Kontonummer. */
60    public static final int MAX_CHARACTERS = 13;
61 
62    /** {@code 10^0..10^9}. */
63    private static final double[] EXP10 =
64    {
65        1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
66        1000000000
67    };
68 
69    /** Serial version UID for backwards compatibility with 1.0.x classes. */
70    private static final long serialVersionUID = 3117245365189973632L;
71 
72    /** Used to cache instances. */
73    private static volatile Reference cacheReference = new SoftReference( null );
74 
75    /**
76     * German account code.
77     * @serial
78     */
79    private long kto;
80 
81    /**
82     * Creates a new {@code Kontonummer} instance.
83     *
84     * @param accountCode The number to create an instance from.
85     *
86     * @throws IllegalArgumentException if {@code accountCode} is negative, zero or greater than 9999999999.
87     *
88     * @see #checkKontonummer(Number)
89     */
90    private Kontonummer( final Number accountCode )
91    {
92        if ( !Kontonummer.checkKontonummer( accountCode ) )
93        {
94            throw new IllegalArgumentException( accountCode.toString() );
95        }
96 
97        this.kto = accountCode.longValue();
98    }
99 
100    /**
101     * Parses text from a string to produce a {@code Kontonummer}.
102     * <p>The method attempts to parse text starting at the index given by {@code pos}. If parsing succeeds, then the
103     * index of {@code pos} is updated to the index after the last character used (parsing does not necessarily use all
104     * characters up to the end of the string), and the parsed value is returned. The updated {@code pos} can be used to
105     * indicate the starting point for the next call to this method.</p>
106     *
107     * @param accountCode A Kontonummer in either electronic or letter format.
108     * @param pos A {@code ParsePosition} object with index and error index information as described above.
109     *
110     * @return The parsed value, or {@code null} if the parse fails.
111     *
112     * @throws NullPointerException if either {@code accountCode} or {@code pos} is {@code null}.
113     */
114    public static Kontonummer parse( final String accountCode, final ParsePosition pos )
115    {
116        if ( accountCode == null )
117        {
118            throw new NullPointerException( "accountCode" );
119        }
120        if ( pos == null )
121        {
122            throw new NullPointerException( "pos" );
123        }
124 
125        Kontonummer ret = null;
126        boolean sawSpace = false;
127        boolean failed = false;
128        final ParsePosition fmtPos = new ParsePosition( 0 );
129        final int len = accountCode.length();
130        final int startIndex = pos.getIndex();
131        final int maxIndex = startIndex + MAX_CHARACTERS;
132        final StringBuffer digits = new StringBuffer( MAX_DIGITS );
133        int mode = ELECTRONIC_FORMAT;
134        int part = 0;
135        int partStart = 0;
136        int partEnd = 2;
137        int digit = 0;
138        int i = startIndex;
139 
140        for ( ; i < len && i < maxIndex && digits.length() < MAX_DIGITS; i++ )
141        {
142            final char c = accountCode.charAt( i );
143 
144            if ( Character.isDigit( c ) )
145            {
146                sawSpace = false;
147 
148                if ( mode == LETTER_FORMAT )
149                {
150                    if ( digit < partStart || digit > partEnd )
151                    {
152                        failed = true;
153                    }
154                    else
155                    {
156                        digits.append( c );
157                    }
158                }
159                else
160                {
161                    digits.append( c );
162                }
163 
164                digit++;
165            }
166            else if ( c == ' ' )
167            {
168                if ( sawSpace || i == startIndex || ( mode == ELECTRONIC_FORMAT && digit != 3 ) )
169                {
170                    failed = true;
171                }
172                else
173                {
174                    mode = LETTER_FORMAT;
175                    switch ( part )
176                    {
177                        case 0:
178                            partStart = 3;
179                            partEnd = 5;
180                            break;
181                        case 1:
182                            partStart = 6;
183                            partEnd = 8;
184                            break;
185                        case 2:
186                            partStart = 9;
187                            partEnd = 9;
188                            break;
189                        default:
190                            failed = true;
191                            break;
192                    }
193                    part++;
194 
195                    if ( digit < partStart || digit > partEnd )
196                    {
197                        failed = true;
198                    }
199                }
200 
201                sawSpace = true;
202            }
203            else
204            {
205                failed = true;
206            }
207 
208            if ( failed )
209            {
210                pos.setErrorIndex( i );
211                break;
212            }
213        }
214 
215        if ( !failed )
216        {
217            final Number num = new DecimalFormat( "##########" ).parse( digits.toString(), fmtPos );
218 
219            if ( num != null && fmtPos.getErrorIndex() == -1 )
220            {
221                final String key = num.toString();
222                ret = (Kontonummer) getCache().get( key );
223 
224                if ( ret == null )
225                {
226                    if ( !Kontonummer.checkKontonummer( num ) )
227                    {
228                        pos.setErrorIndex( startIndex );
229                        ret = null;
230                    }
231                    else
232                    {
233                        pos.setIndex( i );
234                        ret = new Kontonummer( num );
235                        getCache().put( key, ret );
236                    }
237                }
238                else
239                {
240                    pos.setIndex( i );
241                }
242            }
243            else
244            {
245                pos.setErrorIndex( startIndex );
246            }
247        }
248 
249        return ret;
250    }
251 
252    /**
253     * Parses text from the beginning of the given string to produce a {@code Kontonummer}.
254     * <p>Unlike the {@link #parse(String, ParsePosition)} method this method throws a {@code ParseException} if
255     * {@code accountCode} cannot be parsed or is of invalid length.</p>
256     *
257     * @param accountCode A Kontonummer in either electronic or letter format.
258     *
259     * @return The parsed value.
260     *
261     * @throws NullPointerException if {@code accountCode} is {@code null}.
262     * @throws ParseException if the parse fails or {@code accountCode} is of invalid length.
263     */
264    public static Kontonummer parse( final String accountCode ) throws ParseException
265    {
266        if ( accountCode == null )
267        {
268            throw new NullPointerException( "accountCode" );
269        }
270 
271        Kontonummer kto = (Kontonummer) getCache().get( accountCode );
272        if ( kto == null )
273        {
274            final ParsePosition pos = new ParsePosition( 0 );
275            kto = Kontonummer.parse( accountCode, pos );
276            if ( kto == null || pos.getErrorIndex() != -1 || pos.getIndex() < accountCode.length() )
277            {
278                throw new ParseException( accountCode, pos.getErrorIndex() != -1 ? pos.getErrorIndex() : pos.getIndex() );
279            }
280            else
281            {
282                getCache().put( accountCode, kto );
283            }
284        }
285 
286        return kto;
287    }
288 
289    /**
290     * Returns an instance for the Kontonummer identified by the given number.
291     *
292     * @param accountCode A number identifying a Kontonummer.
293     *
294     * @return An instance for {@code accountCode}.
295     *
296     * @throws NullPointerException if {@code accountCode} is {@code null}.
297     * @throws IllegalArgumentException if {@code accountCode} is negative, zero or greater than 9999999999.
298     *
299     * @see #checkKontonummer(Number)
300     */
301    public static Kontonummer valueOf( final Number accountCode )
302    {
303        if ( accountCode == null )
304        {
305            throw new NullPointerException( "accountCode" );
306        }
307 
308        final String key = accountCode.toString();
309        Kontonummer ret = (Kontonummer) getCache().get( key );
310        if ( ret == null )
311        {
312            ret = new Kontonummer( accountCode );
313            getCache().put( key, ret );
314        }
315 
316        return ret;
317    }
318 
319    /**
320     * Parses text from the beginning of the given string to produce a {@code Kontonummer}.
321     * <p>Unlike the {@link #parse(String)} method this method throws an {@code IllegalArgumentException} if
322     * {@code accountCode} cannot be parsed or is of invalid length.</p>
323     *
324     * @param accountCode A Kontonummer in either electronic or letter format.
325     *
326     * @return The parsed value.
327     *
328     * @throws NullPointerException if {@code accountCode} is {@code null}.
329     * @throws IllegalArgumentException if the parse fails or {@code accountCode} is of invalid length.
330     */
331    public static Kontonummer valueOf( final String accountCode )
332    {
333        try
334        {
335            return Kontonummer.parse( accountCode );
336        }
337        catch ( final ParseException e )
338        {
339            throw (IllegalArgumentException) new IllegalArgumentException( accountCode ).initCause( e );
340        }
341    }
342 
343    /**
344     * Checks a given number to conform to a Kontonummer.
345     *
346     * @param accountCode The number to check.
347     *
348     * @return {@code true} if {@code accountCode} is a valid Kontonummer; {@code false} if not.
349     */
350    public static boolean checkKontonummer( final Number accountCode )
351    {
352        boolean valid = accountCode != null;
353 
354        if ( valid )
355        {
356            final long num = accountCode.longValue();
357            valid = num > 0L && num < 10000000000L;
358        }
359 
360        return valid;
361    }
362 
363    /**
364     * Returns this Kontonummer as an int value.
365     *
366     * @return This Kontonummer as an int value.
367     */
368    public int intValue()
369    {
370        return (int) this.kto;
371    }
372 
373    /**
374     * Returns this Kontonummer as a long value.
375     *
376     * @return This Kontonummer as a long value.
377     */
378    public long longValue()
379    {
380        return this.kto;
381    }
382 
383    /**
384     * Returns this Kontonummer as a float value.
385     *
386     * @return This Kontonummer as a float value.
387     */
388    public float floatValue()
389    {
390        return this.kto;
391    }
392 
393    /**
394     * Returns this Kontonummer as a double value.
395     *
396     * @return This Kontonummer as a double value.
397     */
398    public double doubleValue()
399    {
400        return this.kto;
401    }
402 
403    /**
404     * Formats a Kontonummer and appends the resulting text to the given string buffer.
405     *
406     * @param style The style to use ({@code ELECTRONIC_FORMAT} or {@code LETTER_FORMAT}).
407     * @param toAppendTo The buffer to which the formatted text is to be appended.
408     *
409     * @return The value passed in as {@code toAppendTo}.
410     *
411     * @throws NullPointerException if {@code toAppendTo} is {@code null}.
412     * @throws IllegalArgumentException if {@code style} is neither {@code ELECTRONIC_FORMAT} nor {@code LETTER_FORMAT}.
413     *
414     * @see #ELECTRONIC_FORMAT
415     * @see #LETTER_FORMAT
416     */
417    public StringBuffer format( final int style, final StringBuffer toAppendTo )
418    {
419        if ( toAppendTo == null )
420        {
421            throw new NullPointerException( "toAppendTo" );
422        }
423        if ( style != Kontonummer.ELECTRONIC_FORMAT && style != Kontonummer.LETTER_FORMAT )
424        {
425            throw new IllegalArgumentException( Integer.toString( style ) );
426        }
427 
428        final int[] digits = Kontonummer.toDigits( this.kto );
429        for ( int i = digits.length - 1, lastDigit = 0; i >= 0; i-- )
430        {
431            if ( digits[i] != 0 || lastDigit > 0 )
432            {
433                toAppendTo.append( digits[i] );
434                lastDigit++;
435            }
436 
437            if ( style == Kontonummer.LETTER_FORMAT && ( lastDigit == 3 || lastDigit == 6 || lastDigit == 9 ) )
438            {
439                toAppendTo.append( ' ' );
440            }
441        }
442 
443        return toAppendTo;
444    }
445 
446    /**
447     * Formats a Kontonummer to produce a string. Same as
448     * <blockquote>
449     * {@link #format(int, StringBuffer) format<code>(style, new StringBuffer()).toString()</code>}
450     * </blockquote>
451     *
452     * @param style The style to use ({@code ELECTRONIC_FORMAT} or {@code LETTER_FORMAT}).
453     *
454     * @return The formatted string.
455     *
456     * @throws IllegalArgumentException if {@code style} is neither {@code ELECTRONIC_FORMAT} nor {@code LETTER_FORMAT}.
457     *
458     * @see #ELECTRONIC_FORMAT
459     * @see #LETTER_FORMAT
460     */
461    public String format( final int style )
462    {
463        return this.format( style, new StringBuffer() ).toString();
464    }
465 
466    /**
467     * Formats a Kontonummer to produce a string. Same as
468     * <blockquote>
469     * {@link #format(int) kontonummer.format(ELECTRONIC_FORMAT)}
470     * </blockquote>
471     *
472     * @param kontonummer The {@code Kontonummer} instance to format.
473     *
474     * @return The formatted string.
475     *
476     * @throws NullPointerException if {@code kontonummer} is {@code null}.
477     */
478    public static String toString( final Kontonummer kontonummer )
479    {
480        if ( kontonummer == null )
481        {
482            throw new NullPointerException( "kontonummer" );
483        }
484 
485        return kontonummer.format( ELECTRONIC_FORMAT );
486    }
487 
488    /**
489     * Creates an array holding the digits of {@code number}.
490     *
491     * @param number The number to return the digits for.
492     *
493     * @return An array holding the digits of {@code number}.
494     */
495    private static int[] toDigits( final long number )
496    {
497        int i;
498        int j;
499        long subst;
500        final int[] ret = new int[ MAX_DIGITS ];
501 
502        for ( i = MAX_DIGITS - 1; i >= 0; i-- )
503        {
504            for ( j = i + 1, subst = 0L; j < MAX_DIGITS; j++ )
505            {
506                subst += ret[j] * EXP10[j];
507            }
508            ret[i] = (int) Math.floor( ( number - subst ) / EXP10[i] );
509        }
510 
511        return ret;
512    }
513 
514    /**
515     * Creates a string representing the properties of the instance.
516     *
517     * @return A string representing the properties of the instance.
518     */
519    private String internalString()
520    {
521        return new StringBuffer( 500 ).append( "{accountCode=" ).append( this.kto ).append( '}' ).toString();
522    }
523 
524    /**
525     * Gets the current cache instance.
526     *
527     * @return Current cache instance.
528     */
529    private static Map getCache()
530    {
531        Map cache = (Map) cacheReference.get();
532        if ( cache == null )
533        {
534            cache = Collections.synchronizedMap( new HashMap( 1024 ) );
535            cacheReference = new SoftReference( cache );
536        }
537 
538        return cache;
539    }
540 
541    /**
542     * Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer
543     * as this object is less than, equal to, or greater than the specified object.<p>
544     *
545     * @param o The Object to be compared.
546     * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than
547     * the specified object.
548     *
549     * @throws NullPointerException if {@code o} is {@code null}.
550     * @throws ClassCastException if the specified object's type prevents it from being compared to this Object.
551     */
552    public int compareTo( final Object o )
553    {
554        if ( o == null )
555        {
556            throw new NullPointerException( "o" );
557        }
558        if ( !( o instanceof Kontonummer ) )
559        {
560            throw new ClassCastException( o.getClass().getName() );
561        }
562 
563        int result = 0;
564        final Kontonummer that = (Kontonummer) o;
565 
566        if ( !this.equals( that ) )
567        {
568            result = this.kto > that.kto ? 1 : -1;
569        }
570 
571        return result;
572    }
573 
574    /**
575     * Indicates whether some other object is equal to this one.
576     *
577     * @param o The reference object with which to compare.
578     *
579     * @return {@code true} if this object is the same as {@code o}; {@code false} otherwise.
580     */
581    public boolean equals( final Object o )
582    {
583        boolean equal = o == this;
584 
585        if ( !equal && o instanceof Kontonummer )
586        {
587            equal = this.kto == ( (Kontonummer) o ).kto;
588        }
589 
590        return equal;
591    }
592 
593    /**
594     * Returns a hash code value for this object.
595     *
596     * @return A hash code value for this object.
597     */
598    public int hashCode()
599    {
600        return (int) ( this.kto ^ ( this.kto >>> 32 ) );
601    }
602 
603    /**
604     * Returns a string representation of the object.
605     *
606     * @return A string representation of the object.
607     */
608    public String toString()
609    {
610        return super.toString() + this.internalString();
611    }
612 
613}

[all classes][org.jdtaus.banking]
EMMA 2.1.5320 (stable) (C) Vladimir Roubtsov