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.text.ParseException;
024import javax.swing.JFormattedTextField;
025import javax.swing.JFormattedTextField.AbstractFormatter;
026import javax.swing.text.AttributeSet;
027import javax.swing.text.BadLocationException;
028import javax.swing.text.DocumentFilter;
029import org.jdtaus.banking.AlphaNumericText27;
030import org.jdtaus.core.container.ContainerFactory;
031
032/**
033 * {@code JFormattedTextField} supporting the {@code AlphaNumericText27} type.
034 * <p>This textfield uses the {@link AlphaNumericText27} type for parsing and formatting. An empty string value is
035 * treated as {@code null}. The {@code normalizing} flag controls parsing. If {@code true} (default) the field's value
036 * is normalized using the {@link AlphaNumericText27#normalize(String)} method prior to parsing. The {@code validating}
037 * flag controls validation of values entered into the textfield. If {@code true} (default), a {@code DocumentFilter} is
038 * registered with the textfield disallowing invalid values, that is, values which are not {@code null} and not empty
039 * strings and for which the {@link AlphaNumericText27#parse(String)} method throws a {@code ParseException}.</p>
040 *
041 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
042 * @version $JDTAUS: AlphaNumericText27TextField.java 8864 2014-01-10 17:13:30Z schulte $
043 */
044public final class AlphaNumericText27TextField extends JFormattedTextField
045{
046
047    /** Serial version UID for backwards compatibility with 1.1.x classes. */
048    private static final long serialVersionUID = -8152767220100367519L;
049
050    /**
051     * Flag indicating if a normalizing parser is used.
052     * @serial
053     */
054    private Boolean normalizing;
055
056    /**
057     * Flag indicating if validation is performed.
058     * @serial
059     */
060    private Boolean validating;
061
062    /** Creates a new default {@code AlphaNumericText27TextField} instance. */
063    public AlphaNumericText27TextField()
064    {
065        super();
066        this.setColumns( AlphaNumericText27.MAX_LENGTH );
067        this.setFormatterFactory( new AbstractFormatterFactory()
068        {
069
070            public AbstractFormatter getFormatter( final JFormattedTextField ftf )
071            {
072                return new AbstractFormatter()
073                {
074
075                    public Object stringToValue( final String text ) throws ParseException
076                    {
077                        Object value = null;
078
079                        if ( text != null && text.trim().length() > 0 )
080                        {
081                            value = AlphaNumericText27.parse(
082                                isNormalizing() ? AlphaNumericText27.normalize( text ) : text );
083
084                        }
085
086                        return value;
087                    }
088
089                    public String valueToString( final Object value ) throws ParseException
090                    {
091                        String ret = null;
092
093                        if ( value instanceof AlphaNumericText27 )
094                        {
095                            final AlphaNumericText27 txt = (AlphaNumericText27) value;
096                            ret = txt.isEmpty() ? null : txt.format().trim();
097                        }
098
099                        return ret;
100                    }
101
102                    protected DocumentFilter getDocumentFilter()
103                    {
104                        return new DocumentFilter()
105                        {
106
107                            public void insertString( final FilterBypass fb, final int o, String s,
108                                                      final AttributeSet a ) throws BadLocationException
109                            {
110                                if ( isValidating() )
111                                {
112                                    if ( isNormalizing() )
113                                    {
114                                        final char[] chars = s.toCharArray();
115                                        for ( int i = chars.length - 1; i >= 0; i-- )
116                                        {
117                                            chars[i] = Character.toUpperCase( chars[i] );
118                                        }
119                                        s = String.valueOf( chars );
120                                    }
121
122                                    final StringBuffer b =
123                                        new StringBuffer( fb.getDocument().getLength() + s.length() );
124
125                                    b.append( fb.getDocument().getText( 0, fb.getDocument().getLength() ) );
126                                    b.insert( o, s );
127
128                                    try
129                                    {
130                                        AlphaNumericText27.parse( b.toString() );
131                                    }
132                                    catch ( ParseException e )
133                                    {
134                                        invalidEdit();
135                                        return;
136                                    }
137                                }
138
139                                super.insertString( fb, o, s, a );
140                            }
141
142                            public void replace( final FilterBypass fb, final int o, final int l, String s,
143                                                 final AttributeSet a ) throws BadLocationException
144                            {
145                                if ( isValidating() )
146                                {
147                                    if ( isNormalizing() )
148                                    {
149                                        final char[] chars = s.toCharArray();
150                                        for ( int i = chars.length - 1; i >= 0; i-- )
151                                        {
152                                            chars[i] = Character.toUpperCase( chars[i] );
153                                        }
154                                        s = String.valueOf( chars );
155                                    }
156
157                                    final StringBuffer b = new StringBuffer(
158                                        fb.getDocument().getText( 0, fb.getDocument().getLength() ) );
159
160                                    b.delete( o, o + l );
161
162                                    if ( s != null )
163                                    {
164                                        b.insert( o, s );
165                                    }
166
167                                    try
168                                    {
169                                        AlphaNumericText27.parse( b.toString() );
170                                    }
171                                    catch ( ParseException e )
172                                    {
173                                        invalidEdit();
174                                        return;
175                                    }
176                                }
177
178                                super.replace( fb, o, l, s, a );
179                            }
180
181                        };
182                    }
183
184                };
185            }
186
187        } );
188    }
189
190    /**
191     * Gets the last valid {@code AlphaNumericText27}.
192     *
193     * @return the last valid {@code AlphaNumericText27} or {@code null}.
194     */
195    public AlphaNumericText27 getAlphaNumericText27()
196    {
197        return (AlphaNumericText27) this.getValue();
198    }
199
200    /**
201     * Gets the flag indicating if a normalizing parser is used.
202     *
203     * @return {@code true} if a normalizing parser is used; {@code false} if a strict parser is used
204     * (defaults to {@code true}).
205     */
206    public boolean isNormalizing()
207    {
208        if ( this.normalizing == null )
209        {
210            this.normalizing = this.isDefaultNormalizing();
211        }
212
213        return this.normalizing.booleanValue();
214    }
215
216    /**
217     * Sets the flag indicating if a normalizing parser should be used.
218     *
219     * @param value {@code true} to use a normalizing parser; {@code false} to use a strict parser
220     * (defaults to {@code true}).
221     */
222    public void setNormalizing( final boolean value )
223    {
224        this.normalizing = Boolean.valueOf( value );
225    }
226
227    /**
228     * Gets the flag indicating if validation is performed.
229     *
230     * @return {@code true} if the field's value is validated; {@code false} if no validation of the field's value is
231     * performed.
232     */
233    public boolean isValidating()
234    {
235        if ( this.validating == null )
236        {
237            this.validating = this.isDefaultValidating();
238        }
239
240        return this.validating.booleanValue();
241    }
242
243    /**
244     * Sets the flag indicating if validation should be performed.
245     *
246     * @param value {@code true} to validate the field's value; {@code false} to not validate the field's value.
247     */
248    public void setValidating( boolean value )
249    {
250        this.validating = Boolean.valueOf( value );
251    }
252
253    //--Properties--------------------------------------------------------------
254
255// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
256    // This section is managed by jdtaus-container-mojo.
257
258    /**
259     * Gets the value of property <code>defaultValidating</code>.
260     *
261     * @return Default value of the flag indicating if validation should be performed.
262     */
263    private java.lang.Boolean isDefaultValidating()
264    {
265        return (java.lang.Boolean) ContainerFactory.getContainer().
266            getProperty( this, "defaultValidating" );
267
268    }
269
270    /**
271     * Gets the value of property <code>defaultNormalizing</code>.
272     *
273     * @return Default value of the flag indicating if a normalizing parser should be used.
274     */
275    private java.lang.Boolean isDefaultNormalizing()
276    {
277        return (java.lang.Boolean) ContainerFactory.getContainer().
278            getProperty( this, "defaultNormalizing" );
279
280    }
281
282// </editor-fold>//GEN-END:jdtausProperties
283
284    //--------------------------------------------------------------Properties--
285}