View Javadoc
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      * &lt;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 }