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 }