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