001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.beanutils.converters;
018    
019    import org.apache.commons.beanutils.ConversionException;
020    
021    /**
022     * {@link org.apache.commons.beanutils.Converter} implementaion that handles conversion
023     * to and from <b>Boolean</b> objects.
024     * {@link org.apache.commons.beanutils.Converter} implementaion that
025     * handles conversion to and from <b>java.lang.Boolean</b> objects.
026     * <p>
027     * Can be configured to either return a <i>default value</i> or throw a
028     * <code>ConversionException</code> if a conversion error occurs.
029     * <p>
030     * By default any object whose string representation is one of the values
031     * {"yes", "y", "true", "on", "1"} is converted to Boolean.TRUE, and 
032     * string representations {"no", "n", "false", "off", "0"} are converted
033     * to Boolean.FALSE. The recognised true/false strings can be changed by:
034     * <pre>
035     *  String[] trueStrings = {"oui", "o", "1"};
036     *  String[] falseStrings = {"non", "n", "0"};
037     *  Converter bc = new BooleanConverter(trueStrings, falseStrings);
038     *  ConvertUtils.register(bc, Boolean.class);
039     *  ConvertUtils.register(bc, Boolean.TYPE);
040     * </pre>
041     * In addition, it is recommended that the BooleanArrayConverter also be
042     * modified to recognise the same set of values:
043     * <pre>
044     *   Converter bac = new BooleanArrayConverter(bc, BooleanArrayConverter.NO_DEFAULT);
045     *   ConvertUtils.register(bac, bac.MODEL);
046     * </pre>
047     * </p>
048     * 
049     * <p>Case is ignored when converting values to true or false.</p>
050     *
051     * @author Craig R. McClanahan
052     * @version $Revision: 801644 $ $Date: 2009-08-06 14:38:56 +0100 (Thu, 06 Aug 2009) $
053     * @since 1.3
054     */
055    public final class BooleanConverter extends AbstractConverter {
056    
057    
058        // ----------------------------------------------------------- Constructors
059    
060    
061        /**
062         * Create a {@link org.apache.commons.beanutils.Converter} that will throw a {@link ConversionException}
063         * if a conversion error occurs, ie the string value being converted is
064         * not one of the known true strings, nor one of the known false strings.
065         */
066        public BooleanConverter() {
067            super();
068        }
069    
070    
071        /**
072         * Create a {@link org.apache.commons.beanutils.Converter} that will return the specified default value
073         * if a conversion error occurs, ie the string value being converted is
074         * not one of the known true strings, nor one of the known false strings.
075         *
076         * @param defaultValue The default value to be returned if the value
077         *  being converted is not recognised. This value may be null, in which
078         *  case null will be returned on conversion failure. When non-null, it is
079         *  expected that this value will be either Boolean.TRUE or Boolean.FALSE.
080         *  The special value BooleanConverter.NO_DEFAULT can also be passed here,
081         *  in which case this constructor acts like the no-argument one.
082         */
083        public BooleanConverter(Object defaultValue) {
084            super();
085            if (defaultValue != NO_DEFAULT) {
086                setDefaultValue(defaultValue);
087            }
088        }
089    
090        /**
091         * Create a {@link org.apache.commons.beanutils.Converter} that will throw a {@link ConversionException}
092         * if a conversion error occurs, ie the string value being converted is
093         * not one of the known true strings, nor one of the known false strings.
094         * <p>
095         * The provided string arrays are copied, so that changes to the elements
096         * of the array after this call is made do not affect this object.
097         *
098         * @param trueStrings is the set of strings which should convert to the
099         *  value Boolean.TRUE. The value null must not be present. Case is
100         *  ignored.
101         *
102         * @param falseStrings is the set of strings which should convert to the
103         *  value Boolean.TRUE. The value null must not be present. Case is
104         *  ignored.
105         * @since 1.8.0
106         */
107        public BooleanConverter(String[] trueStrings, String[] falseStrings) {
108            super();
109            this.trueStrings = copyStrings(trueStrings);
110            this.falseStrings = copyStrings(falseStrings);
111        }
112    
113        /**
114         * Create a {@link org.apache.commons.beanutils.Converter} that will return
115         * the specified default value if a conversion error occurs.
116         * <p>
117         * The provided string arrays are copied, so that changes to the elements
118         * of the array after this call is made do not affect this object.
119         *
120         * @param trueStrings is the set of strings which should convert to the
121         *  value Boolean.TRUE. The value null must not be present. Case is
122         *  ignored.
123         *
124         * @param falseStrings is the set of strings which should convert to the
125         *  value Boolean.TRUE. The value null must not be present. Case is
126         *  ignored.
127         *
128         * @param defaultValue The default value to be returned if the value
129         *  being converted is not recognised. This value may be null, in which
130         *  case null will be returned on conversion failure. When non-null, it is
131         *  expected that this value will be either Boolean.TRUE or Boolean.FALSE.
132         *  The special value BooleanConverter.NO_DEFAULT can also be passed here,
133         *  in which case an exception will be thrown on conversion failure.
134         * @since 1.8.0
135         */
136        public BooleanConverter(String[] trueStrings, String[] falseStrings, 
137                    Object defaultValue) {
138            super();
139            this.trueStrings = copyStrings(trueStrings);
140            this.falseStrings = copyStrings(falseStrings);
141            if (defaultValue != NO_DEFAULT) {
142                setDefaultValue(defaultValue);
143            }
144        }
145    
146    
147        // ----------------------------------------------------- Static Variables
148    
149    
150        /**
151         * This is a special reference that can be passed as the "default object"
152         * to the constructor to indicate that no default is desired. Note that
153         * the value 'null' cannot be used for this purpose, as the caller may
154         * want a null to be returned as the default.
155         * @deprecated Use constructors without default value.
156         */
157        public static final Object NO_DEFAULT = new Object();
158    
159    
160        // ----------------------------------------------------- Instance Variables
161    
162        /**
163         * The set of strings that are known to map to Boolean.TRUE.
164         */
165        private String[] trueStrings = {"true", "yes", "y", "on", "1"};
166    
167        /**
168         * The set of strings that are known to map to Boolean.FALSE.
169         */
170        private String[] falseStrings = {"false", "no", "n", "off", "0"};
171    
172        // --------------------------------------------------------- Protected Methods
173    
174        /**
175         * Return the default type this <code>Converter</code> handles.
176         *
177         * @return The default type this <code>Converter</code> handles.
178         * @since 1.8.0
179         */
180        protected Class getDefaultType() {
181            return Boolean.class;
182        }
183    
184        /**
185         * Convert the specified input object into an output object of the
186         * specified type.
187         *
188         * @param type is the type to which this value should be converted. In the
189         *  case of this BooleanConverter class, this value is ignored.
190         *
191         * @param value is the input value to be converted. The toString method
192         *  shall be invoked on this object, and the result compared (ignoring
193         *  case) against the known "true" and "false" string values.
194         *
195         * @return Boolean.TRUE if the value was a recognised "true" value, 
196         *  Boolean.FALSE if the value was a recognised "false" value, or
197         *  the default value if the value was not recognised and the constructor
198         *  was provided with a default value.
199         *
200         * @throws Throwable if an error occurs converting to the specified type
201         * @since 1.8.0
202         */
203        protected Object convertToType(Class type, Object value) throws Throwable {
204    
205            // All the values in the trueStrings and falseStrings arrays are
206            // guaranteed to be lower-case. By converting the input value
207            // to lowercase too, we can use the efficient String.equals method
208            // instead of the less-efficient String.equalsIgnoreCase method.
209            String stringValue = value.toString().toLowerCase();
210    
211            for(int i=0; i<trueStrings.length; ++i) {
212                if (trueStrings[i].equals(stringValue)) {
213                    return Boolean.TRUE;
214                }
215            }
216    
217            for(int i=0; i<falseStrings.length; ++i) {
218                if (falseStrings[i].equals(stringValue)) {
219                    return Boolean.FALSE;
220                }
221            }
222            
223            throw new ConversionException("Can't convert value '" + value + "' to a Boolean");
224        }
225    
226        /**
227         * This method creates a copy of the provided array, and ensures that
228         * all the strings in the newly created array contain only lower-case
229         * letters.
230         * <p>
231         * Using this method to copy string arrays means that changes to the
232         * src array do not modify the dst array.
233         */
234        private static String[] copyStrings(String[] src) {
235            String[] dst = new String[src.length];
236            for(int i=0; i<src.length; ++i) {
237                dst[i] = src[i].toLowerCase();
238            }
239            return dst;
240        }
241    }