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.DecimalFormatSymbols;
27  import java.util.Locale;
28  
29  /**
30   * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
31   *
32   * @author Thomas Spiegl (latest modification by $Author: struberg $)
33   * @version $Revision: 1188598 $ $Date: 2011-10-25 05:46:21 -0500 (Tue, 25 Oct 2011) $
34   */
35  @JSFConverter
36  public class DoubleConverter
37          implements Converter
38  {
39      // API FIELDS
40      public static final String CONVERTER_ID = "javax.faces.Double";
41      public static final String STRING_ID = "javax.faces.converter.STRING";
42      public static final String DOUBLE_ID = "javax.faces.converter.DoubleConverter.DOUBLE";
43  
44      // CONSTRUCTORS
45      public DoubleConverter()
46      {
47      }
48  
49      // METHODS
50      public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
51      {
52          if (facesContext == null)
53          {
54              throw new NullPointerException("facesContext");
55          }
56          if (uiComponent == null)
57          {
58              throw new NullPointerException("uiComponent");
59          }
60  
61          if (value != null)
62          {
63              value = value.trim();
64              if (value.length() > 0)
65              {
66                  try
67                  {
68                      value = fixLocale(facesContext, value);
69                      return this.stringToDouble(value);
70                  }
71                  catch (NumberFormatException e)
72                  {
73                      throw new ConverterException(_MessageUtils.getErrorMessage(facesContext,
74                                     DOUBLE_ID,
75                                     new Object[]{value,"4214",_MessageUtils.getLabel(facesContext, uiComponent)}), e);
76                  }
77  
78              }
79          }
80          return null;
81      }
82  
83      /**
84       * Since Double.valueOf is not Locale aware, and NumberFormatter
85       * cannot parse E values correctly, we need to make a US Locale
86       * string from our input value.
87       * E.g. '34,383e3' will be translated to '34.383e3' if Locale.DE
88       * is set in the {@link javax.faces.component.UIViewRoot#getLocale()}
89       *
90       * @param facesContext
91       * @param value
92       * @return the 'fixed' value String
93       */
94      private String fixLocale(FacesContext facesContext, String value)
95      {
96          Locale loc = facesContext.getViewRoot().getLocale();
97          if (loc == null || loc == Locale.US)
98          {
99              // nothing to fix if we are already using the US Locale
100             return value;
101         }
102 
103         // TODO: DecimalFormatSymbols.getInstance exists only on JDK 1.6
104         // change it on JSF 2.1
105         //DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(loc);
106         DecimalFormatSymbols dfs = new DecimalFormatSymbols(loc);
107 
108         char decSep   = dfs.getDecimalSeparator();
109 
110 
111         // replace decimal separators which are different to '.'
112         if (decSep != '.' && value.lastIndexOf(decSep) >= 0)
113         {
114             StringBuffer sbVal = new StringBuffer();
115 
116             // remove all groupSeperators and change the decimalSeperator
117             for (int i = 0; i < value.length(); i++)
118             {
119                 if (value.charAt(i) == decSep)
120                 {
121                     sbVal.append('.'); // we append the Locale.US decimal separator
122                     continue;
123                 }
124 
125                 // just append all other characters as usual
126                 sbVal.append(value.charAt(i));
127             }
128 
129             value = sbVal.toString();
130         }
131 
132         // we need the formatter with the correct Locale of the user
133         return value;
134     }
135 
136     private Double stringToDouble(String value)
137     {
138         // this is a special hack for a jvm vulnerability with
139         // converting some special double values.
140         // e.g. "2.225073858507201200000e-308"
141         // see MYFACES-3024 for further information
142         // TODO we can remove this hack, once this got fixed in the jvm!
143         if (value.length() >= 23)
144         {
145             StringBuffer normalized = new StringBuffer();
146             for (int i=0; i< value.length(); i++)
147             {
148                 char c = value.charAt(i);
149                 if ( c != '.')
150                 {
151                     normalized.append(c);
152                 }
153             }
154 
155             String normalizedString = normalized.toString();
156             if (normalizedString.contains("22250738585072012") && normalizedString.contains("e-"))
157             {
158                 // oops, baaad value!
159                throw new NumberFormatException("Not Allowed! This value could possibly kill the VM!");
160             }
161         }
162 
163 
164         return Double.valueOf(value);
165     }
166 
167     public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value)
168     {
169         if (facesContext == null)
170         {
171             throw new NullPointerException("facesContext");
172         }
173         if (uiComponent == null)
174         {
175             throw new NullPointerException("uiComponent");
176         }
177 
178         if (value == null)
179         {
180             return "";
181         }
182         if (value instanceof String)
183         {
184             return (String)value;
185         }
186         try
187         {
188             return Double.toString(((Number)value).doubleValue());
189         }
190         catch (Exception e)
191         {
192             throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, STRING_ID,
193                     new Object[]{value,_MessageUtils.getLabel(facesContext, uiComponent)}),e);
194         }
195     }
196 }