Coverage Report - org.apache.tapestry5.internal.translator.NumericTranslatorSupportImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
NumericTranslatorSupportImpl
98%
59/60
95%
21/22
0
 
 1  
 // Copyright 2009 The Apache Software Foundation
 2  
 //
 3  
 // Licensed under the Apache License, Version 2.0 (the "License");
 4  
 // you may not use this file except in compliance with the License.
 5  
 // You may obtain a copy of the License at
 6  
 //
 7  
 //     http://www.apache.org/licenses/LICENSE-2.0
 8  
 //
 9  
 // Unless required by applicable law or agreed to in writing, software
 10  
 // distributed under the License is distributed on an "AS IS" BASIS,
 11  
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
 // See the License for the specific language governing permissions and
 13  
 // limitations under the License.
 14  
 
 15  
 package org.apache.tapestry5.internal.translator;
 16  
 
 17  
 import org.apache.tapestry5.Field;
 18  
 import org.apache.tapestry5.RenderSupport;
 19  
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 20  
 import org.apache.tapestry5.ioc.services.ThreadLocale;
 21  
 import org.apache.tapestry5.ioc.services.TypeCoercer;
 22  
 import org.apache.tapestry5.json.JSONObject;
 23  
 import org.apache.tapestry5.services.ClientBehaviorSupport;
 24  
 import org.apache.tapestry5.services.Request;
 25  
 
 26  
 import java.math.BigDecimal;
 27  
 import java.math.BigInteger;
 28  
 import java.text.DecimalFormat;
 29  
 import java.text.DecimalFormatSymbols;
 30  
 import java.text.NumberFormat;
 31  
 import java.text.ParseException;
 32  
 import java.util.Locale;
 33  
 import java.util.Map;
 34  
 import java.util.Set;
 35  
 
 36  
 public class NumericTranslatorSupportImpl implements NumericTranslatorSupport
 37  
 {
 38  
     private final TypeCoercer typeCoercer;
 39  
 
 40  
     private final ThreadLocale threadLocale;
 41  
 
 42  
     private final Request request;
 43  
 
 44  
     private final RenderSupport renderSupport;
 45  
 
 46  
     private final ClientBehaviorSupport clientBehaviorSupport;
 47  
 
 48  18
     private final Map<Locale, DecimalFormatSymbols> symbolsCache = CollectionFactory.newConcurrentMap();
 49  
 
 50  18
     private final Set<Class> integerTypes = CollectionFactory.newSet();
 51  
 
 52  
     private static final String DECIMAL_FORMAT_SYMBOLS_PROVIDED = "tapestry.decimal-format-symbols-provided";
 53  
 
 54  
     public NumericTranslatorSupportImpl(TypeCoercer typeCoercer, ThreadLocale threadLocale, Request request,
 55  
                                         RenderSupport renderSupport, ClientBehaviorSupport clientBehaviorSupport)
 56  18
     {
 57  18
         this.typeCoercer = typeCoercer;
 58  18
         this.threadLocale = threadLocale;
 59  18
         this.request = request;
 60  18
         this.renderSupport = renderSupport;
 61  18
         this.clientBehaviorSupport = clientBehaviorSupport;
 62  
 
 63  18
         Class[] integerTypes = {
 64  
                 Byte.class, Short.class, Integer.class, Long.class, BigInteger.class
 65  
         };
 66  
 
 67  108
         for (Class c : integerTypes)
 68  90
             this.integerTypes.add(c);
 69  
 
 70  18
     }
 71  
 
 72  
     public <T extends Number> void addValidation(Class<T> type, Field field, String message)
 73  
     {
 74  72
         if (request.getAttribute(DECIMAL_FORMAT_SYMBOLS_PROVIDED) == null)
 75  
         {
 76  54
             renderSupport.addScript("Tapestry.decimalFormatSymbols = %s;", createJSONDecimalFormatSymbols());
 77  
 
 78  54
             request.setAttribute(DECIMAL_FORMAT_SYMBOLS_PROVIDED, true);
 79  
         }
 80  
 
 81  72
         clientBehaviorSupport.addValidation(field, "numericformat", message, isIntegerType(type));
 82  72
     }
 83  
 
 84  
     private JSONObject createJSONDecimalFormatSymbols()
 85  
     {
 86  54
         Locale locale = threadLocale.getLocale();
 87  
 
 88  54
         DecimalFormatSymbols symbols = getSymbols(locale);
 89  
 
 90  54
         JSONObject result = new JSONObject();
 91  
 
 92  54
         result.put("groupingSeparator", toString(symbols.getGroupingSeparator()));
 93  54
         result.put("minusSign", toString(symbols.getMinusSign()));
 94  54
         result.put("decimalSeparator", toString(symbols.getDecimalSeparator()));
 95  
 
 96  54
         return result;
 97  
     }
 98  
 
 99  
     private DecimalFormatSymbols getSymbols(Locale locale)
 100  
     {
 101  216
         DecimalFormatSymbols symbols = symbolsCache.get(locale);
 102  
 
 103  216
         if (symbols == null)
 104  
         {
 105  6
             symbols = new DecimalFormatSymbols(locale);
 106  6
             symbolsCache.put(locale, symbols);
 107  
         }
 108  
 
 109  216
         return symbols;
 110  
     }
 111  
 
 112  
     private boolean isIntegerType(Class type)
 113  
     {
 114  370
         return integerTypes.contains(type);
 115  
     }
 116  
 
 117  
     public <T extends Number> T parseClient(Class<T> type, String clientValue) throws ParseException
 118  
     {
 119  74
         NumericFormatter formatter = getParseFormatter(type);
 120  
 
 121  74
         Number number = formatter.parse(clientValue.trim());
 122  
 
 123  60
         return typeCoercer.coerce(number, type);
 124  
     }
 125  
 
 126  
     private NumericFormatter getParseFormatter(Class type)
 127  
     {
 128  74
         Locale locale = threadLocale.getLocale();
 129  74
         DecimalFormatSymbols symbols = getSymbols(locale);
 130  
 
 131  74
         if (type.equals(BigInteger.class))
 132  2
             return new BigIntegerNumericFormatter(symbols);
 133  
 
 134  72
         if (type.equals(BigDecimal.class))
 135  2
             return new BigDecimalNumericFormatter(symbols);
 136  
 
 137  
         // We don't cache NumberFormat instances because they are not thread safe.
 138  
         // Perhaps we should turn this service into a perthread so that we can cache
 139  
         // (for the duration of a request)?
 140  
 
 141  
         // We don't cache the rest of these, because they are built on DecimalFormat which is
 142  
         // not thread safe.
 143  
 
 144  70
         if (isIntegerType(type))
 145  
         {
 146  56
             NumberFormat format = NumberFormat.getIntegerInstance(locale);
 147  56
             return new NumericFormatterImpl(format);
 148  
         }
 149  
 
 150  14
         DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(locale);
 151  
 
 152  14
         if (type.equals(BigDecimal.class))
 153  0
             df.setParseBigDecimal(true);
 154  
 
 155  14
         return new NumericFormatterImpl(df);
 156  
     }
 157  
 
 158  
     private NumericFormatter getOutputFormatter(Class type)
 159  
     {
 160  88
         Locale locale = threadLocale.getLocale();
 161  
 
 162  88
         DecimalFormatSymbols symbols = getSymbols(locale);
 163  
 
 164  88
         if (type.equals(BigInteger.class))
 165  2
             return new BigIntegerNumericFormatter(symbols);
 166  
 
 167  86
         if (type.equals(BigDecimal.class))
 168  2
             return new BigDecimalNumericFormatter(symbols);
 169  
 
 170  
 
 171  
         // We don't cache the rest of these, because they are built on DecimalFormat which is
 172  
         // not thread safe.
 173  
 
 174  84
         if (!isIntegerType(type))
 175  
         {
 176  22
             NumberFormat format = NumberFormat.getNumberInstance(locale);
 177  
 
 178  22
             return new NumericFormatterImpl(format);
 179  
         }
 180  
 
 181  62
         DecimalFormat df = new DecimalFormat(toString(symbols.getZeroDigit()), symbols);
 182  
 
 183  62
         return new NumericFormatterImpl(df);
 184  
     }
 185  
 
 186  
     public <T extends Number> String toClient(Class<T> type, T value)
 187  
     {
 188  88
         return getOutputFormatter(type).toClient(value);
 189  
     }
 190  
 
 191  
     public <T extends Number> String getMessageKey(Class<T> type)
 192  
     {
 193  144
         return isIntegerType(type)
 194  
                ? "integer-format-exception"
 195  
                : "number-format-exception";
 196  
     }
 197  
 
 198  
     private static String toString(char ch)
 199  
     {
 200  224
         return String.valueOf(ch);
 201  
     }
 202  
 }