001/*
002 *  jDTAUS Banking API
003 *  Copyright (C) 2005 Christian Schulte
004 *  <cs@schulte.it>
005 *
006 *  This library is free software; you can redistribute it and/or
007 *  modify it under the terms of the GNU Lesser General Public
008 *  License as published by the Free Software Foundation; either
009 *  version 2.1 of the License, or any later version.
010 *
011 *  This library is distributed in the hope that it will be useful,
012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *  Lesser General Public License for more details.
015 *
016 *  You should have received a copy of the GNU Lesser General Public
017 *  License along with this library; if not, write to the Free Software
018 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
019 *
020 */
021package org.jdtaus.banking;
022
023import java.io.ObjectStreamException;
024import java.io.Serializable;
025import java.text.MessageFormat;
026import java.util.Date;
027import java.util.HashMap;
028import java.util.HashSet;
029import java.util.Iterator;
030import java.util.Locale;
031import java.util.Map;
032import java.util.Set;
033import org.jdtaus.core.container.ContainerFactory;
034import org.jdtaus.core.container.PropertyException;
035
036/**
037 * Type of a transaction in germany.
038 * <p>A Textschlüssel is made up of a two-digit positive integer (the key) and a three-digit positive integer
039 * (the extension). The key, together with a constant extension, uniquely identifies a transaction's type. The extension
040 * may also be used to hold non-identifying data. In such cases only the key is used to identify a transaction's type
041 * and the extension holds variable data.</p>
042 *
043 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
044 * @version $JDTAUS: Textschluessel.java 8890 2014-12-08 22:37:57Z schulte $
045 *
046 * @see TextschluesselVerzeichnis
047 */
048public class Textschluessel implements Cloneable, Comparable, Serializable
049{
050
051    /** Constant for the name of property {@code key}. */
052    public static final String PROP_KEY = "org.jdtaus.banking.Textschluessel.PROP_KEY";
053
054    /** Constant for the name of property {@code extension}. */
055    public static final String PROP_EXTENSION = "org.jdtaus.banking.Textschluessel.PROP_EXTENSION";
056
057    /** Constant for the name of property {@code validTo}. */
058    public static final String PROP_VALID_TO = "org.jdtaus.banking.Textschluessel.PROP_VALID_TO";
059
060    /** Constant for the name of property {@code validFrom}. */
061    public static final String PROP_VALID_FROM = "org.jdtaus.banking.Textschluessel.PROP_VALID_FROM";
062
063    /** Constant for the name of property {@code debit}. */
064    public static final String PROP_DEBIT = "org.jdtaus.banking.Textschluessel.PROP_DEBIT";
065
066    /** Constant for the name of property {@code remittance}. */
067    public static final String PROP_REMITTANCE = "org.jdtaus.banking.Textschluessel.PROP_REMITTANCE";
068
069    /** Constant for the name of property {@code variable}. */
070    public static final String PROP_VARIABLE = "org.jdtaus.banking.Textschluessel.PROP_VARIABLE";
071
072    /** Constant for the name of property {@code shortDescription}. */
073    public static final String PROP_SHORTDESCRIPTION = "org.jdtaus.banking.Textschluessel.PROP_SHORTDESCRIPTION";
074
075    /** Serial version UID for backwards compatibility with 1.0.x classes. */
076    private static final long serialVersionUID = -8556424800883022756L;
077
078    /**
079     * Key of the Textschlüssel.
080     * @serial
081     */
082    private int key;
083
084    /**
085     * Extension of the Textschlüsse.
086     * @serial
087     */
088    private int extension;
089
090    /**
091     * Start date of validity.
092     * @serial
093     */
094    private Date validFrom;
095    private transient long validFromMillis;
096
097    /**
098     * End date of validity.
099     * @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}