View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package javax.faces.convert;
20  
21  import javax.faces.component.UIComponent;
22  import javax.faces.context.FacesContext;
23  
24  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFConverter;
25  
26  import java.text.DecimalFormat;
27  import java.text.DecimalFormatSymbols;
28  import java.text.NumberFormat;
29  import java.text.ParseException;
30  import java.util.Locale;
31  
32  /**
33   * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
34   *
35   * @author Thomas Spiegl (latest modification by $Author: bommel $)
36   * @version $Revision: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct 2011) $
37   */
38  @JSFConverter
39  public class DoubleConverter
40          implements Converter
41  {
42      // API FIELDS
43      public static final String CONVERTER_ID = "javax.faces.Double";
44      public static final String STRING_ID = "javax.faces.converter.STRING";
45      public static final String DOUBLE_ID = "javax.faces.converter.DoubleConverter.DOUBLE";
46  
47      // CONSTRUCTORS
48      public DoubleConverter()
49      {
50      }
51  
52      // METHODS
53      public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
54      {
55          if (facesContext == null) throw new NullPointerException("facesContext");
56          if (uiComponent == null) throw new NullPointerException("uiComponent");
57  
58          if (value != null)
59          {
60              value = value.trim();
61              if (value.length() > 0)
62              {
63                  try
64                  {
65                      value = fixLocale(facesContext, value);
66                      return this.stringToDouble(value);
67                  }
68                  catch (NumberFormatException e)
69                  {
70                      throw new ConverterException(_MessageUtils.getErrorMessage(facesContext,
71                                                                                 DOUBLE_ID,
72                                                                                 new Object[]{value,"4214",_MessageUtils.getLabel(facesContext, uiComponent)}), e);
73                  }
74  
75              }
76          }
77          return null;
78      }
79  
80      /**
81       * Since Double.valueOf is not Locale aware, and NumberFormatter
82       * cannot parse E values correctly, we need to make a US Locale
83       * string from our input value.
84       * E.g. '34,383e3' will be translated to '34.383e3' if Locale.DE
85       * is set in the {@link javax.faces.component.UIViewRoot#getLocale()}
86       *
87       * @param facesContext
88       * @param value
89       * @return the 'fixed' value String
90       */
91      private String fixLocale(FacesContext facesContext, String value)
92      {
93          Locale loc = facesContext.getViewRoot().getLocale();
94          if (loc == null || loc == Locale.US)
95          {
96              // nothing to fix if we are already using the US Locale
97              return value;
98          }
99  
100         // TODO: DecimalFormatSymbols.getInstance exists only on JDK 1.6
101         // change it on JSF 2.1
102         //DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(loc);
103         DecimalFormatSymbols dfs = new DecimalFormatSymbols(loc);
104 
105         char decSep   = dfs.getDecimalSeparator();
106 
107 
108         // replace decimal separators which are different to '.'
109         if (decSep != '.' && value.lastIndexOf(decSep) >= 0)
110         {
111             StringBuffer sbVal = new StringBuffer();
112 
113             // remove all groupSeperators and change the decimalSeperator
114             for (int i = 0; i < value.length(); i++)
115             {
116                 if (value.charAt(i) == decSep)
117                 {
118                     sbVal.append('.'); // we append the Locale.US decimal separator
119                     continue;
120                 }
121 
122                 // just append all other characters as usual
123                 sbVal.append(value.charAt(i));
124             }
125 
126             value = sbVal.toString();
127         }
128 
129         // we need the formatter with the correct Locale of the user
130         return value;
131     }
132 
133     private Double stringToDouble(String value)
134     {
135         // this is a special hack for a jvm vulnerability with
136         // converting some special double values.
137         // e.g. "2.225073858507201200000e-308"
138         // see MYFACES-3024 for further information
139         // TODO we can remove this hack, once this got fixed in the jvm!
140         if (value.length() >= 23)
141         {
142             StringBuffer normalized = new StringBuffer();
143             for (int i=0; i< value.length(); i++)
144             {
145                 char c = value.charAt(i);
146                 if ( c != '.')
147                 {
148                     normalized.append(c);
149                 }
150             }
151 
152             String normalizedString = normalized.toString();
153             if (normalizedString.contains("22250738585072012") && normalizedString.contains("e-"))
154             {
155                 // oops, baaad value!
156                throw new NumberFormatException("Not Allowed! This value could possibly kill the VM!");
157             }
158         }
159 
160 
161         return Double.valueOf(value);
162     }
163 
164     public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value)
165     {
166         if (facesContext == null) throw new NullPointerException("facesContext");
167         if (uiComponent == null) throw new NullPointerException("uiComponent");
168 
169         if (value == null)
170         {
171             return "";
172         }
173         if (value instanceof String)
174         {
175             return (String)value;
176         }
177         try
178         {
179             return Double.toString(((Number)value).doubleValue());
180         }
181         catch (Exception e)
182         {
183             throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, STRING_ID, new Object[]{value,_MessageUtils.getLabel(facesContext, uiComponent)}),e);
184         }
185     }
186 }