1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.jdtaus.iso13616;
25
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.Serializable;
29 import java.lang.ref.Reference;
30 import java.lang.ref.SoftReference;
31 import java.math.BigInteger;
32 import java.net.URL;
33 import java.text.ParseException;
34 import java.text.ParsePosition;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Formattable;
38 import java.util.Formatter;
39 import java.util.HashMap;
40 import java.util.IllegalFormatFlagsException;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Properties;
44 import java.util.concurrent.ConcurrentHashMap;
45 import static java.util.FormattableFlags.ALTERNATE;
46 import static java.util.FormattableFlags.LEFT_JUSTIFY;
47 import static java.util.FormattableFlags.UPPERCASE;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 @javax.annotation.Generated( value = "org.jomc.tools.SourceFileProcessor 1.5", comments = "See http://www.jomc.org/jomc/1.5/jomc-tools-1.5" )
80
81
82 public final class IBAN implements CharSequence, Comparable<IBAN>, Formattable, Serializable
83 {
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 private static final class Structure
105 {
106
107
108
109
110
111
112
113 private static final class Part
114 {
115
116
117 private final char type;
118
119
120 private final String literal;
121
122
123 private final Integer fixedLength;
124
125
126 private final Integer maximumLength;
127
128
129
130
131
132
133
134
135
136 private Part( final char type, final String literal, final Integer fixedLength,
137 final Integer maximumLength )
138 {
139 super();
140 this.type = type;
141 this.literal = literal;
142 this.fixedLength = fixedLength;
143 this.maximumLength = maximumLength;
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 private char getType()
160 {
161 return this.type;
162 }
163
164
165
166
167
168
169 private String getLiteral()
170 {
171 return this.literal;
172 }
173
174
175
176
177
178
179
180 private Integer getFixedLength()
181 {
182 return this.fixedLength;
183 }
184
185
186
187
188
189
190
191 private Integer getMaximumLength()
192 {
193 return this.maximumLength;
194 }
195
196 }
197
198
199 private final List<Part> parts;
200
201
202
203
204
205
206 private Structure( final List<Part> parts )
207 {
208 super();
209 this.parts = parts;
210 }
211
212
213
214
215
216
217 private List<Part> getParts()
218 {
219 return this.parts;
220 }
221
222 }
223
224
225
226
227
228
229
230 private static final class ParseContext
231 {
232
233
234 private final String text;
235
236
237 private final ParsePosition parsePosition;
238
239
240 private final IbanFormat format;
241
242
243 private int length;
244
245
246 private Character previous;
247
248
249
250
251
252
253
254
255 private ParseContext( final String text, final ParsePosition pos, final IbanFormat format )
256 {
257 super();
258 this.text = text;
259 this.parsePosition = pos;
260 this.format = format;
261 this.length = 0;
262 }
263
264 }
265
266
267 private static final long serialVersionUID = -8123668345632147105L;
268
269
270 private static volatile Reference<Map<String, IBAN>> cacheReference = new SoftReference<Map<String, IBAN>>( null );
271
272
273 private static final Map<String, Boolean> SEPA_COUNTRY_FLAGS = new HashMap<String, Boolean>( 128 );
274
275
276 private static final Map<String, Structure> BBAN_STRUCTURES = new HashMap<String, Structure>( 128 );
277
278
279 private static final Map<String, List<Number>> BBAN_BANK_ID_PARTS = new HashMap<String, List<Number>>( 128 );
280
281
282 private static final Map<String, List<Number>> BBAN_BRANCH_ID_PARTS = new HashMap<String, List<Number>>( 128 );
283
284
285 private static final Map<String, Structure> IBAN_STRUCTURES = new HashMap<String, Structure>( 128 );
286
287
288 private static final Map<String, List<Number>> IBAN_BANK_ID_PARTS = new HashMap<String, List<Number>>( 128 );
289
290
291 private static final Map<String, List<Number>> IBAN_BRANCH_ID_PARTS = new HashMap<String, List<Number>>( 128 );
292
293
294 private static final List<String> COUNTRIES = new ArrayList<String>( 128 );
295
296
297 private static final BigInteger INTEGER_97 = BigInteger.valueOf( 97L );
298
299
300 private static final BigInteger INTEGER_98 = BigInteger.valueOf( 98L );
301
302 static
303 {
304 InputStream in = null;
305
306 try
307 {
308 final Properties properties = new Properties();
309 final URL resource = IBAN.class.getResource( "IBAN.properties" );
310 assert resource != null : "Expected resource 'IBAN.properties' to exist.";
311
312 if ( resource != null )
313 {
314 in = resource.openStream();
315 properties.load( in );
316 in.close();
317 in = null;
318
319 if ( properties.containsKey( "countries" ) )
320 {
321 final String[] countries = properties.getProperty( "countries" ).split( "\\|" );
322 COUNTRIES.addAll( Arrays.asList( countries ) );
323
324 for ( int i = 0, s0 = countries.length; i < s0; i++ )
325 {
326 if ( countries[i].length() != 2 )
327 {
328 throw new AssertionError( countries[i] );
329 }
330
331 final String bbanStructure = properties.getProperty( countries[i] + ".bban.structure" );
332 final String bbanBankIds = properties.getProperty( countries[i] + ".bban.bankidparts" );
333 final String bbanBranchIds = properties.getProperty( countries[i] + ".bban.branchidparts" );
334 final String ibanStructure = properties.getProperty( countries[i] + ".iban.structure" );
335 final String ibanBankIds = properties.getProperty( countries[i] + ".iban.bankidparts" );
336 final String ibanBranchIds = properties.getProperty( countries[i] + ".iban.branchidparts" );
337 final String sepa = properties.getProperty( countries[i] + ".sepa" );
338
339 SEPA_COUNTRY_FLAGS.put( countries[i], Boolean.valueOf( sepa ) );
340
341 if ( bbanStructure != null )
342 {
343 BBAN_STRUCTURES.put( countries[i], parseStructure( bbanStructure ) );
344 assert bbanBankIds != null :
345 "Expected '" + countries[i] + ".bban.bankidparts' property to exists.";
346
347 if ( bbanBankIds != null )
348 {
349 BBAN_BANK_ID_PARTS.put( countries[i], splitNumbers( bbanBankIds ) );
350 }
351
352 if ( bbanBranchIds != null )
353 {
354 BBAN_BRANCH_ID_PARTS.put( countries[i], splitNumbers( bbanBranchIds ) );
355 }
356 }
357
358 if ( ibanStructure != null )
359 {
360 IBAN_STRUCTURES.put( countries[i], parseStructure( ibanStructure ) );
361 assert ibanBankIds != null :
362 "Expected '" + countries[i] + ".iban.bankidparts' property to exists.";
363
364 if ( ibanBankIds != null )
365 {
366 IBAN_BANK_ID_PARTS.put( countries[i], splitNumbers( ibanBankIds ) );
367 }
368
369 if ( ibanBranchIds != null )
370 {
371 IBAN_BRANCH_ID_PARTS.put( countries[i], splitNumbers( ibanBranchIds ) );
372 }
373 }
374 }
375 }
376 }
377 }
378 catch ( final ParseException e )
379 {
380 throw new AssertionError( e );
381 }
382 catch ( final IOException e )
383 {
384 throw new AssertionError( e );
385 }
386 finally
387 {
388 try
389 {
390 if ( in != null )
391 {
392 in.close();
393 }
394 }
395 catch ( final IOException e )
396 {
397 throw new AssertionError( e );
398 }
399 }
400 }
401
402
403 public static final int MAX_CHARACTERS = 42;
404
405
406
407
408
409 private String countryCode;
410
411
412
413
414
415 private boolean sepaCountry;
416
417
418
419
420
421 private String bankIdentifier;
422
423
424
425
426
427 private String branchIdentifier;
428
429
430
431
432
433 private String electronicFormat;
434
435
436
437
438
439 private String letterFormat;
440
441
442
443
444
445 private Comparable[] parts;
446
447
448 private transient String string;
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463 private IBAN( final String countryCode, final boolean sepaCountry, final String bankIdentifier,
464 final String branchIdentifier, final String electronicFormat, final String letterFormat,
465 final Comparable[] parts )
466 {
467 super();
468 this.countryCode = countryCode;
469 this.sepaCountry = sepaCountry;
470 this.bankIdentifier = bankIdentifier;
471 this.branchIdentifier = branchIdentifier;
472 this.electronicFormat = electronicFormat;
473 this.letterFormat = letterFormat;
474 this.parts = parts;
475 getCache().put( electronicFormat, this );
476 getCache().put( letterFormat, this );
477 }
478
479
480
481
482
483
484
485
486 public static String[] getCountryCodes()
487 {
488 return COUNTRIES.toArray( new String[ COUNTRIES.size() ] );
489 }
490
491
492
493
494
495
496
497
498 public String getCountryCode()
499 {
500 return this.countryCode;
501 }
502
503
504
505
506
507
508
509 public boolean isSepaCountry()
510 {
511 return this.sepaCountry;
512 }
513
514
515
516
517
518
519 public String getBankIdentifier()
520 {
521 return this.bankIdentifier;
522 }
523
524
525
526
527
528
529 public String getBranchIdentifier()
530 {
531 return this.branchIdentifier;
532 }
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549 public static IBAN parse( final String countryCode, final String bban ) throws IbanSyntaxException
550 {
551 if ( countryCode == null )
552 {
553 throw new NullPointerException( "countryCode" );
554 }
555 if ( bban == null )
556 {
557 throw new NullPointerException( "bban" );
558 }
559
560 final Structure structure = BBAN_STRUCTURES.get( countryCode );
561
562 if ( structure == null )
563 {
564 throw new IllegalArgumentException( countryCode );
565 }
566
567 final List<Number> bankIdParts = BBAN_BANK_ID_PARTS.get( countryCode );
568 final List<Number> branchIdParts = BBAN_BRANCH_ID_PARTS.get( countryCode );
569 final boolean sepa_country = SEPA_COUNTRY_FLAGS.get( countryCode );
570
571
572 final StringBuilder electronic_format_builder = new StringBuilder( MAX_CHARACTERS );
573 final StringBuilder bank_id_builder = new StringBuilder( MAX_CHARACTERS );
574 final StringBuilder branch_id_builder = new StringBuilder( MAX_CHARACTERS );
575 final List<Comparable<?>> comparables = new ArrayList<Comparable<?>>( structure.getParts().size() + 2 );
576 final ParseContext context =
577 new ParseContext( bban, new ParsePosition( 0 ), bban.length() > 4 && bban.charAt( 4 ) == ' '
578 ? IbanFormat.PRINT : IbanFormat.ELECTRONIC );
579
580 for ( int p = 0, s0 = structure.getParts().size(); p < s0 && context.parsePosition.getErrorIndex() < 0; p++ )
581 {
582 final Integer idKey = Integer.valueOf( p + 1 );
583 final Structure.Part part = structure.getParts().get( p );
584 final String chars = parsePart( context, part );
585
586 if ( context.parsePosition.getErrorIndex() < 0 )
587 {
588 if ( part.getFixedLength() != null && chars.length() != part.getFixedLength().intValue() )
589 {
590 throw new IbanSyntaxException( bban, context.parsePosition.getIndex() );
591 }
592
593 electronic_format_builder.append( chars );
594
595 if ( bankIdParts != null && bankIdParts.contains( idKey ) )
596 {
597 bank_id_builder.append( chars );
598 }
599 if ( branchIdParts != null && branchIdParts.contains( idKey ) )
600 {
601 branch_id_builder.append( chars );
602 }
603
604 switch ( part.getType() )
605 {
606 case 'n':
607 comparables.add( new BigInteger( chars ) );
608 break;
609 default:
610 comparables.add( chars );
611 break;
612 }
613 }
614 else
615 {
616 throw new IbanSyntaxException( bban, context.parsePosition.getErrorIndex() );
617 }
618 }
619
620
621 final StringBuilder integer_builder = new StringBuilder( MAX_CHARACTERS * 2 );
622 appendIso7064Digits( integer_builder, electronic_format_builder.toString() );
623 appendIso7064Digits( integer_builder, countryCode );
624 appendIso7064Digits( integer_builder, "00" );
625
626 final BigInteger integer = new BigInteger( integer_builder.toString() );
627 final BigInteger checksum = INTEGER_98.subtract( integer.remainder( INTEGER_97 ) );
628 final String checksumPart = checksum.compareTo( BigInteger.TEN ) < 0 ? "0" + checksum : checksum.toString();
629
630 comparables.add( 0, checksumPart );
631 comparables.add( 0, countryCode );
632
633 electronic_format_builder.insert( 0, checksumPart );
634 electronic_format_builder.insert( 0, countryCode );
635
636 return new IBAN( countryCode, sepa_country, bank_id_builder.toString(),
637 branch_id_builder.length() > 0 ? branch_id_builder.toString() : null,
638 electronic_format_builder.toString(), toLetterFormat( electronic_format_builder.toString() ),
639 comparables.toArray( new Comparable<?>[ comparables.size() ] ) );
640
641 }
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658 public static IBAN parse( final String text, final ParsePosition pos ) throws IbanCheckDigitsException
659 {
660 if ( text == null )
661 {
662 throw new NullPointerException( "text" );
663 }
664 if ( pos == null )
665 {
666 throw new NullPointerException( "pos" );
667 }
668
669 IBAN ret = null;
670 final int begin_index = pos.getIndex();
671
672
673 if ( text.length() > begin_index + 1 )
674 {
675 final String country_code = text.substring( begin_index, begin_index + 2 );
676
677 if ( !isLiteral( country_code.charAt( 0 ) ) )
678 {
679 pos.setIndex( begin_index );
680 pos.setErrorIndex( begin_index );
681 }
682 else if ( !isLiteral( country_code.charAt( 1 ) ) )
683 {
684 pos.setIndex( begin_index );
685 pos.setErrorIndex( begin_index + 1 );
686 }
687 else
688 {
689 final Structure structure = IBAN_STRUCTURES.get( country_code );
690
691 if ( structure != null )
692 {
693 final List<Number> bankIdParts = IBAN_BANK_ID_PARTS.get( country_code );
694 final List<Number> branchIdParts = IBAN_BRANCH_ID_PARTS.get( country_code );
695 final boolean sepa_country = SEPA_COUNTRY_FLAGS.get( country_code );
696 final StringBuilder electronic_format_builder = new StringBuilder( MAX_CHARACTERS );
697 final StringBuilder bank_id_builder = new StringBuilder( MAX_CHARACTERS );
698 final StringBuilder branch_id_builder = new StringBuilder( MAX_CHARACTERS );
699 final List<Comparable<?>> comparables = new ArrayList<Comparable<?>>( structure.getParts().size() );
700 final ParseContext context =
701 new ParseContext( text, pos,
702 text.length() > begin_index + 4 && text.charAt( begin_index + 4 ) == ' '
703 ? IbanFormat.PRINT : IbanFormat.ELECTRONIC );
704
705 for ( int p = 0, s0 = structure.getParts().size();
706 p < s0 && context.parsePosition.getErrorIndex() < 0;
707 p++ )
708 {
709 final Integer idKey = Integer.valueOf( p + 1 );
710 final Structure.Part part = structure.getParts().get( p );
711 final String chars = parsePart( context, part );
712
713 if ( context.parsePosition.getErrorIndex() < 0 )
714 {
715 if ( part.getFixedLength() != null && chars.length() != part.getFixedLength().intValue() )
716 {
717 context.parsePosition.setErrorIndex( context.parsePosition.getIndex() );
718 context.parsePosition.setIndex( begin_index );
719 break;
720 }
721
722 electronic_format_builder.append( chars );
723
724 if ( bankIdParts != null && bankIdParts.contains( idKey ) )
725 {
726 bank_id_builder.append( chars );
727 }
728 if ( branchIdParts != null && branchIdParts.contains( idKey ) )
729 {
730 branch_id_builder.append( chars );
731 }
732
733 switch ( part.getType() )
734 {
735 case 'n':
736 comparables.add( new BigInteger( chars ) );
737 break;
738 default:
739 comparables.add( chars );
740 break;
741 }
742 }
743 }
744
745 if ( context.parsePosition.getErrorIndex() < 0 )
746 {
747 final StringBuilder integer_builder = new StringBuilder( MAX_CHARACTERS * 2 );
748 appendIso7064Digits( integer_builder, electronic_format_builder.substring( 4 ) );
749 appendIso7064Digits( integer_builder, electronic_format_builder.substring( 0, 4 ) );
750
751 final BigInteger integer = new BigInteger( integer_builder.toString() );
752
753 if ( integer.remainder( INTEGER_97 ).equals( BigInteger.ONE ) )
754 {
755 ret = new IBAN( country_code, sepa_country, bank_id_builder.toString(),
756 branch_id_builder.length() > 0 ? branch_id_builder.toString() : null,
757 electronic_format_builder.toString(),
758 toLetterFormat( electronic_format_builder.toString() ),
759 comparables.toArray( new Comparable<?>[ comparables.size() ] ) );
760
761 }
762 else
763 {
764 context.parsePosition.setIndex( begin_index );
765 context.parsePosition.setErrorIndex( begin_index + 2 );
766
767 throw new IbanCheckDigitsException( electronic_format_builder.toString(),
768 (Number) comparables.get( 1 ) );
769
770 }
771 }
772 }
773 else
774 {
775 pos.setIndex( begin_index );
776 pos.setErrorIndex( begin_index + 1 );
777 }
778 }
779 }
780 else
781 {
782 pos.setIndex( begin_index );
783 pos.setErrorIndex( begin_index );
784
785 if ( begin_index < text.length() && isLiteral( text.charAt( begin_index ) ) )
786 {
787 pos.setErrorIndex( begin_index + 1 );
788 }
789 }
790
791 return ret;
792 }
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809 public static IBAN parse( final String text ) throws IbanSyntaxException, IbanCheckDigitsException
810 {
811 if ( text == null )
812 {
813 throw new NullPointerException( "text" );
814 }
815
816 IBAN iban = getCache().get( text );
817
818 if ( iban == null )
819 {
820 final ParsePosition pos = new ParsePosition( 0 );
821 iban = IBAN.parse( text, pos );
822
823 if ( iban == null || pos.getErrorIndex() != -1 || pos.getIndex() < text.length() )
824 {
825 throw new IbanSyntaxException( text, pos.getErrorIndex() != -1
826 ? pos.getErrorIndex()
827 : pos.getIndex() );
828
829 }
830 }
831
832 return iban;
833 }
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850 public static IBAN valueOf( final String text )
851 {
852 if ( text == null )
853 {
854 throw new NullPointerException( "text" );
855 }
856
857 try
858 {
859 return IBAN.parse( text );
860 }
861 catch ( final IbanSyntaxException e )
862 {
863 throw new IllegalArgumentException( text, e );
864 }
865 catch ( final IbanCheckDigitsException e )
866 {
867 throw new IllegalArgumentException( text, e );
868 }
869 }
870
871
872
873
874
875
876
877
878 public static boolean isIbanAlphabet( final char c )
879 {
880 return ( c >= 'A' && c <= 'Z' ) || ( c >= 'a' && c <= 'z' ) || ( c >= '0' && c <= '9' );
881 }
882
883
884
885
886
887
888
889
890
891
892
893
894
895 public static boolean peekIban( final String text ) throws IbanCheckDigitsException
896 {
897 if ( text == null )
898 {
899 throw new NullPointerException( "text" );
900 }
901
902 boolean valid = true;
903 boolean complete = true;
904
905
906 if ( text.length() > 1 )
907 {
908 final String country_code = text.substring( 0, 2 );
909 final Structure structure = IBAN_STRUCTURES.get( country_code );
910
911 if ( structure != null )
912 {
913 final StringBuilder electronic_format_builder = new StringBuilder( MAX_CHARACTERS );
914 final ParseContext context =
915 new ParseContext( text, new ParsePosition( 0 ), text.length() > 4 && text.charAt( 4 ) == ' '
916 ? IbanFormat.PRINT : IbanFormat.ELECTRONIC );
917
918 for ( int p = 0, s0 = structure.getParts().size();
919 p < s0 && context.parsePosition.getErrorIndex() < 0 && complete;
920 p++ )
921 {
922 final Structure.Part part = structure.getParts().get( p );
923 final String chars = parsePart( context, part );
924
925 if ( context.parsePosition.getErrorIndex() < 0 )
926 {
927 if ( part.getFixedLength() != null && chars.length() != part.getFixedLength().intValue() )
928 {
929 complete = false;
930 break;
931 }
932
933 electronic_format_builder.append( chars );
934 }
935 }
936
937 if ( context.parsePosition.getErrorIndex() < 0 )
938 {
939 if ( complete )
940 {
941 final StringBuilder integer_builder = new StringBuilder( MAX_CHARACTERS * 2 );
942 appendIso7064Digits( integer_builder, electronic_format_builder.substring( 4 ) );
943 appendIso7064Digits( integer_builder, electronic_format_builder.substring( 0, 4 ) );
944
945 final BigInteger integer = new BigInteger( integer_builder.toString() );
946
947 valid = integer.remainder( INTEGER_97 ).equals( BigInteger.ONE );
948
949 if ( !valid )
950 {
951 throw new IbanCheckDigitsException(
952 electronic_format_builder.toString(),
953 new BigInteger( electronic_format_builder.substring( 2, 4 ) ) );
954
955 }
956
957 if ( context.parsePosition.getIndex() != text.length() )
958 {
959 valid = false;
960 }
961 }
962 }
963 else
964 {
965 valid = false;
966 }
967 }
968 else
969 {
970 valid = false;
971 }
972 }
973 else if ( text.length() > 0 && !( text.charAt( 0 ) >= 'A' && text.charAt( 0 ) <= 'Z' ) )
974 {
975 valid = false;
976 }
977
978 return valid;
979 }
980
981
982
983
984
985
986
987
988
989
990
991
992 public Appendable append( final IbanFormat format, final Appendable appendable ) throws IOException
993 {
994 if ( format == null )
995 {
996 throw new NullPointerException( "format" );
997 }
998 if ( appendable == null )
999 {
1000 throw new NullPointerException( "appendable" );
1001 }
1002
1003 switch ( format )
1004 {
1005 case ELECTRONIC:
1006 return appendable.append( this.electronicFormat );
1007 case PRINT:
1008 return appendable.append( this.letterFormat );
1009 default:
1010 throw new AssertionError( format );
1011 }
1012 }
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023 public String toString( final IbanFormat format )
1024 {
1025 if ( format == null )
1026 {
1027 throw new NullPointerException( "format" );
1028 }
1029
1030 switch ( format )
1031 {
1032 case ELECTRONIC:
1033 return this.electronicFormat;
1034 case PRINT:
1035 return this.letterFormat;
1036 default:
1037 throw new AssertionError( format );
1038 }
1039 }
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053 public void formatTo( final Formatter formatter, final int flags, final int width, final int precision )
1054 {
1055 if ( formatter == null )
1056 {
1057 throw new NullPointerException( "formatter" );
1058 }
1059 if ( ( flags & UPPERCASE ) == UPPERCASE )
1060 {
1061 final StringBuilder flagsBuilder = new StringBuilder( 3 );
1062
1063 if ( ( flags & ALTERNATE ) == ALTERNATE )
1064 {
1065 flagsBuilder.append( "#" );
1066 }
1067 if ( ( flags & LEFT_JUSTIFY ) == LEFT_JUSTIFY )
1068 {
1069 flagsBuilder.append( "-" );
1070 }
1071
1072 flagsBuilder.append( "^" );
1073
1074 throw new IllegalFormatFlagsException( flagsBuilder.toString() );
1075 }
1076
1077 final IbanFormat format = ( flags & ALTERNATE ) == ALTERNATE ? IbanFormat.PRINT : IbanFormat.ELECTRONIC;
1078
1079 String str = this.toString( format );
1080 if ( precision != -1 && precision < str.length() )
1081 {
1082 str = str.substring( 0, precision );
1083 }
1084
1085 final StringBuilder stringBuilder = new StringBuilder( str );
1086
1087 if ( width != -1 )
1088 {
1089 final int len = width - stringBuilder.length();
1090
1091 if ( len > 0 )
1092 {
1093 final char[] pad = new char[ len ];
1094 Arrays.fill( pad, ' ' );
1095
1096 if ( ( flags & LEFT_JUSTIFY ) == LEFT_JUSTIFY )
1097 {
1098 stringBuilder.append( pad );
1099 }
1100 else
1101 {
1102 stringBuilder.insert( 0, pad );
1103 }
1104 }
1105 }
1106
1107 formatter.format( stringBuilder.toString() );
1108 }
1109
1110
1111
1112
1113
1114
1115
1116 public int length()
1117 {
1118 return this.electronicFormat.length();
1119 }
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136 public char charAt( final int index )
1137 {
1138 return this.electronicFormat.charAt( index );
1139 }
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155 public CharSequence subSequence( final int start, final int end )
1156 {
1157 return this.electronicFormat.subSequence( start, end );
1158 }
1159
1160
1161
1162
1163
1164
1165 @Override
1166 public int hashCode()
1167 {
1168 return this.electronicFormat.hashCode();
1169 }
1170
1171
1172
1173
1174
1175
1176
1177
1178 @Override
1179 public boolean equals( final Object o )
1180 {
1181 boolean equal = this == o;
1182
1183 if ( !equal && ( o instanceof IBAN ) )
1184 {
1185 equal = this.electronicFormat.equals( ( (IBAN) o ).electronicFormat );
1186 }
1187
1188 return equal;
1189 }
1190
1191
1192
1193
1194
1195
1196 @Override
1197 public String toString()
1198 {
1199 return super.toString() + this.internalString();
1200 }
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214 public int compareTo( final IBAN o )
1215 {
1216 if ( o == null )
1217 {
1218 throw new NullPointerException( "o" );
1219 }
1220
1221 int r = this.getCountryCode().compareTo( o.getCountryCode() );
1222 for ( int i = 2, s0 = this.parts.length; r == 0 && i < s0; r = this.parts[i].compareTo( o.parts[i] ), i++ );
1223 return r;
1224 }
1225
1226
1227
1228
1229
1230
1231 private static Map<String, IBAN> getCache()
1232 {
1233 Map<String, IBAN> cache = cacheReference.get();
1234
1235 if ( cache == null )
1236 {
1237 cache = new ConcurrentHashMap<String, IBAN>( 1024 );
1238 cacheReference = new SoftReference<Map<String, IBAN>>( cache );
1239 }
1240
1241 return cache;
1242 }
1243
1244
1245
1246
1247
1248
1249 private String internalString()
1250 {
1251 if ( this.string == null )
1252 {
1253 final StringBuilder b = new StringBuilder( 500 ).append( "{" );
1254 b.append( "countryCode=" ).append( this.getCountryCode() ).
1255 append( ", sepaCountry=" ).append( this.sepaCountry ).
1256 append( ", bankIdentifier=" ).append( this.bankIdentifier ).
1257 append( ", branchIdentifier=" ).append( this.branchIdentifier ).
1258 append( ", electronicFormat=" ).append( this.electronicFormat ).
1259 append( ", letterFormat=" ).append( this.letterFormat ).
1260 append( ", parts={" );
1261
1262 final StringBuilder partBuilder = new StringBuilder();
1263 for ( int i = 0, s0 = this.parts.length; i < s0; i++ )
1264 {
1265 partBuilder.append( ",[" ).append( i ).append( "]=" ).append( this.parts[i] );
1266 }
1267
1268 this.string = b.append( partBuilder.substring( 1 ) ).append( "}}" ).toString();
1269 }
1270
1271 return this.string;
1272 }
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283 private static Structure parseStructure( final String structure ) throws ParseException
1284 {
1285 boolean in_literal = false;
1286 boolean in_part = false;
1287 boolean fixed_length_part = false;
1288 final List<Structure.Part> parts = new ArrayList<Structure.Part>( IBAN.MAX_CHARACTERS );
1289 final StringBuilder literalBuilder = new StringBuilder( IBAN.MAX_CHARACTERS );
1290 final StringBuilder numberBuilder = new StringBuilder( IBAN.MAX_CHARACTERS );
1291
1292 for ( int i = 0, s0 = structure.length(); i < s0; i++ )
1293 {
1294 final char c = structure.charAt( i );
1295
1296 if ( in_part )
1297 {
1298
1299 if ( isDigit( c ) )
1300 {
1301 if ( fixed_length_part )
1302 {
1303 throw new ParseException( structure, i );
1304 }
1305 numberBuilder.append( c );
1306 }
1307 else if ( isTypeIdentifier( c ) )
1308 {
1309 if ( fixed_length_part )
1310 {
1311 parts.add( new Structure.Part( c, null, Integer.parseInt( numberBuilder.toString() ), null ) );
1312 }
1313 else
1314 {
1315 parts.add( new Structure.Part( c, null, null, Integer.parseInt( numberBuilder.toString() ) ) );
1316 }
1317 numberBuilder.setLength( 0 );
1318 in_part = false;
1319 fixed_length_part = false;
1320 }
1321 else if ( isLengthIndicator( c ) )
1322 {
1323 if ( fixed_length_part )
1324 {
1325 throw new ParseException( structure, i );
1326 }
1327 fixed_length_part = true;
1328 }
1329 else
1330 {
1331 throw new ParseException( structure, i );
1332 }
1333 }
1334 else if ( in_literal )
1335 {
1336
1337 if ( isLiteral( c ) )
1338 {
1339 literalBuilder.append( c );
1340 }
1341 else if ( isDigit( c ) )
1342 {
1343 parts.add( new Structure.Part( 'l', literalBuilder.toString(), literalBuilder.length(), null ) );
1344 numberBuilder.append( c );
1345 literalBuilder.setLength( 0 );
1346 in_part = true;
1347 in_literal = false;
1348 }
1349 else
1350 {
1351 throw new ParseException( structure, i );
1352 }
1353 }
1354 else
1355 {
1356 if ( fixed_length_part )
1357 {
1358 throw new ParseException( structure, i );
1359 }
1360
1361
1362 if ( isDigit( c ) )
1363 {
1364 numberBuilder.append( c );
1365 in_part = true;
1366 }
1367 else if ( isLiteral( c ) )
1368 {
1369 literalBuilder.append( c );
1370 in_literal = true;
1371 }
1372 else
1373 {
1374 throw new ParseException( structure, i );
1375 }
1376 }
1377 }
1378
1379 if ( fixed_length_part )
1380 {
1381 throw new ParseException( structure, structure.length() );
1382 }
1383
1384 if ( in_part )
1385 {
1386 throw new ParseException( structure, structure.length() );
1387 }
1388
1389 if ( in_literal )
1390 {
1391 parts.add( new Structure.Part( 'l', literalBuilder.toString(), literalBuilder.length(), null ) );
1392 }
1393
1394 return new Structure( parts );
1395 }
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405 private static String parsePart( final ParseContext context, final Structure.Part part )
1406 {
1407 final StringBuilder partBuilder = new StringBuilder( MAX_CHARACTERS );
1408 final int start_index = context.parsePosition.getIndex();
1409
1410 next:
1411 for ( int index = context.parsePosition.getIndex(), text_len = context.text.length(), literal_index = 0,
1412 part_len = part.getFixedLength() != null
1413 ? part.getFixedLength().intValue() : part.getMaximumLength().intValue();
1414 index < text_len && index - start_index < part_len; index++, context.parsePosition.setIndex( index ) )
1415 {
1416 final char current = context.text.charAt( index );
1417
1418 if ( current == ' ' && part.getType() != 'e' )
1419 {
1420 if ( context.format == IbanFormat.PRINT && context.length % 4 == 0
1421 && ( context.previous == null || context.previous.charValue() != ' ' ) )
1422 {
1423 part_len++;
1424 context.previous = Character.valueOf( current );
1425 continue next;
1426 }
1427 else
1428 {
1429 context.parsePosition.setIndex( start_index );
1430 context.parsePosition.setErrorIndex( index );
1431 break next;
1432 }
1433 }
1434
1435 switch ( part.getType() )
1436 {
1437 case 'a':
1438
1439 if ( current >= 'A' && current <= 'Z' )
1440 {
1441 partBuilder.append( current );
1442 context.length++;
1443 }
1444 else
1445 {
1446 context.parsePosition.setIndex( start_index );
1447 context.parsePosition.setErrorIndex( index );
1448 }
1449 break;
1450 case 'c':
1451
1452 if ( ( current >= 'A' && current <= 'Z' )
1453 || ( current >= 'a' && current <= 'z' )
1454 || ( current >= '0' && current <= '9' ) )
1455 {
1456 partBuilder.append( current );
1457 context.length++;
1458 }
1459 else
1460 {
1461 context.parsePosition.setIndex( start_index );
1462 context.parsePosition.setErrorIndex( index );
1463 }
1464 break;
1465 case 'e':
1466
1467 if ( current == ' ' )
1468 {
1469 partBuilder.append( current );
1470 context.length++;
1471 }
1472 else
1473 {
1474 context.parsePosition.setIndex( start_index );
1475 context.parsePosition.setErrorIndex( index );
1476 }
1477 break;
1478 case 'n':
1479
1480 if ( current >= '0' && current <= '9' )
1481 {
1482 partBuilder.append( current );
1483 context.length++;
1484 }
1485 else
1486 {
1487 context.parsePosition.setIndex( start_index );
1488 context.parsePosition.setErrorIndex( index );
1489 }
1490 break;
1491 case 'l':
1492
1493 if ( current == part.getLiteral().charAt( literal_index++ ) )
1494 {
1495 context.length++;
1496 partBuilder.append( current );
1497 }
1498 else
1499 {
1500 context.parsePosition.setIndex( start_index );
1501 context.parsePosition.setErrorIndex( index );
1502 }
1503 break;
1504 default:
1505 context.parsePosition.setIndex( start_index );
1506 context.parsePosition.setErrorIndex( index );
1507 break next;
1508 }
1509
1510 context.previous = Character.valueOf( current );
1511 }
1512
1513 return context.parsePosition.getErrorIndex() < 0 ? partBuilder.toString() : null;
1514 }
1515
1516
1517
1518
1519
1520
1521
1522
1523 private static boolean isLiteral( final char c )
1524 {
1525 return ( c >= 'A' && c <= 'Z' );
1526 }
1527
1528
1529
1530
1531
1532
1533
1534
1535 private static boolean isDigit( final char c )
1536 {
1537 return ( c >= '0' && c <= '9' );
1538 }
1539
1540
1541
1542
1543
1544
1545
1546
1547 private static boolean isTypeIdentifier( final char c )
1548 {
1549 return c == 'a' || c == 'c' || c == 'e' || c == 'n';
1550 }
1551
1552
1553
1554
1555
1556
1557
1558
1559 private static boolean isLengthIndicator( final char c )
1560 {
1561 return c == '!';
1562 }
1563
1564
1565
1566
1567
1568
1569
1570
1571 private static List<Number> splitNumbers( final String str )
1572 {
1573 final String[] parts = str.split( "\\|" );
1574 final List<Number> numbers = new ArrayList<Number>( parts.length );
1575 for ( int i = 0, l0 = parts.length; i < l0; numbers.add( Integer.valueOf( parts[i] ) ), i++ );
1576 return numbers;
1577 }
1578
1579
1580
1581
1582
1583
1584
1585 private static void appendIso7064Digits( final StringBuilder buffer, final String chars )
1586 {
1587 for ( int i = 0, l0 = chars.length(); i < l0; i++ )
1588 {
1589 final char c = chars.charAt( i );
1590
1591 if ( c >= 'A' && c <= 'Z' )
1592 {
1593 buffer.append( Integer.toString( ( c - 'A' ) + 10 ) );
1594 }
1595 else if ( c >= 'a' && c <= 'z' )
1596 {
1597 buffer.append( Integer.toString( ( c - 'a' ) + 10 ) );
1598 }
1599 else if ( c >= '0' && c <= '9' )
1600 {
1601 buffer.append( c );
1602 }
1603 else
1604 {
1605 throw new AssertionError( c );
1606 }
1607 }
1608 }
1609
1610
1611
1612
1613
1614
1615
1616
1617 private static String toLetterFormat( final String electronicFormat )
1618 {
1619 final StringBuilder letter_format_builder = new StringBuilder( electronicFormat.length() );
1620
1621 for ( int i = 0, l0 = electronicFormat.length(); i < l0; i++ )
1622 {
1623 if ( i > 0 && i % 4 == 0 )
1624 {
1625 letter_format_builder.append( ' ' );
1626 }
1627
1628 letter_format_builder.append( electronicFormat.charAt( i ) );
1629 }
1630
1631 return letter_format_builder.toString();
1632 }
1633
1634
1635
1636
1637
1638
1639
1640
1641 }