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
019/**
020 * {@link org.apache.commons.beanutils.Converter} implementation that handles conversion
021 * to and from <b>Boolean</b> objects.
022 * {@link org.apache.commons.beanutils.Converter} implementation that
023 * handles conversion to and from <b>java.lang.Boolean</b> objects.
024 * <p>
025 * Can be configured to either return a <i>default value</i> or throw a
026 * <code>ConversionException</code> if a conversion error occurs.
027 * <p>
028 * By default any object whose string representation is one of the values
029 * {"yes", "y", "true", "on", "1"} is converted to Boolean.TRUE, and
030 * string representations {"no", "n", "false", "off", "0"} are converted
031 * to Boolean.FALSE. The recognized true/false strings can be changed by:
032 * <pre>
033 *  String[] trueStrings = {"oui", "o", "1"};
034 *  String[] falseStrings = {"non", "n", "0"};
035 *  Converter bc = new BooleanConverter(trueStrings, falseStrings);
036 *  ConvertUtils.register(bc, Boolean.class);
037 *  ConvertUtils.register(bc, Boolean.TYPE);
038 * </pre>
039 * In addition, it is recommended that the BooleanArrayConverter also be
040 * modified to recognise the same set of values:
041 * <pre>
042 *   Converter bac = new BooleanArrayConverter(bc, BooleanArrayConverter.NO_DEFAULT);
043 *   ConvertUtils.register(bac, bac.MODEL);
044 * </pre>
045 * </p>
046 *
047 * <p>Case is ignored when converting values to true or false.</p>
048 *
049 * @version $Id$
050 * @since 1.3
051 */
052public final class BooleanConverter extends AbstractConverter {
053
054
055    // ----------------------------------------------------------- Constructors
056
057
058    /**
059     * Create a {@link org.apache.commons.beanutils.Converter} that will throw a
060     * {@link org.apache.commons.beanutils.ConversionException}
061     * if a conversion error occurs, ie the string value being converted is
062     * not one of the known true strings, nor one of the known false strings.
063     */
064    public BooleanConverter() {
065        super();
066    }
067
068
069    /**
070     * Create a {@link org.apache.commons.beanutils.Converter} that will return the specified default value
071     * if a conversion error occurs, ie the string value being converted is
072     * not one of the known true strings, nor one of the known false strings.
073     *
074     * @param defaultValue The default value to be returned if the value
075     *  being converted is not recognized. This value may be null, in which
076     *  case null will be returned on conversion failure. When non-null, it is
077     *  expected that this value will be either Boolean.TRUE or Boolean.FALSE.
078     *  The special value BooleanConverter.NO_DEFAULT can also be passed here,
079     *  in which case this constructor acts like the no-argument one.
080     */
081    public BooleanConverter(final Object defaultValue) {
082        super();
083        if (defaultValue != NO_DEFAULT) {
084            setDefaultValue(defaultValue);
085        }
086    }
087
088    /**
089     * Create a {@link org.apache.commons.beanutils.Converter} that will throw a
090     * {@link org.apache.commons.beanutils.ConversionException}
091     * if a conversion error occurs, ie the string value being converted is
092     * not one of the known true strings, nor one of the known false strings.
093     * <p>
094     * The provided string arrays are copied, so that changes to the elements
095     * of the array after this call is made do not affect this object.
096     *
097     * @param trueStrings is the set of strings which should convert to the
098     *  value Boolean.TRUE. The value null must not be present. Case is
099     *  ignored.
100     *
101     * @param falseStrings is the set of strings which should convert to the
102     *  value Boolean.TRUE. The value null must not be present. Case is
103     *  ignored.
104     * @since 1.8.0
105     */
106    public BooleanConverter(final String[] trueStrings, final String[] falseStrings) {
107        super();
108        this.trueStrings = copyStrings(trueStrings);
109        this.falseStrings = copyStrings(falseStrings);
110    }
111
112    /**
113     * Create a {@link org.apache.commons.beanutils.Converter} that will return
114     * the specified default value if a conversion error occurs.
115     * <p>
116     * The provided string arrays are copied, so that changes to the elements
117     * of the array after this call is made do not affect this object.
118     *
119     * @param trueStrings is the set of strings which should convert to the
120     *  value Boolean.TRUE. The value null must not be present. Case is
121     *  ignored.
122     *
123     * @param falseStrings is the set of strings which should convert to the
124     *  value Boolean.TRUE. The value null must not be present. Case is
125     *  ignored.
126     *
127     * @param defaultValue The default value to be returned if the value
128     *  being converted is not recognized. This value may be null, in which
129     *  case null will be returned on conversion failure. When non-null, it is
130     *  expected that this value will be either Boolean.TRUE or Boolean.FALSE.
131     *  The special value BooleanConverter.NO_DEFAULT can also be passed here,
132     *  in which case an exception will be thrown on conversion failure.
133     * @since 1.8.0
134     */
135    public BooleanConverter(final String[] trueStrings, final String[] falseStrings,
136                final Object defaultValue) {
137        super();
138        this.trueStrings = copyStrings(trueStrings);
139        this.falseStrings = copyStrings(falseStrings);
140        if (defaultValue != NO_DEFAULT) {
141            setDefaultValue(defaultValue);
142        }
143    }
144
145
146    // ----------------------------------------------------- Static Variables
147
148
149    /**
150     * This is a special reference that can be passed as the "default object"
151     * to the constructor to indicate that no default is desired. Note that
152     * the value 'null' cannot be used for this purpose, as the caller may
153     * want a null to be returned as the default.
154     * @deprecated Use constructors without default value.
155     */
156    @Deprecated
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    @Override
181    protected Class<Boolean> getDefaultType() {
182        return Boolean.class;
183    }
184
185    /**
186     * Convert the specified input object into an output object of the
187     * specified type.
188     *
189     * @param <T> Target type of the conversion.
190     * @param type is the type to which this value should be converted. In the
191     *  case of this BooleanConverter class, this value is ignored.
192     *
193     * @param value is the input value to be converted. The toString method
194     *  shall be invoked on this object, and the result compared (ignoring
195     *  case) against the known "true" and "false" string values.
196     *
197     * @return Boolean.TRUE if the value was a recognized "true" value,
198     *  Boolean.FALSE if the value was a recognized "false" value, or
199     *  the default value if the value was not recognized and the constructor
200     *  was provided with a default value.
201     *
202     * @throws Throwable if an error occurs converting to the specified type
203     * @since 1.8.0
204     */
205    @Override
206    protected <T> T convertToType(final Class<T> type, final Object value) throws Throwable {
207
208        if (Boolean.class.equals(type) || Boolean.TYPE.equals(type)) {
209            // All the values in the trueStrings and falseStrings arrays are
210            // guaranteed to be lower-case. By converting the input value
211            // to lowercase too, we can use the efficient String.equals method
212            // instead of the less-efficient String.equalsIgnoreCase method.
213            final String stringValue = value.toString().toLowerCase();
214
215            for (String trueString : trueStrings) {
216                if (trueString.equals(stringValue)) {
217                    return type.cast(Boolean.TRUE);
218                }
219            }
220
221            for (String falseString : falseStrings) {
222                if (falseString.equals(stringValue)) {
223                    return type.cast(Boolean.FALSE);
224                }
225            }
226        }
227
228        throw conversionException(type, value);
229    }
230
231    /**
232     * This method creates a copy of the provided array, and ensures that
233     * all the strings in the newly created array contain only lower-case
234     * letters.
235     * <p>
236     * Using this method to copy string arrays means that changes to the
237     * src array do not modify the dst array.
238     */
239    private static String[] copyStrings(final String[] src) {
240        final String[] dst = new String[src.length];
241        for(int i=0; i<src.length; ++i) {
242            dst[i] = src[i].toLowerCase();
243        }
244        return dst;
245    }
246}