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 */
017package org.apache.commons.beanutils.converters;
018
019import 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 */
055public 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}