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 * https://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
18 package org.apache.commons.beanutils2.locale.converters;
19
20 import java.text.DecimalFormat;
21 import java.text.NumberFormat;
22 import java.text.ParseException;
23 import java.util.Locale;
24
25 import org.apache.commons.beanutils2.ConversionException;
26 import org.apache.commons.beanutils2.locale.BaseLocaleConverter;
27 import org.apache.commons.beanutils2.locale.LocaleConverter;
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30
31 /**
32 * Standard {@link LocaleConverter} implementation that converts an incoming locale-sensitive String into a {@link Number} object, optionally using a default
33 * value or throwing a {@link ConversionException} if a conversion error occurs.
34 *
35 * @param <T> The converter type.
36 * @since 1.7
37 */
38 public class DecimalLocaleConverter<T extends Number> extends BaseLocaleConverter<T> {
39
40 /**
41 * Builds instances of {@link DateLocaleConverter}.
42 *
43 * @param <B> The builder type.
44 * @param <T> The Number type.
45 */
46 public static class Builder<B extends Builder<B, T>, T extends Number> extends BaseLocaleConverter.Builder<B, T> {
47
48 /**
49 * Constructs a new instance.
50 */
51 public Builder() {
52 // empty
53 }
54
55 /**
56 * Constructs a new instance.
57 * <p>
58 * By default, construct a {@link LocaleConverter} that will throw a {@link ConversionException} if a conversion error occurs. The locale is the default
59 * locale for this instance of the Java Virtual Machine and an unlocalized pattern is used for the conversion.
60 * </p>
61 *
62 * @return a new instance.
63 */
64 @Override
65 public DecimalLocaleConverter<?> get() {
66 return new DecimalLocaleConverter<>(defaultValue, locale, pattern, useDefault || defaultValue != null, localizedPattern);
67 }
68
69 }
70
71 /** All logging goes through this logger */
72 private static final Log LOG = LogFactory.getLog(DecimalLocaleConverter.class);
73
74 /**
75 * Constructs a new builder.
76 *
77 * @param <B> The builder type.
78 * @param <T> The Number type.
79 * @return a new builder.
80 */
81 @SuppressWarnings("unchecked")
82 public static <B extends Builder<B, T>, T extends Number> B builder() {
83 return (B) new Builder<>();
84 }
85
86 /**
87 * Constructs a new instance.
88 *
89 * @param defaultValue default value.
90 * @param locale locale.
91 * @param pattern pattern.
92 * @param useDefault use the default.
93 * @param locPattern localized pattern.
94 */
95 protected DecimalLocaleConverter(final T defaultValue, final Locale locale, final String pattern, final boolean useDefault, final boolean locPattern) {
96 super(defaultValue, locale, pattern, useDefault, locPattern);
97 }
98
99 /**
100 * Converts the specified locale-sensitive input object into an output object of the specified type.
101 *
102 * @param value The input object to be converted
103 * @param pattern The pattern is used for the conversion
104 * @return The converted value
105 * @throws ConversionException if conversion cannot be performed successfully
106 * @throws ParseException if an error occurs parsing a String to a Number
107 */
108 @Override
109 protected T parse(final Object value, final String pattern) throws ParseException {
110 if (value instanceof Number) {
111 return (T) value;
112 }
113
114 // Note that despite the ambiguous "getInstance" name, and despite the
115 // fact that objects returned from this method have the same toString
116 // representation, each call to getInstance actually returns a new
117 // object.
118 final DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(locale);
119
120 // if some constructors default pattern to null, it makes only sense
121 // to handle null pattern gracefully
122 if (pattern != null) {
123 if (localizedPattern) {
124 formatter.applyLocalizedPattern(pattern);
125 } else {
126 formatter.applyPattern(pattern);
127 }
128 } else {
129 LOG.debug("No pattern provided, using default.");
130 }
131
132 return (T) formatter.parse((String) value);
133 }
134 }