You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2010/11/13 22:02:14 UTC
svn commit: r1034872 - in
/myfaces/commons/trunk/myfaces-commons-converters/src/main/java/org/apache/myfaces/commons/converter:
AbstractTypedNumberConverter.java TypedNumberConverter.java
Author: lu4242
Date: Sat Nov 13 21:02:13 2010
New Revision: 1034872
URL: http://svn.apache.org/viewvc?rev=1034872&view=rev
Log:
TOMAHAWK-1010 Default JSF Converters evaluate value-bindings only at converter creation time, MFCOMMONS-12 Add ConverterBase class to allow create converters that evaluate EL expressions at render time
Added:
myfaces/commons/trunk/myfaces-commons-converters/src/main/java/org/apache/myfaces/commons/converter/AbstractTypedNumberConverter.java
Removed:
myfaces/commons/trunk/myfaces-commons-converters/src/main/java/org/apache/myfaces/commons/converter/TypedNumberConverter.java
Added: myfaces/commons/trunk/myfaces-commons-converters/src/main/java/org/apache/myfaces/commons/converter/AbstractTypedNumberConverter.java
URL: http://svn.apache.org/viewvc/myfaces/commons/trunk/myfaces-commons-converters/src/main/java/org/apache/myfaces/commons/converter/AbstractTypedNumberConverter.java?rev=1034872&view=auto
==============================================================================
--- myfaces/commons/trunk/myfaces-commons-converters/src/main/java/org/apache/myfaces/commons/converter/AbstractTypedNumberConverter.java (added)
+++ myfaces/commons/trunk/myfaces-commons-converters/src/main/java/org/apache/myfaces/commons/converter/AbstractTypedNumberConverter.java Sat Nov 13 21:02:13 2010
@@ -0,0 +1,614 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.myfaces.commons.converter;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Currency;
+import java.util.Locale;
+
+import javax.el.ValueExpression;
+import javax.faces.FacesException;
+import javax.faces.application.FacesMessage;
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.ConverterException;
+
+import org.apache.commons.beanutils.ConvertUtils;
+import org.apache.commons.beanutils.Converter;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFConverter;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
+import org.apache.myfaces.commons.util.MessageUtils;
+
+/**
+ * Converter which uses either the manually set <code>destType</code> or the value binding to determine the
+ * correct destination type to convert the number to
+ *
+ * This tag creates a number formatting converter and associates it with the nearest
+ * parent UIComponent. It uses either the manually set destType or the value
+ * binding to determine the correct destination type to convert the number to.
+ *
+ * Unless otherwise specified, all attributes accept static values or EL expressions.
+ *
+ *
+ * @author imario@apache.org
+ */
+@JSFConverter(
+ name = "mcc:convertNumber",
+ clazz = "org.apache.myfaces.commons.converter.TypedNumberConverter",
+ tagClass = "org.apache.myfaces.commons.converter.TypedNumberConverterTag",
+ serialuidtag = "-6592309048440572608L")
+public abstract class AbstractTypedNumberConverter extends ConverterBase
+{
+ public static final String CONVERTER_ID = "org.apache.myfaces.custom.convertNumber.TypedNumberConverter";
+
+ private Class destType;
+
+ public AbstractTypedNumberConverter()
+ {
+ }
+
+ public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
+ {
+ Object convertedValue = _getAsObject(facesContext, uiComponent, value);
+ if (convertedValue == null)
+ {
+ return null;
+ }
+
+ Class destType = getDestType();
+ if (destType == null)
+ {
+ ValueExpression valueBinding = uiComponent.getValueExpression("value");
+ if (valueBinding != null)
+ {
+ destType = valueBinding.getType(facesContext.getELContext());
+ }
+ }
+
+ if (destType != null)
+ {
+ Converter converter = ConvertUtils.lookup(destType);
+ if (converter == null)
+ {
+ throw new UnsupportedOperationException("cant deal with " + destType);
+ }
+
+ // setting type to null, in fact the documentation is wrong here and this type is never used
+ convertedValue = converter.convert(null, convertedValue);
+ }
+
+
+ return convertedValue;
+ }
+
+ public void restoreState(FacesContext facesContext, Object state)
+ {
+ Object[] states = (Object[]) state;
+ _restoreState(facesContext, states[0]);
+ destType = (Class) states[1];
+ }
+
+ public Object saveState(FacesContext facesContext)
+ {
+ return new Object[]
+ {
+ _saveState(facesContext),
+ destType
+ };
+ }
+
+ /**
+ * The java class name the value should be converted to.
+ *
+ * Default: automatically determined through valueBinding
+ *
+ */
+ @JSFProperty
+ public Class getDestType()
+ {
+ return destType;
+ }
+
+ public void setDestType(Class destType)
+ {
+ this.destType = destType;
+ }
+
+ /* ORIGINAL STUFF COPIED FROM javax.faces.convert.NumberConverter */
+
+ // internal constants
+ // private static final String CONVERSION_MESSAGE_ID = "javax.faces.convert.NumberConverter.CONVERSION";
+ public static final String STRING_ID = "javax.faces.converter.STRING";
+ public static final String CURRENCY_ID = "javax.faces.converter.NumberConverter.CURRENCY";
+ public static final String NUMBER_ID = "javax.faces.converter.NumberConverter.NUMBER";
+ public static final String PATTERN_ID = "javax.faces.converter.NumberConverter.PATTERN";
+ public static final String PERCENT_ID = "javax.faces.converter.NumberConverter.PERCENT";
+
+ private static final boolean JAVA_VERSION_14;
+
+ static
+ {
+ JAVA_VERSION_14 = checkJavaVersion14();
+ }
+
+ private String _currencyCode;
+ private String _currencySymbol;
+ private Locale _locale;
+ private boolean _transient;
+
+
+
+ // METHODS
+ public Object _getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
+ {
+ if (facesContext == null) throw new NullPointerException("facesContext");
+ if (uiComponent == null) throw new NullPointerException("uiComponent");
+
+ if (value != null)
+ {
+ value = value.trim();
+ if (value.length() > 0)
+ {
+ NumberFormat format = getNumberFormat(facesContext);
+ format.setParseIntegerOnly(isIntegerOnly());
+
+ DecimalFormat df = (DecimalFormat)format;
+
+ // The best we can do in this case is check if there is a ValueExpression
+ // with a BigDecimal as returning type , and if that so enable BigDecimal parsing
+ // to prevent loss in precision, and do not break existing examples (since
+ // in those cases it is expected to return Double). See MYFACES-1890 and TRINIDAD-1124
+ // for details
+ Class destType = getDestType();
+ if (destType == null)
+ {
+ ValueExpression valueBinding = uiComponent.getValueExpression("value");
+ if (valueBinding != null)
+ {
+ destType = valueBinding.getType(facesContext.getELContext());
+ }
+ }
+ if (destType != null && BigDecimal.class.isAssignableFrom(destType))
+ {
+ df.setParseBigDecimal(true);
+ }
+
+ DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
+ boolean changed = false;
+ if(dfs.getGroupingSeparator() == '\u00a0')
+ {
+ dfs.setGroupingSeparator(' ');
+ df.setDecimalFormatSymbols(dfs);
+ changed = true;
+ }
+
+ formatCurrency(format);
+
+ try
+ {
+ return format.parse(value);
+ }
+ catch (ParseException e)
+ {
+ if(changed)
+ {
+ dfs.setGroupingSeparator('\u00a0');
+ df.setDecimalFormatSymbols(dfs);
+ }
+ try
+ {
+ return format.parse(value);
+ }
+ catch (ParseException pe)
+ {
+
+ if(getPattern() != null)
+ throw new ConverterException(MessageUtils.getMessage(FacesMessage.FACES_MESSAGES, FacesMessage.SEVERITY_ERROR,
+ PATTERN_ID,
+ new Object[]{value,"$###,###",MessageUtils.getLabel(facesContext, uiComponent)},facesContext));
+ else if(getType().equals("number"))
+ throw new ConverterException(MessageUtils.getMessage(FacesMessage.FACES_MESSAGES, FacesMessage.SEVERITY_ERROR,
+ NUMBER_ID,
+ new Object[]{value,format.format(21),MessageUtils.getLabel(facesContext, uiComponent)},facesContext));
+ else if(getType().equals("currency"))
+ throw new ConverterException(MessageUtils.getMessage(FacesMessage.FACES_MESSAGES, FacesMessage.SEVERITY_ERROR,
+ CURRENCY_ID,
+ new Object[]{value,format.format(42.25),MessageUtils.getLabel(facesContext, uiComponent)},facesContext));
+ else if(getType().equals("percent"))
+ throw new ConverterException(MessageUtils.getMessage(FacesMessage.FACES_MESSAGES, FacesMessage.SEVERITY_ERROR,
+ PERCENT_ID,
+ new Object[]{value,format.format(.90),MessageUtils.getLabel(facesContext, uiComponent)},facesContext));
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value)
+ {
+ if (facesContext == null) throw new NullPointerException("facesContext");
+ if (uiComponent == null) throw new NullPointerException("uiComponent");
+
+ if (value == null)
+ {
+ return "";
+ }
+ if (value instanceof String)
+ {
+ return (String)value;
+ }
+
+ NumberFormat format = getNumberFormat(facesContext);
+ format.setGroupingUsed(isGroupingUsed());
+ Integer maxFractionDigits = getMaxFractionDigits();
+ Integer maxIntegerDigits = getMaxIntegerDigits();
+ Integer minFractionDigits = getMinFractionDigits();
+ Integer minIntegerDigits = getMinIntegerDigits();
+ if (maxFractionDigits != null) format.setMaximumFractionDigits(maxFractionDigits);
+ if (maxIntegerDigits != null) format.setMaximumIntegerDigits(maxIntegerDigits);
+ if (minFractionDigits != null) format.setMinimumFractionDigits(minFractionDigits);
+ if (minIntegerDigits != null) format.setMinimumIntegerDigits(minIntegerDigits);
+ formatCurrency(format);
+ try
+ {
+ return format.format(value);
+ }
+ catch (Exception e)
+ {
+ throw new ConverterException(MessageUtils.getMessage(FacesMessage.FACES_MESSAGES, FacesMessage.SEVERITY_ERROR, STRING_ID, new Object[]{value,MessageUtils.getLabel(facesContext, uiComponent)}, facesContext),e);
+ }
+ }
+
+ private NumberFormat getNumberFormat(FacesContext facesContext)
+ {
+ Locale lokale = getLocale();
+
+ if (getPattern() == null && getType() == null)
+ {
+ throw new ConverterException("Cannot get NumberFormat, either type or pattern needed.");
+ }
+
+ // pattern
+ if (getPattern() != null)
+ {
+ return new DecimalFormat(getPattern(), new DecimalFormatSymbols(lokale));
+ }
+
+ // type
+ if (getType().equals("number"))
+ {
+ return NumberFormat.getNumberInstance(lokale);
+ }
+ else if (getType().equals("currency"))
+ {
+ return NumberFormat.getCurrencyInstance(lokale);
+ }
+ else if (getType().equals("percent"))
+ {
+ return NumberFormat.getPercentInstance(lokale);
+ }
+ throw new ConverterException("Cannot get NumberFormat, illegal type " + getType());
+ }
+
+ private void formatCurrency(NumberFormat format)
+ {
+ if (getLocalCurrencyCode() == null && getLocalCurrencySymbol() == null)
+ {
+ return;
+ }
+
+ boolean useCurrencyCode;
+ if (JAVA_VERSION_14)
+ {
+ useCurrencyCode = getLocalCurrencyCode() != null;
+ }
+ else
+ {
+ useCurrencyCode = getLocalCurrencySymbol() == null;
+ }
+
+ if (useCurrencyCode)
+ {
+ // set Currency
+ try
+ {
+ format.setCurrency(Currency.getInstance(getLocalCurrencyCode()));
+ }
+ catch (Exception e)
+ {
+ throw new ConverterException("Unable to get Currency instance for currencyCode " +
+ getLocalCurrencyCode());
+ }
+ }
+ else if (format instanceof DecimalFormat)
+
+ {
+ DecimalFormat dFormat = (DecimalFormat)format;
+ DecimalFormatSymbols symbols = dFormat.getDecimalFormatSymbols();
+ symbols.setCurrencySymbol(getLocalCurrencySymbol());
+ dFormat.setDecimalFormatSymbols(symbols);
+ }
+ }
+
+ // STATE SAVE/RESTORE
+ public void _restoreState(FacesContext facesContext, Object state)
+ {
+ Object values[] = (Object[])state;
+ super.restoreState(facesContext, values[0]);
+ _currencyCode = (String)values[1];
+ _currencySymbol = (String)values[2];
+ _locale = (Locale)values[3];
+ }
+
+ public Object _saveState(FacesContext facesContext)
+ {
+ Object values[] = new Object[4];
+ values[0] = super.saveState(facesContext);
+ values[1] = _currencyCode;
+ values[2] = _currencySymbol;
+ values[3] = _locale;
+ return values;
+ }
+
+ // GETTER & SETTER
+
+ /**
+ * ISO 4217 currency code
+ *
+ */
+ @JSFProperty
+ public String getCurrencyCode()
+ {
+ if (_currencyCode != null)
+ {
+ return _currencyCode;
+ }
+ ValueExpression vb = getValueExpression("currencyCode");
+ if (vb != null)
+ {
+ return (String) vb.getValue(getFacesContext().getELContext());
+ }
+ return getDecimalFormatSymbols().getInternationalCurrencySymbol();
+ }
+
+ protected String getLocalCurrencyCode()
+ {
+ if (_currencyCode != null)
+ {
+ return _currencyCode;
+ }
+ ValueExpression vb = getValueExpression("currencyCode");
+ if (vb != null)
+ {
+ return (String) vb.getValue(getFacesContext().getELContext());
+ }
+ return null;
+ }
+
+ public void setCurrencyCode(String currencyCode)
+ {
+ this._currencyCode = currencyCode;
+ }
+
+ /**
+ * The currency symbol used to format a currency value.
+ *
+ * Defaults to the currency symbol for locale.
+ *
+ */
+ @JSFProperty
+ public String getCurrencySymbol()
+ {
+ if (_currencySymbol != null)
+ {
+ return _currencySymbol;
+ }
+ ValueExpression vb = getValueExpression("currencySymbol");
+ if (vb != null)
+ {
+ return (String) vb.getValue(getFacesContext().getELContext());
+ }
+ return getDecimalFormatSymbols().getCurrencySymbol();
+ }
+
+ public String getLocalCurrencySymbol()
+ {
+ if (_currencySymbol != null)
+ {
+ return _currencySymbol;
+ }
+ ValueExpression vb = getValueExpression("currencySymbol");
+ if (vb != null)
+ {
+ return (String) vb.getValue(getFacesContext().getELContext());
+ }
+ return null;
+ }
+
+ public void setCurrencySymbol(String currencySymbol)
+ {
+ this._currencySymbol = currencySymbol;
+ }
+
+ /**
+ * Specifies whether output will contain grouping separators.
+ *
+ * Default: true.
+ *
+ */
+ @JSFProperty(defaultValue="true")
+ public abstract boolean isGroupingUsed();
+
+ /**
+ * Specifies whether only the integer part of the input will be parsed.
+ *
+ * Default: false.
+ *
+ */
+ @JSFProperty(defaultValue="false")
+ public abstract boolean isIntegerOnly();
+
+ /**
+ * The name of the locale to be used, instead of the default as specified
+ * in the faces configuration file.
+ *
+ */
+ @JSFProperty(deferredValueType="java.lang.Object")
+ public Locale getLocale()
+ {
+ if (_locale != null)
+ {
+ return _locale;
+ }
+ ValueExpression vb = getValueExpression("locale");
+ if (vb != null)
+ {
+ Object _localeValue = vb.getValue(getFacesContext().getELContext());
+ if (_localeValue instanceof String)
+ {
+ _localeValue = org.apache.myfaces.commons.util.TagUtils.getLocale((String)_localeValue);
+ }
+ return (java.util.Locale)_localeValue;
+ }
+ FacesContext context = FacesContext.getCurrentInstance();
+ return context.getViewRoot().getLocale();
+ }
+
+ public void setLocale(Locale locale)
+ {
+ _locale = locale;
+ }
+
+ /**
+ * The maximum number of digits in the fractional portion of the number.
+ *
+ */
+ @JSFProperty
+ public abstract Integer getMaxFractionDigits();
+
+ /**
+ * The maximum number of digits in the integer portion of the number.
+ *
+ */
+ @JSFProperty
+ public abstract Integer getMaxIntegerDigits();
+
+ /**
+ * The minimum number of digits in the fractional portion of the number.
+ *
+ */
+ @JSFProperty
+ public abstract Integer getMinFractionDigits();
+
+ /**
+ * The minimum number of digits in the integer portion of the number.
+ *
+ */
+ @JSFProperty
+ public abstract Integer getMinIntegerDigits();
+
+ /**
+ * A custom Date formatting pattern, in the format used by java.text.SimpleDateFormat.
+ *
+ */
+ @JSFProperty
+ public abstract String getPattern();
+
+ public boolean isTransient()
+ {
+ return _transient;
+ }
+
+ public void setTransient(boolean aTransient)
+ {
+ _transient = aTransient;
+ }
+
+ /**
+ * The type of formatting/parsing to be performed.
+ *
+ * Values include: number, currency, and percentage. Default: number.
+ *
+ */
+ @JSFProperty(defaultValue="number")
+ public abstract String getType();
+
+ private static boolean checkJavaVersion14()
+ {
+ String version = System.getProperty("java.version");
+ if (version == null)
+ {
+ return false;
+ }
+ byte java14 = 0;
+ for (int idx = version.indexOf('.'), i = 0; idx > 0 || version != null; i++)
+ {
+ if (idx > 0)
+ {
+ byte value = Byte.parseByte(version.substring(0, 1));
+ version = version.substring(idx + 1, version.length());
+ idx = version.indexOf('.');
+ switch (i)
+ {
+ case 0:
+ if (value == 1)
+ {
+ java14 = 1;
+ break;
+ }
+ else if (value > 1)
+ {
+ java14 = 2;
+ }
+ case 1:
+ if (java14 > 0 && value >= 4)
+ {
+ java14 = 2;
+ }
+ ;
+ default:
+ idx = 0;
+ version = null;
+ break;
+ }
+ }
+ else
+ {
+ byte value = Byte.parseByte(version.substring(0, 1));
+ if (java14 > 0 && value >= 4)
+ {
+ java14 = 2;
+ }
+ break;
+ }
+ }
+ return java14 == 2;
+ }
+
+
+ private DecimalFormatSymbols getDecimalFormatSymbols()
+ {
+ return new DecimalFormatSymbols(getLocale());
+ }
+}