org.apache.tapestry5.internal.translator.NumericTranslatorSupportImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tapestry-core Show documentation
Show all versions of tapestry-core Show documentation
Central module for Tapestry, containing interfaces to the Java
Servlet API and all core services and components.
// Copyright 2009, 2010 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry5.internal.translator;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.tapestry5.Field;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.services.ThreadLocale;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.ClientBehaviorSupport;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.javascript.InitializationPriority;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
public class NumericTranslatorSupportImpl implements NumericTranslatorSupport
{
private final TypeCoercer typeCoercer;
private final ThreadLocale threadLocale;
private final Request request;
private final JavaScriptSupport javascriptSupport;
private final ClientBehaviorSupport clientBehaviorSupport;
private final boolean compactJSON;
private final Map symbolsCache = CollectionFactory.newConcurrentMap();
private final Set integerTypes = CollectionFactory.newSet();
private static final String DECIMAL_FORMAT_SYMBOLS_PROVIDED = "tapestry.decimal-format-symbols-provided";
public NumericTranslatorSupportImpl(TypeCoercer typeCoercer, ThreadLocale threadLocale, Request request,
JavaScriptSupport javascriptSupport, ClientBehaviorSupport clientBehaviorSupport,
@Symbol(SymbolConstants.COMPACT_JSON)
boolean compactJSON)
{
this.typeCoercer = typeCoercer;
this.threadLocale = threadLocale;
this.request = request;
this.javascriptSupport = javascriptSupport;
this.clientBehaviorSupport = clientBehaviorSupport;
this.compactJSON = compactJSON;
Class[] integerTypes =
{ Byte.class, Short.class, Integer.class, Long.class, BigInteger.class };
for (Class c : integerTypes)
this.integerTypes.add(c);
}
public void addValidation(Class type, Field field, String message)
{
if (request.getAttribute(DECIMAL_FORMAT_SYMBOLS_PROVIDED) == null)
{
javascriptSupport.addScript(InitializationPriority.IMMEDIATE, "Tapestry.decimalFormatSymbols = %s;",
createJSONDecimalFormatSymbols().toString(compactJSON));
request.setAttribute(DECIMAL_FORMAT_SYMBOLS_PROVIDED, true);
}
clientBehaviorSupport.addValidation(field, "numericformat", message, isIntegerType(type));
}
private JSONObject createJSONDecimalFormatSymbols()
{
Locale locale = threadLocale.getLocale();
DecimalFormatSymbols symbols = getSymbols(locale);
JSONObject result = new JSONObject();
result.put("groupingSeparator", toString(symbols.getGroupingSeparator()));
result.put("minusSign", toString(symbols.getMinusSign()));
result.put("decimalSeparator", toString(symbols.getDecimalSeparator()));
return result;
}
private DecimalFormatSymbols getSymbols(Locale locale)
{
DecimalFormatSymbols symbols = symbolsCache.get(locale);
if (symbols == null)
{
symbols = new DecimalFormatSymbols(locale);
symbolsCache.put(locale, symbols);
}
return symbols;
}
private boolean isIntegerType(Class type)
{
return integerTypes.contains(type);
}
public T parseClient(Class type, String clientValue) throws ParseException
{
NumericFormatter formatter = getParseFormatter(type);
Number number = formatter.parse(clientValue.trim());
return typeCoercer.coerce(number, type);
}
private NumericFormatter getParseFormatter(Class type)
{
Locale locale = threadLocale.getLocale();
DecimalFormatSymbols symbols = getSymbols(locale);
if (type.equals(BigInteger.class))
return new BigIntegerNumericFormatter(symbols);
if (type.equals(BigDecimal.class))
return new BigDecimalNumericFormatter(symbols);
// We don't cache NumberFormat instances because they are not thread safe.
// Perhaps we should turn this service into a perthread so that we can cache
// (for the duration of a request)?
// We don't cache the rest of these, because they are built on DecimalFormat which is
// not thread safe.
if (isIntegerType(type))
{
NumberFormat format = NumberFormat.getIntegerInstance(locale);
return new NumericFormatterImpl(format);
}
DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(locale);
if (type.equals(BigDecimal.class))
df.setParseBigDecimal(true);
return new NumericFormatterImpl(df);
}
private NumericFormatter getOutputFormatter(Class type)
{
Locale locale = threadLocale.getLocale();
DecimalFormatSymbols symbols = getSymbols(locale);
if (type.equals(BigInteger.class))
return new BigIntegerNumericFormatter(symbols);
if (type.equals(BigDecimal.class))
return new BigDecimalNumericFormatter(symbols);
// We don't cache the rest of these, because they are built on DecimalFormat which is
// not thread safe.
if (!isIntegerType(type))
{
NumberFormat format = NumberFormat.getNumberInstance(locale);
return new NumericFormatterImpl(format);
}
DecimalFormat df = new DecimalFormat(toString(symbols.getZeroDigit()), symbols);
return new NumericFormatterImpl(df);
}
public String toClient(Class type, T value)
{
return getOutputFormatter(type).toClient(value);
}
public String getMessageKey(Class type)
{
return isIntegerType(type) ? "integer-format-exception" : "number-format-exception";
}
private static String toString(char ch)
{
return String.valueOf(ch);
}
}