BaseLocaleConverter.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.beanutils2.locale;

  18. import java.text.ParseException;
  19. import java.util.Locale;

  20. import org.apache.commons.beanutils2.ConversionException;
  21. import org.apache.commons.beanutils2.ConvertUtils;
  22. import org.apache.commons.logging.Log;
  23. import org.apache.commons.logging.LogFactory;

  24. /**
  25.  * The base class for all standard type locale-sensitive converters. It has {@link LocaleConverter} and {@link org.apache.commons.beanutils2.Converter}
  26.  * implementations, that convert an incoming locale-sensitive Object into an object of correspond type, optionally using a default value or throwing a
  27.  * {@link ConversionException} if a conversion error occurs.
  28.  *
  29.  * @param <T> The converter type.
  30.  */
  31. public abstract class BaseLocaleConverter<T> implements LocaleConverter<T> {

  32.     /**
  33.      * Builds instances of {@link BaseLocaleConverter} subclasses.
  34.      *
  35.      * @param <B> The builder type.
  36.      * @param <T> The converter type.
  37.      */
  38.     public abstract static class Builder<B extends Builder<B, T>, T> {

  39.         /** The default value specified to our Constructor, if any. */
  40.         protected T defaultValue;

  41.         /** The locale specified to our Constructor, by default - system locale. */
  42.         protected Locale locale = Locale.getDefault();

  43.         /** The flag indicating whether the given pattern string is localized or not. */
  44.         protected boolean localizedPattern;

  45.         /** The default pattern specified to our Constructor, if any. */
  46.         protected String pattern;

  47.         /** Should we return the default value on conversion errors? */
  48.         protected boolean useDefault;

  49.         /**
  50.          * Constructs a new instance.
  51.          */
  52.         public Builder() {
  53.             // empty
  54.         }

  55.         /**
  56.          * Returns this instance cast as the exact subclass type.
  57.          *
  58.          * @return this instance cast as the exact subclass type.
  59.          */
  60.         @SuppressWarnings("unchecked")
  61.         protected B asThis() {
  62.             return (B) this;
  63.         }

  64.         /**
  65.          * Gets a newly built instance.
  66.          *
  67.          * @return a newly built instance.
  68.          */
  69.         public abstract BaseLocaleConverter<?> get();

  70.         /**
  71.          * Sets the default value.
  72.          *
  73.          * @param defaultValue the default value.
  74.          * @return {@code this} instance.
  75.          */
  76.         public B setDefault(final T defaultValue) {
  77.             this.defaultValue = defaultValue;
  78.             return asThis();
  79.         }

  80.         /**
  81.          * Sets the locale.
  82.          *
  83.          * @param locale the locale.
  84.          * @return {@code this} instance.
  85.          */
  86.         public B setLocale(final Locale locale) {
  87.             this.locale = locale;
  88.             return asThis();
  89.         }

  90.         /**
  91.          * Sets the localized pattern.
  92.          *
  93.          * @param localizedPattern the localized pattern.
  94.          * @return {@code this} instance.
  95.          */
  96.         public B setLocalizedPattern(final boolean localizedPattern) {
  97.             this.localizedPattern = localizedPattern;
  98.             return asThis();
  99.         }

  100.         /**
  101.          * Sets the pattern.
  102.          *
  103.          * @param pattern the pattern.
  104.          * @return {@code this} instance.
  105.          */
  106.         public B setPattern(final String pattern) {
  107.             this.pattern = pattern;
  108.             return asThis();
  109.         }

  110.         /**
  111.          * Sets the use of default.
  112.          *
  113.          * @param useDefault the use of default.
  114.          * @return {@code this} instance.
  115.          */
  116.         public B setUseDefault(final boolean useDefault) {
  117.             this.useDefault = useDefault;
  118.             return asThis();
  119.         }

  120.     }

  121.     /** All logging goes through this logger */
  122.     private static final Log LOG = LogFactory.getLog(BaseLocaleConverter.class);

  123.     /**
  124.      * Checks whether the result of a conversion is conform to the specified target type. If this is the case, the passed in result object is cast to the
  125.      * correct target type. Otherwise, an exception is thrown.
  126.      *
  127.      * @param <T>    the desired result type
  128.      * @param type   the target class of the conversion
  129.      * @param result the conversion result object
  130.      * @return the result cast to the target class
  131.      * @throws ConversionException if the result object is not compatible with the target type
  132.      */
  133.     private static <R> R checkConversionResult(final Class<R> type, final Object result) {
  134.         if (type == null) {
  135.             // In this case we cannot do much: The result object is returned.
  136.             return (R) result;
  137.         }

  138.         if (result == null) {
  139.             return null;
  140.         }
  141.         if (type.isInstance(result)) {
  142.             return type.cast(result);
  143.         }
  144.         throw new ConversionException("Unsupported target type: " + type);
  145.     }

  146.     /** The default value specified to our Constructor, if any. */
  147.     protected final T defaultValue;

  148.     /** The locale specified to our Constructor, by default - system locale. */
  149.     protected final Locale locale;

  150.     /** The flag indicating whether the given pattern string is localized or not. */
  151.     protected final boolean localizedPattern;

  152.     /** The default pattern specified to our Constructor, if any. */
  153.     protected final String pattern;

  154.     /** Should we return the default value on conversion errors? */
  155.     protected final boolean useDefault;

  156.     /**
  157.      * Constructs a {@link LocaleConverter} that will return the specified default value or throw a {@link ConversionException} if a conversion error occurs.
  158.      *
  159.      * @param defaultValue The default value to be returned
  160.      * @param locale       The locale
  161.      * @param pattern      The conversion pattern
  162.      * @param useDefault   Indicate whether the default value is used or not
  163.      * @param locPattern   Indicate whether the pattern is localized or not
  164.      */
  165.     protected BaseLocaleConverter(final T defaultValue, final Locale locale, final String pattern, final boolean useDefault, final boolean locPattern) {
  166.         this.defaultValue = useDefault ? defaultValue : null;
  167.         this.useDefault = useDefault;
  168.         this.locale = locale != null ? locale : Locale.getDefault();
  169.         this.pattern = pattern;
  170.         this.localizedPattern = locPattern;
  171.     }

  172.     /**
  173.      * Converts the specified locale-sensitive input object into an output object of the specified type. The default pattern is used for the conversion.
  174.      *
  175.      * @param type  Data type to which this value should be converted
  176.      * @param value The input object to be converted
  177.      * @return The converted value
  178.      * @throws ConversionException if conversion cannot be performed successfully
  179.      */
  180.     @Override
  181.     public <R> R convert(final Class<R> type, final Object value) {
  182.         return convert(type, value, null);
  183.     }

  184.     /**
  185.      * Converts the specified locale-sensitive input object into an output object of the specified type.
  186.      *
  187.      * @param type    Data is type to which this value should be converted
  188.      * @param value   is the input object to be converted
  189.      * @param pattern is the pattern is used for the conversion; if null is passed then the default pattern associated with the converter object will be used.
  190.      * @return The converted value
  191.      * @throws ConversionException if conversion cannot be performed successfully
  192.      */
  193.     @Override
  194.     public <R> R convert(final Class<R> type, final Object value, final String pattern) {
  195.         final Class<R> targetType = ConvertUtils.primitiveToWrapper(type);
  196.         if (value == null) {
  197.             if (useDefault) {
  198.                 return getDefaultAs(targetType);
  199.             }
  200.             // symmetric BeanUtils function allows null
  201.             // so do not: throw new ConversionException("No value specified");
  202.             LOG.debug("Null value specified for conversion, returning null");
  203.             return null;
  204.         }

  205.         try {
  206.             if (pattern != null) {
  207.                 return checkConversionResult(targetType, parse(value, pattern));
  208.             }
  209.             return checkConversionResult(targetType, parse(value, this.pattern));
  210.         } catch (final Exception e) {
  211.             if (useDefault) {
  212.                 return getDefaultAs(targetType);
  213.             }
  214.             if (e instanceof ConversionException) {
  215.                 throw (ConversionException) e;
  216.             }
  217.             throw new ConversionException(e);
  218.         }
  219.     }

  220.     /**
  221.      * Converts the specified locale-sensitive input object into an output object. The default pattern is used for the conversion.
  222.      *
  223.      * @param value The input object to be converted
  224.      * @return The converted value
  225.      * @throws ConversionException if conversion cannot be performed successfully
  226.      */
  227.     public Object convert(final Object value) {
  228.         return convert(value, null);
  229.     }

  230.     /**
  231.      * Converts the specified locale-sensitive input object into an output object.
  232.      *
  233.      * @param value   The input object to be converted
  234.      * @param pattern The pattern is used for the conversion
  235.      * @return The converted value
  236.      * @throws ConversionException if conversion cannot be performed successfully
  237.      */
  238.     public T convert(final Object value, final String pattern) {
  239.         return convert(null, value, pattern);
  240.     }

  241.     /**
  242.      * Gets the default object specified for this converter cast for the given target type. If the default value is not conform to the given type, an exception
  243.      * is thrown.
  244.      *
  245.      * @param type the target class of the conversion
  246.      * @return the default value in the given target type
  247.      * @throws ConversionException if the default object is not compatible with the target type
  248.      */
  249.     private <R> R getDefaultAs(final Class<R> type) {
  250.         return checkConversionResult(type, defaultValue);
  251.     }

  252.     /**
  253.      * Converts the specified locale-sensitive input object into an output object of the specified type.
  254.      *
  255.      * @param value   The input object to be converted
  256.      * @param pattern The pattern is used for the conversion
  257.      * @return The converted value
  258.      * @throws ParseException if conversion cannot be performed successfully
  259.      */
  260.     protected abstract T parse(Object value, String pattern) throws ParseException;
  261. }