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 * <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}