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  @JSFConverter
33  public class DoubleConverter
34          implements Converter
35  {
36      // API FIELDS
37      public static final String CONVERTER_ID = "javax.faces.Double";
38      public static final String STRING_ID = "javax.faces.converter.STRING";
39      public static final String DOUBLE_ID = "javax.faces.converter.DoubleConverter.DOUBLE";
40  
41      // CONSTRUCTORS
42      public DoubleConverter()
43      {
44      }
45  
46      // METHODS
47      public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
48      {
49          if (facesContext == null)
50          {
51              throw new NullPointerException("facesContext");
52          }
53          if (uiComponent == null)
54          {
55              throw new NullPointerException("uiComponent");
56          }
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)
167         {
168             throw new NullPointerException("facesContext");
169         }
170         if (uiComponent == null)
171         {
172             throw new NullPointerException("uiComponent");
173         }
174 
175         if (value == null)
176         {
177             return "";
178         }
179         if (value instanceof String)
180         {
181             return (String)value;
182         }
183         try
184         {
185             return Double.toString(((Number)value).doubleValue());
186         }
187         catch (Exception e)
188         {
189             throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, STRING_ID,
190                     new Object[]{value,_MessageUtils.getLabel(facesContext, uiComponent)}),e);
191         }
192     }
193 }