1 /*
2 * jDTAUS Banking Utilities
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.util.swing;
22
23 import java.beans.PropertyChangeEvent;
24 import java.beans.PropertyChangeListener;
25 import java.text.DecimalFormat;
26 import java.text.NumberFormat;
27 import java.text.ParseException;
28 import java.util.Locale;
29 import javax.swing.JFormattedTextField;
30 import javax.swing.JFormattedTextField.AbstractFormatter;
31 import javax.swing.SwingUtilities;
32 import javax.swing.text.AttributeSet;
33 import javax.swing.text.BadLocationException;
34 import javax.swing.text.DocumentFilter;
35 import javax.swing.text.DocumentFilter.FilterBypass;
36 import org.jdtaus.banking.Bankleitzahl;
37 import org.jdtaus.banking.BankleitzahlExpirationException;
38 import org.jdtaus.banking.BankleitzahlInfo;
39 import org.jdtaus.banking.BankleitzahlenVerzeichnis;
40 import org.jdtaus.banking.messages.BankleitzahlExpirationMessage;
41 import org.jdtaus.banking.messages.BankleitzahlReplacementMessage;
42 import org.jdtaus.banking.messages.UnknownBankleitzahlMessage;
43 import org.jdtaus.core.container.ContainerFactory;
44 import org.jdtaus.core.container.PropertyException;
45
46 /**
47 * {@code JFormattedTextField} supporting the {@code Bankleitzahl} type.
48 * <p>This textfield uses the {@link Bankleitzahl} type for parsing and formatting. An empty string value is treated as
49 * {@code null}. Property {@code format} controls formatting and takes one of the format constants defined in class
50 * {@code Bankleitzahl}. By default the {@code ELECTRONIC_FORMAT} is used. The {@code validating} flag controls
51 * validation of values entered into the textfield. If {@code true} (default), a {@code DocumentFilter} is registered
52 * with the textfield disallowing invalid values, that is, values which are not {@code null} and not empty strings and
53 * for which the {@link Bankleitzahl#parse(String)} method throws a {@code ParseException}. The field's tooltip
54 * text is updated with information about the field's value.</p>
55 *
56 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
57 * @version $JDTAUS: BankleitzahlTextField.java 8864 2014-01-10 17:13:30Z schulte $
58 */
59 public final class BankleitzahlTextField extends JFormattedTextField
60 {
61
62 /** Serial version UID for backwards compatibility with 1.1.x classes. */
63 private static final long serialVersionUID = -5461742987164339047L;
64
65 /**
66 * The constant of the format to use when formatting Bankleitzahl instances.
67 * @serial
68 */
69 private Integer format;
70
71 /**
72 * Flag indicating if validation is performed.
73 * @serial
74 */
75 private Boolean validating;
76
77 /** Creates a new default {@code BankleitzahlTextField} instance. */
78 public BankleitzahlTextField()
79 {
80 super();
81 this.assertValidProperties();
82 this.setColumns( Bankleitzahl.MAX_CHARACTERS );
83 this.setFormatterFactory( new AbstractFormatterFactory()
84 {
85
86 public AbstractFormatter getFormatter( final JFormattedTextField ftf )
87 {
88 return new AbstractFormatter()
89 {
90
91 public Object stringToValue( final String text ) throws ParseException
92 {
93 Object value = null;
94
95 if ( text != null && text.trim().length() > 0 )
96 {
97 value = Bankleitzahl.parse( text );
98 }
99
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(
150 fb.getDocument().getText( 0, fb.getDocument().getLength() ) );
151
152 b.delete( o, o + l );
153
154 if ( s != null )
155 {
156 b.insert( o, s );
157 }
158
159 try
160 {
161 Bankleitzahl.parse( b.toString() );
162 }
163 catch ( ParseException e )
164 {
165 invalidEdit();
166 return;
167 }
168 }
169
170 super.replace( fb, o, l, s, a );
171 }
172
173 };
174 }
175
176 };
177 }
178
179 } );
180
181 this.addPropertyChangeListener( "value", new PropertyChangeListener()
182 {
183
184 public void propertyChange( final PropertyChangeEvent evt )
185 {
186 new Thread()
187 {
188
189 public void run()
190 {
191 updateTooltip( (Bankleitzahl) evt.getNewValue() );
192 }
193
194 }.start();
195 }
196
197 } );
198 }
199
200 /**
201 * Gets the last valid {@code Bankleitzahl}.
202 *
203 * @return the last valid {@code Bankleitzahl} or {@code null}.
204 */
205 public Bankleitzahl getBankleitzahl()
206 {
207 return (Bankleitzahl) this.getValue();
208 }
209
210 /**
211 * Gets the constant of the format used when formatting Bankleitzahl instances.
212 *
213 * @return the constant of the format used when formatting Bankleitzahl instances.
214 *
215 * @see Bankleitzahl#ELECTRONIC_FORMAT
216 * @see Bankleitzahl#LETTER_FORMAT
217 */
218 public int getFormat()
219 {
220 if ( this.format == null )
221 {
222 this.format = this.getDefaultFormat();
223 }
224
225 return this.format.intValue();
226 }
227
228 /**
229 * Sets the constant of the format to use when formatting Bankleitzahl instances.
230 *
231 * @param value the constant of the format to use when formatting Bankleitzahl instances.
232 *
233 * @throws IllegalArgumentException if {@code format} is neither {@code ELECTRONIC_FORMAT} nor
234 * {@code LETTER_FORMAT}.
235 *
236 * @see Bankleitzahl#ELECTRONIC_FORMAT
237 * @see Bankleitzahl#LETTER_FORMAT
238 */
239 public void setFormat( final int value )
240 {
241 if ( value != Bankleitzahl.ELECTRONIC_FORMAT && value != Bankleitzahl.LETTER_FORMAT )
242 {
243 throw new IllegalArgumentException( Integer.toString( value ) );
244 }
245
246 this.format = new Integer( value );
247 }
248
249 /**
250 * Gets the flag indicating if validation is performed.
251 *
252 * @return {@code true} if the fields' value is validated; {@code false} if no validation of the fields' value is
253 * performed.
254 */
255 public boolean isValidating()
256 {
257 if ( this.validating == null )
258 {
259 this.validating = this.isDefaultValidating();
260 }
261
262 return this.validating.booleanValue();
263 }
264
265 /**
266 * Sets the flag indicating if validation should be performed.
267 *
268 * @param value {@code true} to validate the fields' values; {@code false} to not validate the fields' values.
269 */
270 public void setValidating( boolean value )
271 {
272 this.validating = Boolean.valueOf( value );
273 }
274
275 /**
276 * Updates the component's tooltip to show information available for the value returned by
277 * {@link #getBankleitzahl()}. This method is called whenever a {@code PropertyChangeEvent} for the property with
278 * name {@code value} occurs.
279 */
280 private void updateTooltip( final Bankleitzahl bankCode )
281 {
282 final StringBuffer tooltip = new StringBuffer( 200 );
283
284 if ( bankCode != null )
285 {
286 tooltip.append( "<html>" );
287
288 try
289 {
290 final BankleitzahlInfo headOffice = this.getBankleitzahlenVerzeichnis().getHeadOffice( bankCode );
291
292 if ( headOffice != null )
293 {
294 tooltip.append( "<b>" ).append( this.getHeadOfficeInfoMessage( this.getLocale() ) ).
295 append( "</b><br>" );
296
297 this.appendBankleitzahlInfo( headOffice, tooltip );
298 }
299 else
300 {
301 tooltip.append( new UnknownBankleitzahlMessage( bankCode ).getText( this.getLocale() ) );
302 }
303 }
304 catch ( final BankleitzahlExpirationException e )
305 {
306 tooltip.append( new BankleitzahlExpirationMessage(
307 e.getExpiredBankleitzahlInfo() ).getText( this.getLocale() ) );
308
309 tooltip.append( "<br>" ).append( new BankleitzahlReplacementMessage(
310 e.getReplacingBankleitzahlInfo() ).getText( this.getLocale() ) );
311
312 tooltip.append( "<br>" );
313 this.appendBankleitzahlInfo( e.getReplacingBankleitzahlInfo(), tooltip );
314 }
315
316 tooltip.append( "</html>" );
317 }
318
319 SwingUtilities.invokeLater( new Runnable()
320 {
321
322 public void run()
323 {
324 setToolTipText( tooltip.length() > 0 ? tooltip.toString() : null );
325 }
326
327 } );
328
329 }
330
331 /**
332 * Checks configured properties.
333 *
334 * @throws PropertyException for invalid property values.
335 */
336 private void assertValidProperties()
337 {
338 if ( this.getFormat() != Bankleitzahl.ELECTRONIC_FORMAT && this.getFormat() != Bankleitzahl.LETTER_FORMAT )
339 {
340 throw new PropertyException( "format", Integer.toString( this.getFormat() ) );
341 }
342 }
343
344 /**
345 * Appends the tooltip text for a given {@code BankleitzahlInfo} to a given {@code StringBuffer}.
346 *
347 * @param bankleitzahlInfo The {@code BankleitzahlInfo} instance to append to {@code buf}.
348 * @param buf The {@code StringBuffer} instance to append {@code bankleitzahlInfo} to.
349 *
350 * @return {@code buf} with information about {@code bankleitzahlInfo} appended.
351 *
352 * @throws NullPointerException if {@code bankleitzahlInfo} is {@code null}.
353 */
354 private StringBuffer appendBankleitzahlInfo( final BankleitzahlInfo bankleitzahlInfo, StringBuffer buf )
355 {
356 if ( bankleitzahlInfo == null )
357 {
358 throw new NullPointerException( "bankleitzahlInfo" );
359 }
360 if ( buf == null )
361 {
362 buf = new StringBuffer();
363 }
364
365 final NumberFormat zipFormat = new DecimalFormat( "#####" );
366 buf.append( "<br>" ).append( bankleitzahlInfo.getName() );
367
368 if ( bankleitzahlInfo.getDescription() != null && bankleitzahlInfo.getDescription().trim().length() > 0
369 && !bankleitzahlInfo.getName().equals( bankleitzahlInfo.getDescription() ) )
370 {
371 buf.append( " (" ).append( bankleitzahlInfo.getDescription() ).append( ")" );
372 }
373
374 buf.append( "<br>" ).append( zipFormat.format( bankleitzahlInfo.getPostalCode() ) ).append( " " ).
375 append( bankleitzahlInfo.getCity() );
376
377 buf.append( "<br>" ).append( this.getBlzInfoMessage(
378 this.getLocale(), bankleitzahlInfo.getBankCode().format( this.getFormat() ) ) );
379
380 if ( bankleitzahlInfo.getBic() != null && bankleitzahlInfo.getBic().trim().length() > 0 )
381 {
382 buf.append( "<br>" ).append( this.getBicInfoMessage( this.getLocale(), bankleitzahlInfo.getBic() ) );
383 }
384
385 if ( bankleitzahlInfo.isMarkedForDeletion() )
386 {
387 buf.append( "<br><br><b>" ).append( this.getBankleitzahlMarkedForDeletionInfoMessage(
388 this.getLocale(), this.getBankleitzahlenVerzeichnis().getDateOfExpiration() ) ).append( "</b>" );
389
390 }
391
392 return buf;
393 }
394
395 //--Dependencies------------------------------------------------------------
396
397 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies
398 // This section is managed by jdtaus-container-mojo.
399
400 /**
401 * Gets the configured <code>BankleitzahlenVerzeichnis</code> implementation.
402 *
403 * @return The configured <code>BankleitzahlenVerzeichnis</code> implementation.
404 */
405 private BankleitzahlenVerzeichnis getBankleitzahlenVerzeichnis()
406 {
407 return (BankleitzahlenVerzeichnis) ContainerFactory.getContainer().
408 getDependency( this, "BankleitzahlenVerzeichnis" );
409
410 }
411
412 // </editor-fold>//GEN-END:jdtausDependencies
413
414 //------------------------------------------------------------Dependencies--
415 //--Properties--------------------------------------------------------------
416
417 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
418 // This section is managed by jdtaus-container-mojo.
419
420 /**
421 * Gets the value of property <code>defaultValidating</code>.
422 *
423 * @return Default value of the flag indicating if validation should be performed.
424 */
425 private java.lang.Boolean isDefaultValidating()
426 {
427 return (java.lang.Boolean) ContainerFactory.getContainer().
428 getProperty( this, "defaultValidating" );
429
430 }
431
432 /**
433 * Gets the value of property <code>defaultFormat</code>.
434 *
435 * @return Default value of the format to use when formatting Bankleitzahl instances (3001 = electronic format, 3002 letter format).
436 */
437 private java.lang.Integer getDefaultFormat()
438 {
439 return (java.lang.Integer) ContainerFactory.getContainer().
440 getProperty( this, "defaultFormat" );
441
442 }
443
444 // </editor-fold>//GEN-END:jdtausProperties
445
446 //--------------------------------------------------------------Properties--
447 //--Messages----------------------------------------------------------------
448
449 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages
450 // This section is managed by jdtaus-container-mojo.
451
452 /**
453 * Gets the text of message <code>blzInfo</code>.
454 * <blockquote><pre>BLZ {0}</pre></blockquote>
455 * <blockquote><pre>BLZ {0}</pre></blockquote>
456 *
457 * @param locale The locale of the message instance to return.
458 * @param bankCode format parameter.
459 *
460 * @return the text of message <code>blzInfo</code>.
461 */
462 private String getBlzInfoMessage( final Locale locale,
463 final java.lang.String bankCode )
464 {
465 return ContainerFactory.getContainer().
466 getMessage( this, "blzInfo", locale,
467 new Object[]
468 {
469 bankCode
470 });
471
472 }
473
474 /**
475 * Gets the text of message <code>bicInfo</code>.
476 * <blockquote><pre>BIC {0}</pre></blockquote>
477 * <blockquote><pre>BIC {0}</pre></blockquote>
478 *
479 * @param locale The locale of the message instance to return.
480 * @param bic format parameter.
481 *
482 * @return the text of message <code>bicInfo</code>.
483 */
484 private String getBicInfoMessage( final Locale locale,
485 final java.lang.String bic )
486 {
487 return ContainerFactory.getContainer().
488 getMessage( this, "bicInfo", locale,
489 new Object[]
490 {
491 bic
492 });
493
494 }
495
496 /**
497 * Gets the text of message <code>headOfficeInfo</code>.
498 * <blockquote><pre>Hauptstelle</pre></blockquote>
499 * <blockquote><pre>Headoffice</pre></blockquote>
500 *
501 * @param locale The locale of the message instance to return.
502 *
503 * @return the text of message <code>headOfficeInfo</code>.
504 */
505 private String getHeadOfficeInfoMessage( final Locale locale )
506 {
507 return ContainerFactory.getContainer().
508 getMessage( this, "headOfficeInfo", locale, null );
509
510 }
511
512 /**
513 * Gets the text of message <code>bankleitzahlMarkedForDeletionInfo</code>.
514 * <blockquote><pre>Vorgesehen zur Löschung am {0,date,full}.</pre></blockquote>
515 * <blockquote><pre>Marked for deletion at {0,date,full}.</pre></blockquote>
516 *
517 * @param locale The locale of the message instance to return.
518 * @param deletionDate format parameter.
519 *
520 * @return the text of message <code>bankleitzahlMarkedForDeletionInfo</code>.
521 */
522 private String getBankleitzahlMarkedForDeletionInfoMessage( final Locale locale,
523 final java.util.Date deletionDate )
524 {
525 return ContainerFactory.getContainer().
526 getMessage( this, "bankleitzahlMarkedForDeletionInfo", locale,
527 new Object[]
528 {
529 deletionDate
530 });
531
532 }
533
534 // </editor-fold>//GEN-END:jdtausMessages
535
536 //----------------------------------------------------------------Messages--
537 }