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 | */ |
21 | package org.jdtaus.banking; |
22 | |
23 | import java.io.ObjectStreamException; |
24 | import java.io.Serializable; |
25 | import java.text.MessageFormat; |
26 | import java.util.Date; |
27 | import java.util.HashMap; |
28 | import java.util.HashSet; |
29 | import java.util.Iterator; |
30 | import java.util.Locale; |
31 | import java.util.Map; |
32 | import java.util.Set; |
33 | import org.jdtaus.core.container.ContainerFactory; |
34 | import org.jdtaus.core.container.PropertyException; |
35 | |
36 | /** |
37 | * Type of a transaction in germany. |
38 | * <p>A Textschlüssel is made up of a two-digit positive integer (the key) and a three-digit positive integer |
39 | * (the extension). The key, together with a constant extension, uniquely identifies a transaction's type. The extension |
40 | * may also be used to hold non-identifying data. In such cases only the key is used to identify a transaction's type |
41 | * and the extension holds variable data.</p> |
42 | * |
43 | * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> |
44 | * @version $JDTAUS: Textschluessel.java 8890 2014-12-08 22:37:57Z schulte $ |
45 | * |
46 | * @see TextschluesselVerzeichnis |
47 | */ |
48 | public class Textschluessel implements Cloneable, Comparable, Serializable |
49 | { |
50 | |
51 | /** Constant for the name of property {@code key}. */ |
52 | public static final String PROP_KEY = "org.jdtaus.banking.Textschluessel.PROP_KEY"; |
53 | |
54 | /** Constant for the name of property {@code extension}. */ |
55 | public static final String PROP_EXTENSION = "org.jdtaus.banking.Textschluessel.PROP_EXTENSION"; |
56 | |
57 | /** Constant for the name of property {@code validTo}. */ |
58 | public static final String PROP_VALID_TO = "org.jdtaus.banking.Textschluessel.PROP_VALID_TO"; |
59 | |
60 | /** Constant for the name of property {@code validFrom}. */ |
61 | public static final String PROP_VALID_FROM = "org.jdtaus.banking.Textschluessel.PROP_VALID_FROM"; |
62 | |
63 | /** Constant for the name of property {@code debit}. */ |
64 | public static final String PROP_DEBIT = "org.jdtaus.banking.Textschluessel.PROP_DEBIT"; |
65 | |
66 | /** Constant for the name of property {@code remittance}. */ |
67 | public static final String PROP_REMITTANCE = "org.jdtaus.banking.Textschluessel.PROP_REMITTANCE"; |
68 | |
69 | /** Constant for the name of property {@code variable}. */ |
70 | public static final String PROP_VARIABLE = "org.jdtaus.banking.Textschluessel.PROP_VARIABLE"; |
71 | |
72 | /** Constant for the name of property {@code shortDescription}. */ |
73 | public static final String PROP_SHORTDESCRIPTION = "org.jdtaus.banking.Textschluessel.PROP_SHORTDESCRIPTION"; |
74 | |
75 | /** Serial version UID for backwards compatibility with 1.0.x classes. */ |
76 | private static final long serialVersionUID = -8556424800883022756L; |
77 | |
78 | /** |
79 | * Key of the Textschlüssel. |
80 | * @serial |
81 | */ |
82 | private int key; |
83 | |
84 | /** |
85 | * Extension of the Textschlüsse. |
86 | * @serial |
87 | */ |
88 | private int extension; |
89 | |
90 | /** |
91 | * Start date of validity. |
92 | * @serial |
93 | */ |
94 | private Date validFrom; |
95 | private transient long validFromMillis; |
96 | |
97 | /** |
98 | * End date of validity. |
99 | * @serial |
100 | */ |
101 | private Date validTo; |
102 | private transient long validToMillis; |
103 | |
104 | /** |
105 | * Flag indicating if a transaction of the type is a debit. |
106 | * @serial |
107 | */ |
108 | private boolean debit; |
109 | |
110 | /** |
111 | * Flag indicating if a transaction of the type is a remittance. |
112 | * @serial |
113 | */ |
114 | private boolean remittance; |
115 | |
116 | /** |
117 | * Flag indicating if the extension holds non-identifying, variable data. |
118 | * @serial |
119 | */ |
120 | private boolean variable; |
121 | |
122 | /** |
123 | * Maps language codes to short descriptions. |
124 | * @serial |
125 | */ |
126 | private Map shortDescriptions = new HashMap( 10 ); |
127 | |
128 | /** Cached hash code. */ |
129 | private transient int hashCode = NO_HASHCODE; |
130 | private static final int NO_HASHCODE = Integer.MIN_VALUE; |
131 | |
132 | /** Creates a new {@code Textschluessel} instance. */ |
133 | public Textschluessel() |
134 | { |
135 | super(); |
136 | this.assertValidProperties(); |
137 | } |
138 | |
139 | /** |
140 | * Getter for property {@code key}. |
141 | * |
142 | * @return Key of the Textschlüssel. |
143 | */ |
144 | public int getKey() |
145 | { |
146 | return this.key; |
147 | } |
148 | |
149 | /** |
150 | * Setter for property {@code key}. |
151 | * |
152 | * @param value New key of the Textschlüssel. |
153 | */ |
154 | public void setKey( final int value ) |
155 | { |
156 | this.key = value; |
157 | this.hashCode = NO_HASHCODE; |
158 | } |
159 | |
160 | /** |
161 | * Getter for property {@code extension}. |
162 | * |
163 | * @return Extension of the Textschlüssel. |
164 | */ |
165 | public int getExtension() |
166 | { |
167 | return this.extension; |
168 | } |
169 | |
170 | /** |
171 | * Setter for property {@code extension}. |
172 | * |
173 | * @param value New extension of the Textschlüssel. |
174 | */ |
175 | public void setExtension( final int value ) |
176 | { |
177 | this.extension = value; |
178 | this.hashCode = NO_HASHCODE; |
179 | } |
180 | |
181 | /** |
182 | * Gets the date of validity of the Textschlüssel. |
183 | * |
184 | * @return The date the Textschlüssel is valid from (inclusive) or {@code null} if nothing is known about the start |
185 | * of validity of the Textschlüssel. |
186 | */ |
187 | public Date getValidFrom() |
188 | { |
189 | return this.validFrom != null ? (Date) this.validFrom.clone() : null; |
190 | } |
191 | |
192 | /** |
193 | * Sets the date of validity of the Textschlüssel. |
194 | * |
195 | * @param value The new date the Textschlüssel is valid from or {@code null} if nothing is known about the start of |
196 | * validity of the Textschlüssel. |
197 | */ |
198 | public void setValidFrom( final Date value ) |
199 | { |
200 | if ( value == null ) |
201 | { |
202 | this.validFrom = null; |
203 | this.validFromMillis = 0L; |
204 | } |
205 | else |
206 | { |
207 | this.validFrom = (Date) value.clone(); |
208 | this.validFromMillis = value.getTime(); |
209 | } |
210 | } |
211 | |
212 | /** |
213 | * Gets the date of expiration of the Textschlüssel. |
214 | * |
215 | * @return The date the Textschlüssel is valid to (inclusive) or {@code null} if nothing is known about the date of |
216 | * expiration of the Textschlüssel. |
217 | */ |
218 | public Date getValidTo() |
219 | { |
220 | return this.validTo != null ? (Date) this.validTo.clone() : null; |
221 | } |
222 | |
223 | /** |
224 | * Sets the date of expiration of the Textschlüssel. |
225 | * |
226 | * @param value The new date the Textschlüssel is valid to or {@code null} if nothing is known about the date of |
227 | * expiration of the Textschlüssel. |
228 | */ |
229 | public void setValidTo( final Date value ) |
230 | { |
231 | if ( value == null ) |
232 | { |
233 | this.validTo = null; |
234 | this.validToMillis = 0L; |
235 | } |
236 | else |
237 | { |
238 | this.validTo = (Date) value.clone(); |
239 | this.validToMillis = 0L; |
240 | } |
241 | } |
242 | |
243 | /** |
244 | * Gets a flag indicating that the Textschlüssel is valid at a given date. |
245 | * |
246 | * @param date The date with which to check. |
247 | * |
248 | * @return {@code true}, if the Textschlüssel is valid at {@code date}; {@code false} if not. |
249 | * |
250 | * @throws NullPointerException if {@code date} is {@code null}. |
251 | */ |
252 | public boolean isValidAt( final Date date ) |
253 | { |
254 | if ( date == null ) |
255 | { |
256 | throw new NullPointerException( "date" ); |
257 | } |
258 | |
259 | |
260 | return !( ( this.validFrom != null && this.validFromMillis > date.getTime() ) || |
261 | ( this.validTo != null && this.validToMillis < date.getTime() ) ); |
262 | |
263 | } |
264 | |
265 | /** |
266 | * Flag indicating if a transaction of this type is a debit. |
267 | * |
268 | * @return {@code true} if a transaction of this type is a debit; {@code false} if not. |
269 | */ |
270 | public boolean isDebit() |
271 | { |
272 | return this.debit; |
273 | } |
274 | |
275 | /** |
276 | * Setter for property {@code debit}. |
277 | * |
278 | * @param value {@code true} if a transaction of this type is a debit; {@code false} if not. |
279 | */ |
280 | public void setDebit( final boolean value ) |
281 | { |
282 | this.debit = value; |
283 | } |
284 | |
285 | /** |
286 | * Flag indicating if a transaction of this type is a remittance. |
287 | * |
288 | * @return {@code true} if a transaction of this type is a remittance; {@code false} if not. |
289 | */ |
290 | public boolean isRemittance() |
291 | { |
292 | return this.remittance; |
293 | } |
294 | |
295 | /** |
296 | * Setter for property {@code remittance}. |
297 | * |
298 | * @param value {@code true} if a transaction of this type is a remittance; {@code false} if not. |
299 | */ |
300 | public void setRemittance( final boolean value ) |
301 | { |
302 | this.remittance = value; |
303 | } |
304 | |
305 | /** |
306 | * Flag indicating if the extension holds non-identifying, variable data. |
307 | * |
308 | * @return {@code true} if the extension holds non-identifying, variable data; {@code false} if the extension is |
309 | * part of the identifying key. |
310 | */ |
311 | public boolean isVariable() |
312 | { |
313 | return this.variable; |
314 | } |
315 | |
316 | /** |
317 | * Setter for property {@code variable}. |
318 | * |
319 | * @param value {@code true} if the extension holds non-identifying, variable data; {@code false} if the extension |
320 | * is part of the identifying key. |
321 | */ |
322 | public void setVariable( final boolean value ) |
323 | { |
324 | this.variable = value; |
325 | this.hashCode = NO_HASHCODE; |
326 | } |
327 | |
328 | /** |
329 | * Gets the short description of the Textschlüssel for a given locale. |
330 | * |
331 | * @param locale The locale of the short description to return or {@code null} for {@code Locale.getDefault()}. |
332 | * |
333 | * @return The short description of the instance for {@code locale}. |
334 | */ |
335 | public String getShortDescription( final Locale locale ) |
336 | { |
337 | final Locale l = locale == null ? Locale.getDefault() : locale; |
338 | |
339 | this.assertValidProperties(); |
340 | |
341 | // Try the requested language. |
342 | String description = (String) this.shortDescriptions.get( l.getLanguage().toLowerCase() ); |
343 | |
344 | if ( description == null ) |
345 | { |
346 | // Try the configured default language. |
347 | description = (String) this.shortDescriptions.get( this.getDefaultLanguage().toLowerCase() ); |
348 | } |
349 | |
350 | if ( description == null ) |
351 | { |
352 | // Try the system's default language. |
353 | description = (String) this.shortDescriptions.get( Locale.getDefault().getLanguage().toLowerCase() ); |
354 | } |
355 | |
356 | if ( description == null ) |
357 | { |
358 | // Fall back to a default message just stating key and extension. |
359 | description = this.getTextschluesselDescriptionMessage( |
360 | this.getLocale(), new Integer( this.getKey() ), new Integer( this.getExtension() ) ); |
361 | |
362 | } |
363 | |
364 | return new MessageFormat( description, l ).format( new Object[] |
365 | { |
366 | new Integer( this.getKey() ), new Integer( this.getExtension() ) |
367 | } ); |
368 | |
369 | } |
370 | |
371 | /** |
372 | * Setter for property {@code shortDescription}. |
373 | * |
374 | * @param locale The locale to set the short description for or {@code null} for {@code Locale.getDefault()}. |
375 | * @param shortDescription The new value for property {@code shortDescription} for {@code locale}. |
376 | * |
377 | * @return The value previously held by the instance for {@code locale} or {@code null} if the instance previously |
378 | * held no value for {@code locale}. |
379 | * |
380 | * @throws NullPointerException if {@code shortDescription} is {@code null}. |
381 | */ |
382 | public String setShortDescription( final Locale locale, final String shortDescription ) |
383 | { |
384 | if ( shortDescription == null ) |
385 | { |
386 | throw new NullPointerException( "shortDescription" ); |
387 | } |
388 | |
389 | final Locale l = locale == null ? Locale.getDefault() : locale; |
390 | return (String) this.shortDescriptions.put( l.getLanguage().toLowerCase(), shortDescription ); |
391 | } |
392 | |
393 | /** |
394 | * Gets all locales for which the instance holds short descriptions. |
395 | * |
396 | * @return All locales for which the instance holds short descriptions. |
397 | */ |
398 | public Locale[] getLocales() |
399 | { |
400 | final Set locales = new HashSet( this.shortDescriptions.size() ); |
401 | for ( final Iterator it = this.shortDescriptions.keySet().iterator(); it.hasNext(); ) |
402 | { |
403 | locales.add( new Locale( (String) it.next() ) ); |
404 | } |
405 | |
406 | return (Locale[]) locales.toArray( new Locale[ locales.size() ] ); |
407 | } |
408 | |
409 | /** |
410 | * Checks configured properties. |
411 | * |
412 | * @throws PropertyException for invalid property values. |
413 | * |
414 | * @deprecated Removed without replacement. |
415 | */ |
416 | protected void assertValidProperties() |
417 | { |
418 | if ( this.getDefaultLanguage() == null || this.getDefaultLanguage().length() <= 0 ) |
419 | { |
420 | throw new PropertyException( "defaultLanguage", this.getDefaultLanguage() ); |
421 | } |
422 | } |
423 | |
424 | /** |
425 | * Compares this object with the specified object for order. |
426 | * <p>Compares the values of properties {@code key} and {@code extension} and returns a negative integer, zero, or a |
427 | * positive integer as this object is less than, equal to, or greater than the specified object.</p> |
428 | * |
429 | * @param o The Object to be compared. |
430 | * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than |
431 | * the specified object. |
432 | * |
433 | * @throws NullPointerException if {@code o} is {@code null}. |
434 | * @throws ClassCastException if the specified object's type prevents it from being compared to this Object. |
435 | */ |
436 | public int compareTo( final Object o ) |
437 | { |
438 | if ( o == null ) |
439 | { |
440 | throw new NullPointerException( "o" ); |
441 | } |
442 | if ( !( o instanceof Textschluessel ) ) |
443 | { |
444 | throw new ClassCastException( o.getClass().getName() ); |
445 | } |
446 | |
447 | int result = 0; |
448 | final Textschluessel that = (Textschluessel) o; |
449 | |
450 | if ( !this.equals( that ) ) |
451 | { |
452 | result = this.key == that.key ? 0 : this.key > that.key ? 1 : -1; |
453 | if ( result == 0 && this.extension != that.extension ) |
454 | { |
455 | result = this.extension > that.extension ? 1 : -1; |
456 | } |
457 | } |
458 | |
459 | return result; |
460 | } |
461 | |
462 | /** |
463 | * Takes care of initializing the {@code validFromMillis} and {@code validToMillis} fields when constructed from an |
464 | * <1.11 object stream. |
465 | * |
466 | * @throws ObjectStreamException if resolution fails. |
467 | */ |
468 | private Object readResolve() throws ObjectStreamException |
469 | { |
470 | if ( this.validFrom != null ) |
471 | { |
472 | this.validFromMillis = this.validFrom.getTime(); |
473 | } |
474 | if ( this.validTo != null ) |
475 | { |
476 | this.validToMillis = this.validTo.getTime(); |
477 | } |
478 | |
479 | return this; |
480 | } |
481 | |
482 | /** |
483 | * Creates and returns a copy of this object. |
484 | * |
485 | * @return A clone of this instance. |
486 | */ |
487 | public Object clone() |
488 | { |
489 | try |
490 | { |
491 | return super.clone(); |
492 | } |
493 | catch ( final CloneNotSupportedException e ) |
494 | { |
495 | throw new AssertionError( e ); |
496 | } |
497 | } |
498 | |
499 | /** |
500 | * Indicates whether some other object is equal to this one by comparing the values of all properties. |
501 | * <p>The extension will only be compared if it is part of the identifying key based on the value of property |
502 | * {@code variable}.</p> |
503 | * |
504 | * @param o The reference object with which to compare. |
505 | * |
506 | * @return {@code true} if this object is the same as {@code o}; {@code false} otherwise. |
507 | */ |
508 | public boolean equals( final Object o ) |
509 | { |
510 | boolean equal = o == this; |
511 | |
512 | if ( !equal && o instanceof Textschluessel ) |
513 | { |
514 | final Textschluessel that = (Textschluessel) o; |
515 | |
516 | if ( this.isVariable() ) |
517 | { |
518 | equal = that.isVariable() && this.key == that.getKey(); |
519 | } |
520 | else |
521 | { |
522 | equal = !that.isVariable() && this.key == that.getKey() && this.extension == that.getExtension(); |
523 | } |
524 | } |
525 | |
526 | return equal; |
527 | } |
528 | |
529 | /** |
530 | * Returns a hash code value for this object. |
531 | * |
532 | * @return A hash code value for this object. |
533 | */ |
534 | public int hashCode() |
535 | { |
536 | if ( this.hashCode == NO_HASHCODE ) |
537 | { |
538 | int hc = 23; |
539 | |
540 | hc = 37 * hc + ( this.variable ? 0 : 1 ); |
541 | hc = 37 * hc + this.key; |
542 | |
543 | if ( !this.variable ) |
544 | { |
545 | hc = 37 * hc + this.extension; |
546 | } |
547 | |
548 | this.hashCode = hc; |
549 | } |
550 | |
551 | return this.hashCode; |
552 | } |
553 | |
554 | /** |
555 | * Returns a string representation of the object. |
556 | * |
557 | * @return A string representation of the object. |
558 | */ |
559 | public String toString() |
560 | { |
561 | return super.toString() + this.internalString(); |
562 | } |
563 | |
564 | /** |
565 | * Creates a string representing the properties of the instance. |
566 | * |
567 | * @return A string representing the properties of the instance. |
568 | */ |
569 | private String internalString() |
570 | { |
571 | return new StringBuffer( 200 ).append( '{' ). |
572 | append( "key=" ).append( this.key ). |
573 | append( ", extension=" ).append( this.extension ). |
574 | append( ", validFrom=" ).append( this.validFrom ). |
575 | append( ", validTo=" ).append( this.validTo ). |
576 | append( ", debit=" ).append( this.debit ). |
577 | append( ", remittance=" ).append( this.remittance ). |
578 | append( ", variable=" ).append( this.variable ). |
579 | append( ", shortDescription=" ). |
580 | append( this.getShortDescription( null ) ). |
581 | append( '}' ).toString(); |
582 | |
583 | } |
584 | |
585 | //--Dependencies------------------------------------------------------------ |
586 | |
587 | // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies |
588 | // This section is managed by jdtaus-container-mojo. |
589 | |
590 | /** |
591 | * Gets the configured <code>Locale</code> implementation. |
592 | * |
593 | * @return The configured <code>Locale</code> implementation. |
594 | */ |
595 | private Locale getLocale() |
596 | { |
597 | return (Locale) ContainerFactory.getContainer(). |
598 | getDependency( this, "Locale" ); |
599 | |
600 | } |
601 | |
602 | // </editor-fold>//GEN-END:jdtausDependencies |
603 | |
604 | //------------------------------------------------------------Dependencies-- |
605 | //--Messages---------------------------------------------------------------- |
606 | |
607 | // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages |
608 | // This section is managed by jdtaus-container-mojo. |
609 | |
610 | /** |
611 | * Gets the text of message <code>textschluesselDescription</code>. |
612 | * <blockquote><pre>{0,number,00}{1,number,000}</pre></blockquote> |
613 | * <blockquote><pre>{0,number,00}{1,number,000}</pre></blockquote> |
614 | * |
615 | * @param locale The locale of the message instance to return. |
616 | * @param k format parameter. |
617 | * @param e format parameter. |
618 | * |
619 | * @return the text of message <code>textschluesselDescription</code>. |
620 | */ |
621 | private String getTextschluesselDescriptionMessage( final Locale locale, |
622 | final java.lang.Number k, |
623 | final java.lang.Number e ) |
624 | { |
625 | return ContainerFactory.getContainer(). |
626 | getMessage( this, "textschluesselDescription", locale, |
627 | new Object[] |
628 | { |
629 | k, |
630 | e |
631 | }); |
632 | |
633 | } |
634 | |
635 | // </editor-fold>//GEN-END:jdtausMessages |
636 | |
637 | //----------------------------------------------------------------Messages-- |
638 | //--Properties-------------------------------------------------------------- |
639 | |
640 | // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties |
641 | // This section is managed by jdtaus-container-mojo. |
642 | |
643 | /** |
644 | * Gets the value of property <code>defaultLanguage</code>. |
645 | * |
646 | * @return Default language of descriptions when there is no description available for a requested language. |
647 | */ |
648 | private java.lang.String getDefaultLanguage() |
649 | { |
650 | return (java.lang.String) ContainerFactory.getContainer(). |
651 | getProperty( this, "defaultLanguage" ); |
652 | |
653 | } |
654 | |
655 | // </editor-fold>//GEN-END:jdtausProperties |
656 | |
657 | //--------------------------------------------------------------Properties-- |
658 | } |