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 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( "&nbsp;(" ).append( bankleitzahlInfo.getDescription() ).append( ")" );
372         }
373 
374         buf.append( "<br>" ).append( zipFormat.format( bankleitzahlInfo.getPostalCode() ) ).append( "&nbsp;" ).
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&nbsp;{0}</pre></blockquote>
455      * <blockquote><pre>BLZ&nbsp;{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&nbsp;{0}</pre></blockquote>
477      * <blockquote><pre>BIC&nbsp;{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 }