001/*
002 *  jDTAUS Banking API
003 *  Copyright (C) 2005 Christian Schulte
004 *  <cs@schulte.it>
005 *
006 *  This library is free software; you can redistribute it and/or
007 *  modify it under the terms of the GNU Lesser General Public
008 *  License as published by the Free Software Foundation; either
009 *  version 2.1 of the License, or any later version.
010 *
011 *  This library is distributed in the hope that it will be useful,
012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *  Lesser General Public License for more details.
015 *
016 *  You should have received a copy of the GNU Lesser General Public
017 *  License along with this library; if not, write to the Free Software
018 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
019 *
020 */
021package org.jdtaus.banking;
022
023import java.lang.ref.Reference;
024import java.lang.ref.SoftReference;
025import java.text.DecimalFormat;
026import java.text.ParseException;
027import java.text.ParsePosition;
028import java.util.Collections;
029import java.util.HashMap;
030import java.util.Map;
031
032/**
033 * Unique identifier to a particular office (branch) of a german bank.
034 * <p>A Bankleitzahl (BLZ) is a positive integer with a maximum of eight digits. For further information see the
035 * <a href="../../../doc-files/merkblatt_bankleitzahlendatei.pdf">Merkblatt Bankleitzahlendatei</a>. An updated version of the document
036 * may be found at <a href="http://www.bundesbank.de">Deutsche Bundesbank</a>.</p>
037 *
038 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
039 * @version $JDTAUS: Bankleitzahl.java 8725 2012-10-04 21:26:50Z schulte $
040 *
041 * @see BankleitzahlenVerzeichnis
042 */
043public final class Bankleitzahl extends Number implements Comparable
044{
045
046    /**
047     * Constant for the electronic format of a Bankleitzahl.
048     * <p>The electronic format of a Bankleitzahl is an eigth digit number with leading zeros omitted (e.g. 5678).</p>
049     */
050    public static final int ELECTRONIC_FORMAT = 3001;
051
052    /**
053     * Constant for the letter format of a Bankleitzahl.
054     * <p>The letter format of a Bankleitzahl is an eigth digit number with leading zeros omitted separated by spaces
055     * between the first three digits and the second three digits, and between the second three digits and the last two
056     * digits (e.g. 123 456 78).</p>
057     */
058    public static final int LETTER_FORMAT = 3002;
059
060    /** Maximum number of digits of a Bankleitzahl. */
061    public static final int MAX_DIGITS = 8;
062
063    /** Maximum number of characters of a Bankleitzahl. */
064    public static final int MAX_CHARACTERS = 10;
065
066    /** {@code 10^0..10^7}. */
067    private static final double[] EXP10 =
068    {
069        1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
070    };
071
072    /** Serial version UID for backwards compatibility with 1.0.x classes. */
073    private static final long serialVersionUID = -3329406998979147668L;
074
075    /** Used to cache instances. */
076    private static volatile Reference cacheReference = new SoftReference( null );
077
078    /**
079     * German bank code.
080     * @serial
081     */
082    private int blz;
083
084    /**
085     * Clearing area code of this Bankleitzahl.
086     * @serial
087     */
088    private int clearingArea;
089
090    /**
091     * Locality code of this Bankleitzahl.
092     * @serial
093     */
094    private int localityCode;
095
096    /**
097     * Network code of this Bankleitzahl.
098     * @serial
099     */
100    private int networkCode;
101
102    /**
103     * Institute code of this Bankleitzahl.
104     * @serial
105     */
106    private int instituteCode;
107
108    /**
109     * Creates a new {@code Bankleitzahl} instance.
110     *
111     * @param bankCode The integer to create an instance from.
112     *
113     * @throws IllegalArgumentException if {@code bankCode} is negative, zero, greater than 99999999 or its first digit
114     * is either zero or nine.
115     *
116     * @see #checkBankleitzahl(Number)
117     */
118    private Bankleitzahl( final Number bankCode )
119    {
120        if ( !Bankleitzahl.checkBankleitzahl( bankCode ) )
121        {
122            throw new IllegalArgumentException( bankCode.toString() );
123        }
124
125        final int[] digits = Bankleitzahl.toDigits( bankCode.longValue() );
126        final long lCode = bankCode.longValue();
127
128        this.clearingArea = digits[7];
129        this.localityCode = (int) Math.floor( lCode / Bankleitzahl.EXP10[5] );
130        this.networkCode = digits[4];
131        this.instituteCode =
132            (int) Math.floor( lCode - digits[7] * Bankleitzahl.EXP10[7] -
133                              digits[6] * Bankleitzahl.EXP10[6] -
134                              digits[5] * Bankleitzahl.EXP10[5] -
135                              digits[4] * Bankleitzahl.EXP10[4] );
136
137        this.blz = bankCode.intValue();
138    }
139
140    /**
141     * Parses text from a string to produce a {@code Bankleitzahl}.
142     * <p>The method attempts to parse text starting at the index given by {@code pos}. If parsing succeeds, then the
143     * index of {@code pos} is updated to the index after the last character used (parsing does not necessarily use all
144     * characters up to the end of the string), and the parsed value is returned. The updated {@code pos} can be used to
145     * indicate the starting point for the next call to this method.</p>
146     *
147     * @param bankCode A Bankleitzahl in either electronic or letter format.
148     * @param pos A {@code ParsePosition} object with index and error index information as described above.
149     *
150     * @return The parsed value, or {@code null} if the parse fails.
151     *
152     * @throws NullPointerException if either {@code bankCode} or {@code pos} is {@code null}.
153     */
154    public static Bankleitzahl parse( final String bankCode, final ParsePosition pos )
155    {
156        if ( bankCode == null )
157        {
158            throw new NullPointerException( "bankCode" );
159        }
160        if ( pos == null )
161        {
162            throw new NullPointerException( "pos" );
163        }
164
165        Bankleitzahl ret = null;
166        boolean sawSpace = false;
167        boolean failed = false;
168        final ParsePosition fmtPos = new ParsePosition( 0 );
169        final int len = bankCode.length();
170        final int startIndex = pos.getIndex();
171        final int maxIndex = startIndex + MAX_CHARACTERS;
172        final StringBuffer digits = new StringBuffer( MAX_DIGITS );
173        int mode = ELECTRONIC_FORMAT;
174        int part = 0;
175        int partStart = 0;
176        int partEnd = 2;
177        int digit = 0;
178        int i = startIndex;
179
180        for ( ; i < len && i < maxIndex && digits.length() < MAX_DIGITS; i++ )
181        {
182            final char c = bankCode.charAt( i );
183
184            if ( Character.isDigit( c ) )
185            {
186                sawSpace = false;
187
188                if ( mode == LETTER_FORMAT )
189                {
190                    if ( digit < partStart || digit > partEnd )
191                    {
192                        failed = true;
193                    }
194                    else
195                    {
196                        digits.append( c );
197                    }
198                }
199                else
200                {
201                    digits.append( c );
202                }
203
204                digit++;
205            }
206            else if ( c == ' ' )
207            {
208                if ( sawSpace || i == startIndex || ( mode == ELECTRONIC_FORMAT && digit != 3 ) )
209                {
210                    failed = true;
211                }
212                else
213                {
214                    mode = LETTER_FORMAT;
215                    switch ( part )
216                    {
217                        case 0:
218                            partStart = 3;
219                            partEnd = 5;
220                            break;
221                        case 1:
222                            partStart = 6;
223                            partEnd = 7;
224                            break;
225                        default:
226                            failed = true;
227                            break;
228                    }
229                    part++;
230
231                    if ( digit < partStart || digit > partEnd )
232                    {
233                        failed = true;
234                    }
235                }
236
237                sawSpace = true;
238            }
239            else
240            {
241                failed = true;
242            }
243
244            if ( failed )
245            {
246                pos.setErrorIndex( i );
247                break;
248            }
249        }
250
251        if ( !failed )
252        {
253            final Number num = new DecimalFormat( "########" ).parse( digits.toString(), fmtPos );
254
255            if ( num != null && fmtPos.getErrorIndex() == -1 )
256            {
257                final String key = num.toString();
258                ret = (Bankleitzahl) getCache().get( key );
259
260                if ( ret == null )
261                {
262                    if ( !Bankleitzahl.checkBankleitzahl( num ) )
263                    {
264                        pos.setErrorIndex( startIndex );
265                        ret = null;
266                    }
267                    else
268                    {
269                        pos.setIndex( i );
270                        ret = new Bankleitzahl( num );
271                        getCache().put( key, ret );
272                    }
273                }
274                else
275                {
276                    pos.setIndex( i );
277                }
278            }
279            else
280            {
281                pos.setErrorIndex( startIndex );
282            }
283        }
284
285        return ret;
286    }
287
288    /**
289     * Parses text from the beginning of the given string to produce a {@code Bankleitzahl}.
290     * <p>Unlike the {@link #parse(String, ParsePosition)} method this method throws a {@code ParseException} if
291     * {@code bankCode} cannot be parsed or is of invalid length.</p>
292     *
293     * @param bankCode A Bankleitzahl in either electronic or letter format.
294     *
295     * @return The parsed value.
296     *
297     * @throws NullPointerException if {@code bankCode} is {@code null}.
298     * @throws ParseException if the parse fails or {@code bankCode} is of invalid length.
299     */
300    public static Bankleitzahl parse( final String bankCode ) throws ParseException
301    {
302        if ( bankCode == null )
303        {
304            throw new NullPointerException( "bankCode" );
305        }
306
307        Bankleitzahl blz = (Bankleitzahl) getCache().get( bankCode );
308
309        if ( blz == null )
310        {
311            final ParsePosition pos = new ParsePosition( 0 );
312            blz = Bankleitzahl.parse( bankCode, pos );
313
314            if ( blz == null || pos.getErrorIndex() != -1 || pos.getIndex() < bankCode.length() )
315            {
316                throw new ParseException( bankCode, pos.getErrorIndex() != -1 ? pos.getErrorIndex() : pos.getIndex() );
317            }
318            else
319            {
320                getCache().put( bankCode, blz );
321            }
322        }
323
324        return blz;
325    }
326
327    /**
328     * Gets a {@code Bankleitzahl} for a given number.
329     *
330     * @param bankCode A number to get a {@code Bankleitzahl} for.
331     *
332     * @return An instance for {@code bankCode}.
333     *
334     * @throws NullPointerException if {@code bankCode} is {@code null}.
335     * @throws IllegalArgumentException if {@code bankCode} is negative, zero, greater than 99999999 or its first digit
336     * is either zero or nine.
337     *
338     * @see #checkBankleitzahl(Number)
339     */
340    public static Bankleitzahl valueOf( final Number bankCode )
341    {
342        if ( bankCode == null )
343        {
344            throw new NullPointerException( "bankCode" );
345        }
346
347        final String key = bankCode.toString();
348        Bankleitzahl ret = (Bankleitzahl) getCache().get( key );
349
350        if ( ret == null )
351        {
352            ret = new Bankleitzahl( bankCode );
353            getCache().put( key, ret );
354        }
355
356        return ret;
357    }
358
359    /**
360     * Parses text from the beginning of the given string to produce a {@code Bankleitzahl}.
361     * <p>Unlike the {@link #parse(String)} method this method throws an {@code IllegalArgumentException} if
362     * {@code bankCode} cannot be parsed or is of invalid length.</p>
363     *
364     * @param bankCode A Bankleitzahl in either electronic or letter format.
365     *
366     * @return The parsed value.
367     *
368     * @throws NullPointerException if {@code bankCode} is {@code null}.
369     * @throws IllegalArgumentException if the parse fails or {@code bankCode} is of invalid length.
370     */
371    public static Bankleitzahl valueOf( final String bankCode )
372    {
373        try
374        {
375            return Bankleitzahl.parse( bankCode );
376        }
377        catch ( final ParseException e )
378        {
379            throw (IllegalArgumentException) new IllegalArgumentException( bankCode ).initCause( e );
380        }
381    }
382
383    /**
384     * Checks a given number to conform to a Bankleitzahl.
385     *
386     * @param bankCode The number to check.
387     *
388     * @return {@code true} if {@code bankCode} is a valid Bankleitzahl; {@code false} if not.
389     */
390    public static boolean checkBankleitzahl( final Number bankCode )
391    {
392        boolean valid = bankCode != null;
393
394        if ( valid )
395        {
396            final long num = bankCode.longValue();
397            valid = num > 0L && num < 100000000L;
398            if ( valid && num > 9999999 )
399            {
400                final int[] digits = Bankleitzahl.toDigits( num );
401                valid = digits[7] != 0 && digits[7] != 9;
402            }
403        }
404
405        return valid;
406    }
407
408    /**
409     * Returns this Bankleitzahl as an int value.
410     *
411     * @return This Bankleitzahl as an int value.
412     */
413    public int intValue()
414    {
415        return this.blz;
416    }
417
418    /**
419     * Returns this Bankleitzahl as a long value.
420     *
421     * @return This Bankleitzahl as a long value.
422     */
423    public long longValue()
424    {
425        return this.blz;
426    }
427
428    /**
429     * Returns this Bankleitzahl as a float value.
430     *
431     * @return This Bankleitzahl as a float value.
432     */
433    public float floatValue()
434    {
435        return this.blz;
436    }
437
438    /**
439     * Returns this Bankleitzahl as a double value.
440     *
441     * @return This Bankleitzahl as a double value.
442     */
443    public double doubleValue()
444    {
445        return this.blz;
446    }
447
448    /**
449     * Gets a flag indicating that this Bankleitzahl provides a clearing area code.
450     *
451     * @return {@code true} if property {@code clearingAreaCode} is supported by this instance; {@code false} if
452     * property {@code clearingAreaCode} is not supported by this instance.
453     *
454     * @see #getClearingAreaCode()
455     */
456    public boolean isClearingAreaCodeSupported()
457    {
458        return this.blz > 9999999;
459    }
460
461    /**
462     * Gets the clearing area code of this Bankleitzahl.
463     * <p><ol>
464     * <li>Berlin, Brandenburg, Mecklenburg-Vorpommern</li>
465     * <li>Bremen, Hamburg, Niedersachsen, Schleswig-Holstein</li>
466     * <li>Rheinland (Regierungsbezirke Düsseldorf, Köln)</li>
467     * <li>Westfalen</li>
468     * <li>Hessen, Rheinland-Pfalz, Saarland</li>
469     * <li>Baden-Württemberg</li>
470     * <li>Bayern</li>
471     * <li>Sachsen, Sachsen-Anhalt, Thüringen</li>
472     * </ol></p>
473     *
474     * @return Code identifying the clearing area of this Bankleitzahl.
475     *
476     * @throws UnsupportedOperationException if this Bankleitzahl does not provide clearing area information.
477     *
478     * @see #isClearingAreaCodeSupported()
479     *
480     * @deprecated Renamed to {@link #getClearingAreaCode() }.
481     */
482    public int getClearingArea()
483    {
484        return this.getClearingAreaCode();
485    }
486
487    /**
488     * Gets the clearing area code of this Bankleitzahl.
489     * <p><ol>
490     * <li>Berlin, Brandenburg, Mecklenburg-Vorpommern</li>
491     * <li>Bremen, Hamburg, Niedersachsen, Schleswig-Holstein</li>
492     * <li>Rheinland (Regierungsbezirke Düsseldorf, Köln)</li>
493     * <li>Westfalen</li>
494     * <li>Hessen, Rheinland-Pfalz, Saarland</li>
495     * <li>Baden-Württemberg</li>
496     * <li>Bayern</li>
497     * <li>Sachsen, Sachsen-Anhalt, Thüringen</li>
498     * </ol></p>
499     *
500     * @return Code identifying the clearing area of this Bankleitzahl.
501     *
502     * @throws UnsupportedOperationException if this Bankleitzahl does not provide clearing area information.
503     *
504     * @see #isClearingAreaCodeSupported()
505     */
506    public int getClearingAreaCode()
507    {
508        if ( !this.isClearingAreaCodeSupported() )
509        {
510            throw new UnsupportedOperationException();
511        }
512
513        return this.clearingArea;
514    }
515
516    /**
517     * Gets a flag indicating that this Bankleitzahl provides a locality code.
518     *
519     * @return {@code true} if property {@code localityCode} is supported by this instance; {@code false} if property
520     * {@code localityCode} is not supported by this instance.
521     *
522     * @see #getLocalityCode()
523     */
524    public boolean isLocalityCodeSupported()
525    {
526        return this.blz > 99999;
527    }
528
529    /**
530     * Gets the locality code of this Bankleitzahl.
531     *
532     * @return Locality code of this Bankleitzahl.
533     *
534     * @throws UnsupportedOperationException if this Bankleitzahl does not provide a locality code.
535     *
536     * @see #isLocalityCodeSupported()
537     */
538    public int getLocalityCode()
539    {
540        if ( !this.isLocalityCodeSupported() )
541        {
542            throw new UnsupportedOperationException();
543        }
544
545        return this.localityCode;
546    }
547
548    /**
549     * Gets a flag indicating that this Bankleitzahl provides a network code.
550     *
551     * @return {@code true} if property {@code networkCode} is supported by this instance; {@code false} if property
552     * {@code networkCode} is not supported by this instance.
553     *
554     * @see #getNetworkCode()
555     */
556    public boolean isNetworkCodeSupported()
557    {
558        return this.blz > 9999;
559    }
560
561    /**
562     * Gets the network code of this Bankleitzahl.
563     * <p><table border="0">
564     * <tr>
565     *   <td>0</td>
566     *   <td>Deutsche Bundesbank</td>
567     * </tr>
568     * <tr>
569     *   <td>1 - 3</td>
570     *   <td>
571     * Kreditinstitute, soweit nicht in einer der anderen Gruppen erfasst
572     *   </td>
573     * </tr>
574     * <tr>
575     *   <td>4</td>
576     *   <td>Commerzbank</td>
577     * </tr>
578     * <tr>
579     *   <td>5</td>
580     *   <td>Girozentralen und Sparkassen</td>
581     * </tr>
582     * <tr>
583     *   <td>6 + 9</td>
584     *   <td>
585     * Genossenschaftliche Zentralbanken, Kreditgenossenschaften sowie ehemalige
586     * Genossenschaften
587     *   </td>
588     * </tr>
589     * <tr>
590     *   <td>7</td>
591     *   <td>Deutsche Bank</td>
592     * </tr>
593     * <tr>
594     *   <td>8</td>
595     *   <td>Dresdner Bank</td>
596     * </tr>
597     * </table></p>
598     *
599     * @return Network code of this Bankleitzahl.
600     *
601     * @throws UnsupportedOperationException if this Bankleitzahl does not provide a network code.
602     *
603     * @see #isNetworkCodeSupported()
604     */
605    public int getNetworkCode()
606    {
607        if ( !this.isNetworkCodeSupported() )
608        {
609            throw new UnsupportedOperationException();
610        }
611
612        return this.networkCode;
613    }
614
615    /**
616     * Gets the institute code of this Bankleitzahl.
617     *
618     * @return Institute code of this Bankleitzahl.
619     */
620    public int getInstituteCode()
621    {
622        return this.instituteCode;
623    }
624
625    /**
626     * Formats a Bankleitzahl and appends the resulting text to the given string buffer.
627     *
628     * @param style The style to use ({@code ELECTRONIC_FORMAT} or {@code LETTER_FORMAT}).
629     * @param toAppendTo The buffer to which the formatted text is to be appended.
630     *
631     * @return The value passed in as {@code toAppendTo}.
632     *
633     * @throws NullPointerException if {@code toAppendTo} is {@code null}.
634     * @throws IllegalArgumentException if {@code style} is neither {@code ELECTRONIC_FORMAT} nor {@code LETTER_FORMAT}.
635     *
636     * @see #ELECTRONIC_FORMAT
637     * @see #LETTER_FORMAT
638     */
639    public StringBuffer format( final int style, final StringBuffer toAppendTo )
640    {
641        if ( toAppendTo == null )
642        {
643            throw new NullPointerException( "toAppendTo" );
644        }
645        if ( style != Bankleitzahl.ELECTRONIC_FORMAT && style != Bankleitzahl.LETTER_FORMAT )
646        {
647            throw new IllegalArgumentException( Integer.toString( style ) );
648        }
649
650        final int[] digits = Bankleitzahl.toDigits( this.blz );
651        for ( int i = digits.length - 1, lastDigit = 0; i >= 0; i-- )
652        {
653            if ( digits[i] != 0 || lastDigit > 0 )
654            {
655                toAppendTo.append( digits[i] );
656                lastDigit++;
657            }
658
659            if ( style == Bankleitzahl.LETTER_FORMAT && ( lastDigit == 3 || lastDigit == 6 ) )
660            {
661                toAppendTo.append( ' ' );
662            }
663        }
664
665        return toAppendTo;
666    }
667
668    /**
669     * Formats a Bankleitzahl to produce a string. Same as
670     * <blockquote>
671     * {@link #format(int, StringBuffer) format<code>(style, new StringBuffer()).toString()</code>}
672     * </blockquote>
673     *
674     * @param style The style to use ({@code ELECTRONIC_FORMAT} or {@code LETTER_FORMAT}).
675     *
676     * @return The formatted string.
677     *
678     * @throws IllegalArgumentException if {@code style} is neither {@code ELECTRONIC_FORMAT} nor {@code LETTER_FORMAT}.
679     *
680     * @see #ELECTRONIC_FORMAT
681     * @see #LETTER_FORMAT
682     */
683    public String format( final int style )
684    {
685        return this.format( style, new StringBuffer() ).toString();
686    }
687
688    /**
689     * Formats a Bankleitzahl to produce a string. Same as
690     * <blockquote>
691     * {@link #format(int) bankleitzahl.format(ELECTRONIC_FORMAT)}
692     * </blockquote>
693     *
694     * @param bankleitzahl The {@code Bankleitzahl} instance to format.
695     *
696     * @return The formatted string.
697     *
698     * @throws NullPointerException if {@code bankleitzahl} is {@code null}.
699     */
700    public static String toString( final Bankleitzahl bankleitzahl )
701    {
702        if ( bankleitzahl == null )
703        {
704            throw new NullPointerException( "bankleitzahl" );
705        }
706
707        return bankleitzahl.format( ELECTRONIC_FORMAT );
708    }
709
710    /**
711     * Creates an array holding the digits of {@code number}.
712     *
713     * @param number The number to return the digits for.
714     *
715     * @return An array holding the digits of {@code number}.
716     */
717    private static int[] toDigits( final long number )
718    {
719        int i;
720        int j;
721        int subst;
722        final int[] ret = new int[ MAX_DIGITS ];
723
724        for ( i = MAX_DIGITS - 1; i >= 0; i-- )
725        {
726            for ( j = i + 1, subst = 0; j < MAX_DIGITS; j++ )
727            {
728                subst += ret[j] * EXP10[j];
729            }
730            ret[i] = (int) Math.floor( ( number - subst ) / EXP10[i] );
731        }
732
733        return ret;
734    }
735
736    /**
737     * Creates a string representing the properties of the instance.
738     *
739     * @return A string representing the properties of the instance.
740     */
741    private String internalString()
742    {
743        return new StringBuffer( 500 ).append( '{' ).
744            append( "blz=" ).append( this.blz ).
745            append( ", clearingAreaCodeSupported=" ).
746            append( this.isClearingAreaCodeSupported() ).
747            append( ", clearingArea=" ).append( this.clearingArea ).
748            append( ", instituteCode=" ).append( this.instituteCode ).
749            append( ", localityCodeSupported=" ).
750            append( this.isLocalityCodeSupported() ).
751            append( ", localityCode=" ).append( this.localityCode ).
752            append( ", networkCodeSupported=" ).
753            append( this.isNetworkCodeSupported() ).
754            append( ", networkCode=" ).append( this.networkCode ).
755            append( '}' ).toString();
756
757    }
758
759    /**
760     * Gets the current cache instance.
761     *
762     * @return Current cache instance.
763     */
764    private static Map getCache()
765    {
766        Map cache = (Map) cacheReference.get();
767        if ( cache == null )
768        {
769            cache = Collections.synchronizedMap( new HashMap( 1024 ) );
770            cacheReference = new SoftReference( cache );
771        }
772
773        return cache;
774    }
775
776    /**
777     * Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer
778     * as this object is less than, equal to, or greater than the specified object.<p>
779     *
780     * @param o The Object to be compared.
781     * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than
782     * the specified object.
783     *
784     * @throws NullPointerException if {@code o} is {@code null}.
785     * @throws ClassCastException if the specified object's type prevents it from being compared to this Object.
786     */
787    public int compareTo( final Object o )
788    {
789        if ( o == null )
790        {
791            throw new NullPointerException( "o" );
792        }
793        if ( !( o instanceof Bankleitzahl ) )
794        {
795            throw new ClassCastException( o.getClass().getName() );
796        }
797
798        int result = 0;
799        final Bankleitzahl that = (Bankleitzahl) o;
800
801        if ( !this.equals( that ) )
802        {
803            result = this.blz > that.blz
804                     ? 1
805                     : -1;
806        }
807
808        return result;
809    }
810
811    /**
812     * Indicates whether some other object is equal to this one.
813     *
814     * @param o The reference object with which to compare.
815     *
816     * @return {@code true} if this object is the same as {@code o}; {@code false} otherwise.
817     */
818    public boolean equals( final Object o )
819    {
820        boolean equal = o == this;
821
822        if ( !equal && o instanceof Bankleitzahl )
823        {
824            equal = this.blz == ( (Bankleitzahl) o ).blz;
825        }
826
827        return equal;
828    }
829
830    /**
831     * Returns a hash code value for this object.
832     *
833     * @return A hash code value for this object.
834     */
835    public int hashCode()
836    {
837        return this.blz;
838    }
839
840    /**
841     * Returns a string representation of the object.
842     *
843     * @return A string representation of the object.
844     */
845    public String toString()
846    {
847        return super.toString() + this.internalString();
848    }
849
850}