1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.jdtaus.banking;
22
23 import java.lang.ref.Reference;
24 import java.lang.ref.SoftReference;
25 import java.text.DecimalFormat;
26 import java.text.ParseException;
27 import java.text.ParsePosition;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.Map;
31
32
33
34
35
36
37
38
39 public final class Kontonummer extends Number implements Comparable
40 {
41
42
43
44
45
46 public static final int ELECTRONIC_FORMAT = 4001;
47
48
49
50
51
52
53
54 public static final int LETTER_FORMAT = 4002;
55
56
57 public static final int MAX_DIGITS = 10;
58
59
60 public static final int MAX_CHARACTERS = 13;
61
62
63 private static final double[] EXP10 =
64 {
65 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
66 1000000000
67 };
68
69
70 private static final long serialVersionUID = 3117245365189973632L;
71
72
73 private static volatile Reference cacheReference = new SoftReference( null );
74
75
76
77
78
79 private long kto;
80
81
82
83
84
85
86
87
88
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
102
103
104
105
106
107
108
109
110
111
112
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
254
255
256
257
258
259
260
261
262
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
291
292
293
294
295
296
297
298
299
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
321
322
323
324
325
326
327
328
329
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
345
346
347
348
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
365
366
367
368 public int intValue()
369 {
370 return (int) this.kto;
371 }
372
373
374
375
376
377
378 public long longValue()
379 {
380 return this.kto;
381 }
382
383
384
385
386
387
388 public float floatValue()
389 {
390 return this.kto;
391 }
392
393
394
395
396
397
398 public double doubleValue()
399 {
400 return this.kto;
401 }
402
403
404
405
406
407
408
409
410
411
412
413
414
415
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
448
449
450
451
452
453
454
455
456
457
458
459
460
461 public String format( final int style )
462 {
463 return this.format( style, new StringBuffer() ).toString();
464 }
465
466
467
468
469
470
471
472
473
474
475
476
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
490
491
492
493
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
516
517
518
519 private String internalString()
520 {
521 return new StringBuffer( 500 ).append( "{accountCode=" ).append( this.kto ).append( '}' ).toString();
522 }
523
524
525
526
527
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
543
544
545
546
547
548
549
550
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
576
577
578
579
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
595
596
597
598 public int hashCode()
599 {
600 return (int) ( this.kto ^ ( this.kto >>> 32 ) );
601 }
602
603
604
605
606
607
608 public String toString()
609 {
610 return super.toString() + this.internalString();
611 }
612
613 }