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 8661 2012-09-27 11:29:58Z 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 =
158                                        new StringBuffer( fb.getDocument().getLength() + s.length() );
159
160                                    b.append( fb.getDocument().getText( 0, fb.getDocument().getLength() ) );
161                                    b.replace( o, o + s.length(), s );
162
163                                    try
164                                    {
165                                        AlphaNumericText27.parse( b.toString() );
166                                    }
167                                    catch ( ParseException e )
168                                    {
169                                        invalidEdit();
170                                        return;
171                                    }
172                                }
173
174                                super.replace( fb, o, l, s, a );
175                            }
176
177                        };
178                    }
179
180                };
181            }
182
183        } );
184    }
185
186    /**
187     * Gets the last valid {@code AlphaNumericText27}.
188     *
189     * @return the last valid {@code AlphaNumericText27} or {@code null}.
190     */
191    public AlphaNumericText27 getAlphaNumericText27()
192    {
193        return (AlphaNumericText27) this.getValue();
194    }
195
196    /**
197     * Gets the flag indicating if a normalizing parser is used.
198     *
199     * @return {@code true} if a normalizing parser is used; {@code false} if a strict parser is used
200     * (defaults to {@code true}).
201     */
202    public boolean isNormalizing()
203    {
204        if ( this.normalizing == null )
205        {
206            this.normalizing = this.isDefaultNormalizing();
207        }
208
209        return this.normalizing.booleanValue();
210    }
211
212    /**
213     * Sets the flag indicating if a normalizing parser should be used.
214     *
215     * @param value {@code true} to use a normalizing parser; {@code false} to use a strict parser
216     * (defaults to {@code true}).
217     */
218    public void setNormalizing( final boolean value )
219    {
220        this.normalizing = Boolean.valueOf( value );
221    }
222
223    /**
224     * Gets the flag indicating if validation is performed.
225     *
226     * @return {@code true} if the field's value is validated; {@code false} if no validation of the field's value is
227     * performed.
228     */
229    public boolean isValidating()
230    {
231        if ( this.validating == null )
232        {
233            this.validating = this.isDefaultValidating();
234        }
235
236        return this.validating.booleanValue();
237    }
238
239    /**
240     * Sets the flag indicating if validation should be performed.
241     *
242     * @param value {@code true} to validate the field's value; {@code false} to not validate the field's value.
243     */
244    public void setValidating( boolean value )
245    {
246        this.validating = Boolean.valueOf( value );
247    }
248
249    //--Properties--------------------------------------------------------------
250
251// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
252    // This section is managed by jdtaus-container-mojo.
253
254    /**
255     * Gets the value of property <code>defaultValidating</code>.
256     *
257     * @return Default value of the flag indicating if validation should be performed.
258     */
259    private java.lang.Boolean isDefaultValidating()
260    {
261        return (java.lang.Boolean) ContainerFactory.getContainer().
262            getProperty( this, "defaultValidating" );
263
264    }
265
266    /**
267     * Gets the value of property <code>defaultNormalizing</code>.
268     *
269     * @return Default value of the flag indicating if a normalizing parser should be used.
270     */
271    private java.lang.Boolean isDefaultNormalizing()
272    {
273        return (java.lang.Boolean) ContainerFactory.getContainer().
274            getProperty( this, "defaultNormalizing" );
275
276    }
277
278// </editor-fold>//GEN-END:jdtausProperties
279
280    //--------------------------------------------------------------Properties--
281}