001/* 002 * jDTAUS Banking Utilities 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.util.swing; 022 023import java.beans.PropertyChangeEvent; 024import java.beans.PropertyChangeListener; 025import java.text.DecimalFormat; 026import java.text.NumberFormat; 027import java.text.ParseException; 028import java.util.Locale; 029import javax.swing.JFormattedTextField; 030import javax.swing.JFormattedTextField.AbstractFormatter; 031import javax.swing.SwingUtilities; 032import javax.swing.text.AttributeSet; 033import javax.swing.text.BadLocationException; 034import javax.swing.text.DocumentFilter; 035import javax.swing.text.DocumentFilter.FilterBypass; 036import org.jdtaus.banking.Bankleitzahl; 037import org.jdtaus.banking.BankleitzahlExpirationException; 038import org.jdtaus.banking.BankleitzahlInfo; 039import org.jdtaus.banking.BankleitzahlenVerzeichnis; 040import org.jdtaus.banking.messages.BankleitzahlExpirationMessage; 041import org.jdtaus.banking.messages.BankleitzahlReplacementMessage; 042import org.jdtaus.banking.messages.UnknownBankleitzahlMessage; 043import org.jdtaus.core.container.ContainerFactory; 044import org.jdtaus.core.container.PropertyException; 045 046/** 047 * {@code JFormattedTextField} supporting the {@code Bankleitzahl} type. 048 * <p>This textfield uses the {@link Bankleitzahl} type for parsing and formatting. An empty string value is treated as 049 * {@code null}. Property {@code format} controls formatting and takes one of the format constants defined in class 050 * {@code Bankleitzahl}. By default the {@code ELECTRONIC_FORMAT} is used. The {@code validating} flag controls 051 * validation of values entered into the textfield. If {@code true} (default), a {@code DocumentFilter} is registered 052 * with the textfield disallowing invalid values, that is, values which are not {@code null} and not empty strings and 053 * for which the {@link Bankleitzahl#parse(String)} method throws a {@code ParseException}. The field's tooltip 054 * text is updated with information about the field's value.</p> 055 * 056 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 057 * @version $JDTAUS: BankleitzahlTextField.java 8661 2012-09-27 11:29:58Z schulte $ 058 */ 059public final class BankleitzahlTextField extends JFormattedTextField 060{ 061 062 /** Serial version UID for backwards compatibility with 1.1.x classes. */ 063 private static final long serialVersionUID = -5461742987164339047L; 064 065 /** 066 * The constant of the format to use when formatting Bankleitzahl instances. 067 * @serial 068 */ 069 private Integer format; 070 071 /** 072 * Flag indicating if validation is performed. 073 * @serial 074 */ 075 private Boolean validating; 076 077 /** Creates a new default {@code BankleitzahlTextField} instance. */ 078 public BankleitzahlTextField() 079 { 080 super(); 081 this.assertValidProperties(); 082 this.setColumns( Bankleitzahl.MAX_CHARACTERS ); 083 this.setFormatterFactory( new AbstractFormatterFactory() 084 { 085 086 public AbstractFormatter getFormatter( final JFormattedTextField ftf ) 087 { 088 return new AbstractFormatter() 089 { 090 091 public Object stringToValue( final String text ) throws ParseException 092 { 093 Object value = null; 094 095 if ( text != null && text.trim().length() > 0 ) 096 { 097 value = Bankleitzahl.parse( text ); 098 } 099 100 return value; 101 } 102 103 public String valueToString( final Object value ) throws ParseException 104 { 105 String ret = null; 106 107 if ( value instanceof Bankleitzahl ) 108 { 109 final Bankleitzahl blz = (Bankleitzahl) value; 110 ret = blz.format( getFormat() ); 111 } 112 113 return ret; 114 } 115 116 protected DocumentFilter getDocumentFilter() 117 { 118 return new DocumentFilter() 119 { 120 121 public void insertString( final FilterBypass fb, final int o, String s, 122 final AttributeSet a ) throws BadLocationException 123 { 124 if ( isValidating() ) 125 { 126 final StringBuffer b = new StringBuffer( fb.getDocument().getLength() + s.length() ); 127 b.append( fb.getDocument().getText( 0, fb.getDocument().getLength() ) ); 128 b.insert( o, s ); 129 130 try 131 { 132 Bankleitzahl.parse( b.toString() ); 133 } 134 catch ( ParseException e ) 135 { 136 invalidEdit(); 137 return; 138 } 139 } 140 141 super.insertString( fb, o, s, a ); 142 } 143 144 public void replace( final FilterBypass fb, final int o, final int l, String s, 145 final AttributeSet a ) throws BadLocationException 146 { 147 if ( isValidating() ) 148 { 149 final StringBuffer b = new StringBuffer( fb.getDocument().getLength() + s.length() ); 150 b.append( fb.getDocument().getText( 0, fb.getDocument().getLength() ) ); 151 b.replace( o, o + s.length(), s ); 152 153 try 154 { 155 Bankleitzahl.parse( b.toString() ); 156 } 157 catch ( ParseException e ) 158 { 159 invalidEdit(); 160 return; 161 } 162 } 163 164 super.replace( fb, o, l, s, a ); 165 } 166 167 }; 168 } 169 170 }; 171 } 172 173 } ); 174 175 this.addPropertyChangeListener( "value", new PropertyChangeListener() 176 { 177 178 public void propertyChange( final PropertyChangeEvent evt ) 179 { 180 new Thread() 181 { 182 183 public void run() 184 { 185 updateTooltip(); 186 } 187 188 }.start(); 189 } 190 191 } ); 192 } 193 194 /** 195 * Gets the last valid {@code Bankleitzahl}. 196 * 197 * @return the last valid {@code Bankleitzahl} or {@code null}. 198 */ 199 public Bankleitzahl getBankleitzahl() 200 { 201 return (Bankleitzahl) this.getValue(); 202 } 203 204 /** 205 * Gets the constant of the format used when formatting Bankleitzahl instances. 206 * 207 * @return the constant of the format used when formatting Bankleitzahl instances. 208 * 209 * @see Bankleitzahl#ELECTRONIC_FORMAT 210 * @see Bankleitzahl#LETTER_FORMAT 211 */ 212 public int getFormat() 213 { 214 if ( this.format == null ) 215 { 216 this.format = this.getDefaultFormat(); 217 } 218 219 return this.format.intValue(); 220 } 221 222 /** 223 * Sets the constant of the format to use when formatting Bankleitzahl instances. 224 * 225 * @param value the constant of the format to use when formatting Bankleitzahl instances. 226 * 227 * @throws IllegalArgumentException if {@code format} is neither {@code ELECTRONIC_FORMAT} nor 228 * {@code LETTER_FORMAT}. 229 * 230 * @see Bankleitzahl#ELECTRONIC_FORMAT 231 * @see Bankleitzahl#LETTER_FORMAT 232 */ 233 public void setFormat( final int value ) 234 { 235 if ( value != Bankleitzahl.ELECTRONIC_FORMAT && value != Bankleitzahl.LETTER_FORMAT ) 236 { 237 throw new IllegalArgumentException( Integer.toString( value ) ); 238 } 239 240 this.format = new Integer( value ); 241 } 242 243 /** 244 * Gets the flag indicating if validation is performed. 245 * 246 * @return {@code true} if the fields' value is validated; {@code false} if no validation of the fields' value is 247 * performed. 248 */ 249 public boolean isValidating() 250 { 251 if ( this.validating == null ) 252 { 253 this.validating = this.isDefaultValidating(); 254 } 255 256 return this.validating.booleanValue(); 257 } 258 259 /** 260 * Sets the flag indicating if validation should be performed. 261 * 262 * @param value {@code true} to validate the fields' values; {@code false} to not validate the fields' values. 263 */ 264 public void setValidating( boolean value ) 265 { 266 this.validating = Boolean.valueOf( value ); 267 } 268 269 /** 270 * Updates the component's tooltip to show information available for the value returned by 271 * {@link #getBankleitzahl()}. This method is called whenever a {@code PropertyChangeEvent} for the property with 272 * name {@code value} occurs. 273 */ 274 private void updateTooltip() 275 { 276 final Bankleitzahl blz = this.getBankleitzahl(); 277 final StringBuffer tooltip = new StringBuffer( 200 ); 278 279 if ( blz != null ) 280 { 281 tooltip.append( "<html>" ); 282 283 try 284 { 285 final BankleitzahlInfo headOffice = this.getBankleitzahlenVerzeichnis().getHeadOffice( blz ); 286 if ( headOffice != null ) 287 { 288 tooltip.append( "<b>" ).append( this.getHeadOfficeInfoMessage( this.getLocale() ) ). 289 append( "</b><br>" ); 290 291 this.appendBankleitzahlInfo( headOffice, tooltip ); 292 } 293 else 294 { 295 tooltip.append( new UnknownBankleitzahlMessage( blz ).getText( this.getLocale() ) ); 296 } 297 } 298 catch ( BankleitzahlExpirationException e ) 299 { 300 tooltip.append( new BankleitzahlExpirationMessage( 301 e.getExpiredBankleitzahlInfo() ).getText( this.getLocale() ) ); 302 303 tooltip.append( "<br>" ).append( new BankleitzahlReplacementMessage( 304 e.getReplacingBankleitzahlInfo() ).getText( this.getLocale() ) ); 305 306 tooltip.append( "<br>" ); 307 this.appendBankleitzahlInfo( e.getReplacingBankleitzahlInfo(), tooltip ); 308 } 309 310 tooltip.append( "</html>" ); 311 } 312 313 SwingUtilities.invokeLater( new Runnable() 314 { 315 316 public void run() 317 { 318 setToolTipText( tooltip.length() > 0 ? tooltip.toString() : null ); 319 } 320 321 } ); 322 323 } 324 325 /** 326 * Checks configured properties. 327 * 328 * @throws PropertyException for invalid property values. 329 */ 330 private void assertValidProperties() 331 { 332 if ( this.getFormat() != Bankleitzahl.ELECTRONIC_FORMAT && this.getFormat() != Bankleitzahl.LETTER_FORMAT ) 333 { 334 throw new PropertyException( "format", Integer.toString( this.getFormat() ) ); 335 } 336 } 337 338 /** 339 * Appends the tooltip text for a given {@code BankleitzahlInfo} to a given {@code StringBuffer}. 340 * 341 * @param bankleitzahlInfo The {@code BankleitzahlInfo} instance to append to {@code buf}. 342 * @param buf The {@code StringBuffer} instance to append {@code bankleitzahlInfo} to. 343 * 344 * @return {@code buf} with information about {@code bankleitzahlInfo} appended. 345 * 346 * @throws NullPointerException if {@code bankleitzahlInfo} is {@code null}. 347 */ 348 private StringBuffer appendBankleitzahlInfo( final BankleitzahlInfo bankleitzahlInfo, StringBuffer buf ) 349 { 350 if ( bankleitzahlInfo == null ) 351 { 352 throw new NullPointerException( "bankleitzahlInfo" ); 353 } 354 if ( buf == null ) 355 { 356 buf = new StringBuffer(); 357 } 358 359 final NumberFormat zipFormat = new DecimalFormat( "#####" ); 360 buf.append( "<br>" ).append( bankleitzahlInfo.getName() ); 361 362 if ( bankleitzahlInfo.getDescription() != null && bankleitzahlInfo.getDescription().trim().length() > 0 && 363 !bankleitzahlInfo.getName().equals( bankleitzahlInfo.getDescription() ) ) 364 { 365 buf.append( " (" ).append( bankleitzahlInfo.getDescription() ).append( ")" ); 366 } 367 368 buf.append( "<br>" ).append( zipFormat.format( bankleitzahlInfo.getPostalCode() ) ).append( " " ). 369 append( bankleitzahlInfo.getCity() ); 370 371 buf.append( "<br>" ).append( this.getBlzInfoMessage( 372 this.getLocale(), bankleitzahlInfo.getBankCode().format( this.getFormat() ) ) ); 373 374 if ( bankleitzahlInfo.getBic() != null && bankleitzahlInfo.getBic().trim().length() > 0 ) 375 { 376 buf.append( "<br>" ).append( this.getBicInfoMessage( this.getLocale(), bankleitzahlInfo.getBic() ) ); 377 } 378 379 return buf; 380 } 381 382 //--Dependencies------------------------------------------------------------ 383 384// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies 385 // This section is managed by jdtaus-container-mojo. 386 387 /** 388 * Gets the configured <code>BankleitzahlenVerzeichnis</code> implementation. 389 * 390 * @return The configured <code>BankleitzahlenVerzeichnis</code> implementation. 391 */ 392 private BankleitzahlenVerzeichnis getBankleitzahlenVerzeichnis() 393 { 394 return (BankleitzahlenVerzeichnis) ContainerFactory.getContainer(). 395 getDependency( this, "BankleitzahlenVerzeichnis" ); 396 397 } 398 399// </editor-fold>//GEN-END:jdtausDependencies 400 401 //------------------------------------------------------------Dependencies-- 402 //--Properties-------------------------------------------------------------- 403 404// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties 405 // This section is managed by jdtaus-container-mojo. 406 407 /** 408 * Gets the value of property <code>defaultValidating</code>. 409 * 410 * @return Default value of the flag indicating if validation should be performed. 411 */ 412 private java.lang.Boolean isDefaultValidating() 413 { 414 return (java.lang.Boolean) ContainerFactory.getContainer(). 415 getProperty( this, "defaultValidating" ); 416 417 } 418 419 /** 420 * Gets the value of property <code>defaultFormat</code>. 421 * 422 * @return Default value of the format to use when formatting Bankleitzahl instances (3001 = electronic format, 3002 letter format). 423 */ 424 private java.lang.Integer getDefaultFormat() 425 { 426 return (java.lang.Integer) ContainerFactory.getContainer(). 427 getProperty( this, "defaultFormat" ); 428 429 } 430 431// </editor-fold>//GEN-END:jdtausProperties 432 433 //--------------------------------------------------------------Properties-- 434 //--Messages---------------------------------------------------------------- 435 436// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages 437 // This section is managed by jdtaus-container-mojo. 438 439 /** 440 * Gets the text of message <code>blzInfo</code>. 441 * <blockquote><pre>BLZ {0}</pre></blockquote> 442 * <blockquote><pre>BLZ {0}</pre></blockquote> 443 * 444 * @param locale The locale of the message instance to return. 445 * @param bankCode format argument. 446 * 447 * @return the text of message <code>blzInfo</code>. 448 */ 449 private String getBlzInfoMessage( final Locale locale, 450 final java.lang.String bankCode ) 451 { 452 return ContainerFactory.getContainer(). 453 getMessage( this, "blzInfo", locale, 454 new Object[] 455 { 456 bankCode 457 }); 458 459 } 460 461 /** 462 * Gets the text of message <code>bicInfo</code>. 463 * <blockquote><pre>BIC {0}</pre></blockquote> 464 * <blockquote><pre>BIC {0}</pre></blockquote> 465 * 466 * @param locale The locale of the message instance to return. 467 * @param bic format argument. 468 * 469 * @return the text of message <code>bicInfo</code>. 470 */ 471 private String getBicInfoMessage( final Locale locale, 472 final java.lang.String bic ) 473 { 474 return ContainerFactory.getContainer(). 475 getMessage( this, "bicInfo", locale, 476 new Object[] 477 { 478 bic 479 }); 480 481 } 482 483 /** 484 * Gets the text of message <code>headOfficeInfo</code>. 485 * <blockquote><pre>Hauptstelle</pre></blockquote> 486 * <blockquote><pre>Headoffice</pre></blockquote> 487 * 488 * @param locale The locale of the message instance to return. 489 * 490 * @return the text of message <code>headOfficeInfo</code>. 491 */ 492 private String getHeadOfficeInfoMessage( final Locale locale ) 493 { 494 return ContainerFactory.getContainer(). 495 getMessage( this, "headOfficeInfo", locale, null ); 496 497 } 498 499// </editor-fold>//GEN-END:jdtausMessages 500 501 //----------------------------------------------------------------Messages-- 502}