View Javadoc

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 8661 2012-09-27 11:29:58Z 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( 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 }