Coverage Report - javax.faces.convert.NumberConverter
 
Classes in this File Line Coverage Branch Coverage Complexity
NumberConverter
0%
0/206
0%
0/127
0
 
 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 java.text.DecimalFormat;
 22  
 import java.text.DecimalFormatSymbols;
 23  
 import java.text.NumberFormat;
 24  
 import java.text.ParseException;
 25  
 import java.util.Currency;
 26  
 import java.util.Locale;
 27  
 
 28  
 import javax.faces.component.PartialStateHolder;
 29  
 import javax.faces.component.UIComponent;
 30  
 import javax.faces.context.FacesContext;
 31  
 
 32  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFConverter;
 33  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
 34  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
 35  
 
 36  
 /**
 37  
  * This tag creates a number formatting converter and associates it
 38  
  * with the nearest parent UIComponent.
 39  
  * 
 40  
  * Unless otherwise specified, all attributes accept static values or EL expressions.
 41  
  * 
 42  
  * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
 43  
  *
 44  
  * @author Thomas Spiegl (latest modification by $Author: lu4242 $)
 45  
  * @version $Revision: 882743 $ $Date: 2009-11-20 17:28:21 -0500 (Fri, 20 Nov 2009) $
 46  
  */
 47  
 @JSFConverter(
 48  
     name="f:convertNumber",
 49  
     bodyContent="empty",
 50  
     tagClass="org.apache.myfaces.taglib.core.ConvertNumberTag")
 51  
 @JSFJspProperty(
 52  
     name="binding", 
 53  
     returnType = "javax.faces.convert.NumberConverter",
 54  
     longDesc = "A ValueExpression that evaluates to a NumberConverter.")
 55  
 public class NumberConverter
 56  
         implements Converter, PartialStateHolder
 57  
 {
 58  
     // API FIELDS
 59  
     public static final String CONVERTER_ID = "javax.faces.Number";
 60  
     public static final String STRING_ID = "javax.faces.converter.STRING";
 61  
     public static final String CURRENCY_ID = "javax.faces.converter.NumberConverter.CURRENCY";
 62  
     public static final String NUMBER_ID = "javax.faces.converter.NumberConverter.NUMBER";
 63  
     public static final String PATTERN_ID = "javax.faces.converter.NumberConverter.PATTERN";
 64  
     public static final String PERCENT_ID = "javax.faces.converter.NumberConverter.PERCENT";
 65  
 
 66  
     private static final boolean JAVA_VERSION_14;
 67  
 
 68  
     static
 69  
     {
 70  0
         JAVA_VERSION_14 = checkJavaVersion14();
 71  0
     }
 72  
 
 73  
     private String _currencyCode;
 74  
     private String _currencySymbol;
 75  
     private Locale _locale;
 76  
     private int _maxFractionDigits;
 77  
     private int _maxIntegerDigits;
 78  
     private int _minFractionDigits;
 79  
     private int _minIntegerDigits;
 80  
     private String _pattern;
 81  0
     private String _type = "number";
 82  0
     private boolean _groupingUsed = true;
 83  0
     private boolean _integerOnly = false;
 84  
     private boolean _transient;
 85  
 
 86  
     private boolean _maxFractionDigitsSet;
 87  
     private boolean _maxIntegerDigitsSet;
 88  
     private boolean _minFractionDigitsSet;
 89  
     private boolean _minIntegerDigitsSet;
 90  
 
 91  
 
 92  
     // CONSTRUCTORS
 93  
     public NumberConverter()
 94  0
     {
 95  0
     }
 96  
 
 97  
     // METHODS
 98  
     public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
 99  
     {
 100  0
         if (facesContext == null) throw new NullPointerException("facesContext");
 101  0
         if (uiComponent == null) throw new NullPointerException("uiComponent");
 102  
 
 103  0
         if (value != null)
 104  
         {
 105  0
             value = value.trim();
 106  0
             if (value.length() > 0)
 107  
             {
 108  0
                 NumberFormat format = getNumberFormat(facesContext);
 109  0
                 format.setParseIntegerOnly(_integerOnly);
 110  
                 
 111  0
                 DecimalFormat df = (DecimalFormat)format;
 112  
                 //df.setParseBigDecimal(true);
 113  0
                 DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
 114  0
                 boolean changed = false;
 115  0
                 if(dfs.getGroupingSeparator() == '\u00a0')
 116  
                 {
 117  0
                   dfs.setGroupingSeparator(' ');
 118  0
                   df.setDecimalFormatSymbols(dfs);
 119  0
                   changed = true;
 120  
                 }
 121  
                 try
 122  
                 {
 123  0
                     return format.parse(value);
 124  
                 }
 125  0
                 catch (ParseException e)
 126  
                 {
 127  0
                   if(changed)
 128  
                   {
 129  0
                     dfs.setGroupingSeparator('\u00a0');
 130  0
                     df.setDecimalFormatSymbols(dfs);
 131  
                   }
 132  
                   try
 133  
                   {
 134  0
                     return format.parse(value);
 135  
                   }
 136  0
                   catch (ParseException pe)
 137  
                   {
 138  
 
 139  0
                     if(getPattern() != null)
 140  0
                         throw new ConverterException(_MessageUtils.getErrorMessage(facesContext,
 141  
                                                                                     PATTERN_ID,
 142  
                                                                                     new Object[]{value,"$###,###",_MessageUtils.getLabel(facesContext, uiComponent)}));
 143  0
                     else if(getType().equals("number"))
 144  0
                         throw new ConverterException(_MessageUtils.getErrorMessage(facesContext,
 145  
                                                                                     NUMBER_ID,
 146  
                                                                                     new Object[]{value,"21",_MessageUtils.getLabel(facesContext, uiComponent)}));
 147  0
                     else if(getType().equals("currency"))
 148  0
                         throw new ConverterException(_MessageUtils.getErrorMessage(facesContext,
 149  
                                                                                     CURRENCY_ID,
 150  
                                                                                     new Object[]{value,"42.25",_MessageUtils.getLabel(facesContext, uiComponent)}));
 151  0
                     else if(getType().equals("percent"))
 152  0
                         throw new ConverterException(_MessageUtils.getErrorMessage(facesContext,
 153  
                                                                                     PERCENT_ID,
 154  
                                                                                     new Object[]{value,".90",_MessageUtils.getLabel(facesContext, uiComponent)}));
 155  
                   }
 156  
                 }
 157  
             }
 158  
         }
 159  0
         return null;
 160  
     }
 161  
 
 162  
     public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value)
 163  
     {
 164  0
         if (facesContext == null) throw new NullPointerException("facesContext");
 165  0
         if (uiComponent == null) throw new NullPointerException("uiComponent");
 166  
 
 167  0
         if (value == null)
 168  
         {
 169  0
             return "";
 170  
         }
 171  0
         if (value instanceof String)
 172  
         {
 173  0
             return (String)value;
 174  
         }
 175  
 
 176  0
         NumberFormat format = getNumberFormat(facesContext);
 177  0
         format.setGroupingUsed(_groupingUsed);
 178  0
         if (_maxFractionDigitsSet) format.setMaximumFractionDigits(_maxFractionDigits);
 179  0
         if (_maxIntegerDigitsSet) format.setMaximumIntegerDigits(_maxIntegerDigits);
 180  0
         if (_minFractionDigitsSet) format.setMinimumFractionDigits(_minFractionDigits);
 181  0
         if (_minIntegerDigitsSet) format.setMinimumIntegerDigits(_minIntegerDigits);
 182  0
         formatCurrency(format);
 183  
         try
 184  
         {
 185  0
             return format.format(value);
 186  
         }
 187  0
         catch (Exception e)
 188  
         {
 189  0
             throw new ConverterException(_MessageUtils.getErrorMessage(facesContext, STRING_ID, new Object[]{value,_MessageUtils.getLabel(facesContext, uiComponent)}),e);
 190  
         }
 191  
     }
 192  
 
 193  
     private NumberFormat getNumberFormat(FacesContext facesContext)
 194  
     {
 195  0
         Locale locale = _locale != null ? _locale : facesContext.getViewRoot().getLocale();
 196  
 
 197  0
         if (_pattern == null && _type == null)
 198  
         {
 199  0
             throw new ConverterException("Cannot get NumberFormat, either type or pattern needed.");
 200  
         }
 201  
 
 202  
         // pattern
 203  0
         if (_pattern != null)
 204  
         {
 205  0
             return new DecimalFormat(_pattern, new DecimalFormatSymbols(locale));
 206  
         }
 207  
 
 208  
         // type
 209  0
         if (_type.equals("number"))
 210  
         {
 211  0
             return NumberFormat.getNumberInstance(locale);
 212  
         }
 213  0
         else if (_type.equals("currency"))
 214  
         {
 215  0
             return NumberFormat.getCurrencyInstance(locale);
 216  
         }
 217  0
         else if (_type.equals("percent"))
 218  
         {
 219  0
             return NumberFormat.getPercentInstance(locale);
 220  
         }
 221  0
         throw new ConverterException("Cannot get NumberFormat, illegal type " + _type);
 222  
     }
 223  
 
 224  
     private void formatCurrency(NumberFormat format)
 225  
     {
 226  0
         if (_currencyCode == null && _currencySymbol == null)
 227  
         {
 228  0
             return;
 229  
         }
 230  
 
 231  
         boolean useCurrencyCode;
 232  0
         if (JAVA_VERSION_14)
 233  
         {
 234  0
             useCurrencyCode = _currencyCode != null;
 235  
         }
 236  
         else
 237  
         {
 238  0
             useCurrencyCode = _currencySymbol == null;
 239  
         }
 240  
 
 241  0
         if (useCurrencyCode)
 242  
         {
 243  
             // set Currency
 244  
             try
 245  
             {
 246  0
                 format.setCurrency(Currency.getInstance(_currencyCode));
 247  
             }
 248  0
             catch (Exception e)
 249  
             {
 250  0
                 throw new ConverterException("Unable to get Currency instance for currencyCode " +
 251  
                                              _currencyCode);
 252  0
             }
 253  
         }
 254  0
         else if (format instanceof DecimalFormat)
 255  
 
 256  
         {
 257  0
             DecimalFormat dFormat = (DecimalFormat)format;
 258  0
             DecimalFormatSymbols symbols = dFormat.getDecimalFormatSymbols();
 259  0
             symbols.setCurrencySymbol(_currencySymbol);
 260  0
             dFormat.setDecimalFormatSymbols(symbols);
 261  
         }
 262  0
     }
 263  
 
 264  
     // STATE SAVE/RESTORE
 265  
     public void restoreState(FacesContext facesContext, Object state)
 266  
     {
 267  0
         if (state != null)
 268  
         {
 269  0
             Object values[] = (Object[])state;
 270  0
             _currencyCode = (String)values[0];
 271  0
             _currencySymbol = (String)values[1];
 272  0
             _locale = (Locale)values[2];
 273  0
             Integer value = (Integer)values[3];
 274  0
             _maxFractionDigits = value != null ? value.intValue() : 0;
 275  0
             value = (Integer)values[4];
 276  0
             _maxIntegerDigits = value != null ? value.intValue() : 0;
 277  0
             value = (Integer)values[5];
 278  0
             _minFractionDigits = value != null ? value.intValue() : 0;
 279  0
             value = (Integer)values[6];
 280  0
             _minIntegerDigits = value != null ? value.intValue() : 0;
 281  0
             _pattern = (String)values[7];
 282  0
             _type = (String)values[8];
 283  0
             _groupingUsed = ((Boolean)values[9]).booleanValue();
 284  0
             _integerOnly = ((Boolean)values[10]).booleanValue();
 285  0
             _maxFractionDigitsSet = ((Boolean)values[11]).booleanValue();
 286  0
             _maxIntegerDigitsSet = ((Boolean)values[12]).booleanValue();
 287  0
             _minFractionDigitsSet = ((Boolean)values[13]).booleanValue();
 288  0
             _minIntegerDigitsSet = ((Boolean)values[14]).booleanValue();
 289  
         }
 290  0
     }
 291  
 
 292  
     public Object saveState(FacesContext facesContext)
 293  
     {
 294  0
         if (!initialStateMarked())
 295  
         {
 296  0
             Object values[] = new Object[15];
 297  0
             values[0] = _currencyCode;
 298  0
             values[1] = _currencySymbol;
 299  0
             values[2] = _locale;
 300  0
             values[3] = _maxFractionDigitsSet ? new Integer(_maxFractionDigits) : null;
 301  0
             values[4] = _maxIntegerDigitsSet ? new Integer(_maxIntegerDigits) : null;
 302  0
             values[5] = _minFractionDigitsSet ? new Integer(_minFractionDigits) : null;
 303  0
             values[6] = _minIntegerDigitsSet ? new Integer(_minIntegerDigits) : null;
 304  0
             values[7] = _pattern;
 305  0
             values[8] = _type;
 306  0
             values[9] = _groupingUsed ? Boolean.TRUE : Boolean.FALSE;
 307  0
             values[10] = _integerOnly ? Boolean.TRUE : Boolean.FALSE;
 308  0
             values[11] = _maxFractionDigitsSet ? Boolean.TRUE : Boolean.FALSE;
 309  0
             values[12] = _maxIntegerDigitsSet ? Boolean.TRUE : Boolean.FALSE;
 310  0
             values[13] = _minFractionDigitsSet ? Boolean.TRUE : Boolean.FALSE;
 311  0
             values[14] = _minIntegerDigitsSet ? Boolean.TRUE : Boolean.FALSE;
 312  0
             return values;
 313  
         }
 314  0
         return null;
 315  
     }
 316  
 
 317  
     // GETTER & SETTER
 318  
     
 319  
     /**
 320  
      * ISO 4217 currency code
 321  
      * 
 322  
      */
 323  
     @JSFProperty
 324  
     public String getCurrencyCode()
 325  
     {
 326  0
         return _currencyCode != null ?
 327  
                _currencyCode :
 328  
                getDecimalFormatSymbols().getInternationalCurrencySymbol();
 329  
     }
 330  
 
 331  
     public void setCurrencyCode(String currencyCode)
 332  
     {
 333  0
         _currencyCode = currencyCode;
 334  0
         clearInitialState();
 335  0
     }
 336  
 
 337  
     /**
 338  
      * The currency symbol used to format a currency value.  Defaults
 339  
      * to the currency symbol for locale.
 340  
      * 
 341  
      */
 342  
     @JSFProperty
 343  
     public String getCurrencySymbol()
 344  
     {
 345  0
         return _currencySymbol != null ?
 346  
                _currencySymbol :
 347  
                getDecimalFormatSymbols().getCurrencySymbol();
 348  
     }
 349  
 
 350  
     public void setCurrencySymbol(String currencySymbol)
 351  
     {
 352  0
         _currencySymbol = currencySymbol;
 353  0
         clearInitialState();
 354  0
     }
 355  
 
 356  
     /**
 357  
      * Specifies whether output will contain grouping separators.  Default: true.
 358  
      * 
 359  
      */
 360  
     @JSFProperty(deferredValueType="java.lang.Boolean")
 361  
     public boolean isGroupingUsed()
 362  
     {
 363  0
         return _groupingUsed;
 364  
     }
 365  
 
 366  
     public void setGroupingUsed(boolean groupingUsed)
 367  
     {
 368  0
         _groupingUsed = groupingUsed;
 369  0
         clearInitialState();
 370  0
     }
 371  
 
 372  
     /**
 373  
      * Specifies whether only the integer part of the input will be parsed.  Default: false.
 374  
      * 
 375  
      */
 376  
     @JSFProperty(deferredValueType="java.lang.Boolean")
 377  
     public boolean isIntegerOnly()
 378  
     {
 379  0
         return _integerOnly;
 380  
     }
 381  
 
 382  
     public void setIntegerOnly(boolean integerOnly)
 383  
     {
 384  0
         _integerOnly = integerOnly;
 385  0
         clearInitialState();
 386  0
     }
 387  
 
 388  
     /**
 389  
      * The name of the locale to be used, instead of the default as
 390  
      * specified in the faces configuration file.
 391  
      * 
 392  
      */
 393  
     @JSFProperty(deferredValueType="java.lang.Object")
 394  
     public Locale getLocale()
 395  
     {
 396  0
         if (_locale != null) return _locale;
 397  0
         FacesContext context = FacesContext.getCurrentInstance();
 398  0
         return context.getViewRoot().getLocale();
 399  
     }
 400  
 
 401  
     public void setLocale(Locale locale)
 402  
     {
 403  0
         _locale = locale;
 404  0
         clearInitialState();
 405  0
     }
 406  
 
 407  
     /**
 408  
      * The maximum number of digits in the fractional portion of the number.
 409  
      * 
 410  
      */
 411  
     @JSFProperty(deferredValueType="java.lang.Integer")
 412  
     public int getMaxFractionDigits()
 413  
     {
 414  0
         return _maxFractionDigits;
 415  
     }
 416  
 
 417  
     public void setMaxFractionDigits(int maxFractionDigits)
 418  
     {
 419  0
         _maxFractionDigitsSet = true;
 420  0
         _maxFractionDigits = maxFractionDigits;
 421  0
         clearInitialState();
 422  0
     }
 423  
 
 424  
     /**
 425  
      * The maximum number of digits in the integer portion of the number.
 426  
      * 
 427  
      */
 428  
     @JSFProperty(deferredValueType="java.lang.Integer")
 429  
     public int getMaxIntegerDigits()
 430  
     {
 431  0
         return _maxIntegerDigits;
 432  
     }
 433  
 
 434  
     public void setMaxIntegerDigits(int maxIntegerDigits)
 435  
     {
 436  0
         _maxIntegerDigitsSet = true;
 437  0
         _maxIntegerDigits = maxIntegerDigits;
 438  0
         clearInitialState();
 439  0
     }
 440  
 
 441  
     /**
 442  
      * The minimum number of digits in the fractional portion of the number.
 443  
      * 
 444  
      */
 445  
     @JSFProperty(deferredValueType="java.lang.Integer")
 446  
     public int getMinFractionDigits()
 447  
     {
 448  0
         return _minFractionDigits;
 449  
     }
 450  
 
 451  
     public void setMinFractionDigits(int minFractionDigits)
 452  
     {
 453  0
         _minFractionDigitsSet = true;
 454  0
         _minFractionDigits = minFractionDigits;
 455  0
         clearInitialState();
 456  0
     }
 457  
 
 458  
     /**
 459  
      * The minimum number of digits in the integer portion of the number.
 460  
      * 
 461  
      */
 462  
     @JSFProperty(deferredValueType="java.lang.Integer")
 463  
     public int getMinIntegerDigits()
 464  
     {
 465  0
         return _minIntegerDigits;
 466  
     }
 467  
 
 468  
     public void setMinIntegerDigits(int minIntegerDigits)
 469  
     {
 470  0
         _minIntegerDigitsSet = true;
 471  0
         _minIntegerDigits = minIntegerDigits;
 472  0
         clearInitialState();
 473  0
     }
 474  
 
 475  
     /**
 476  
      * A custom Date formatting pattern, in the format used by java.text.SimpleDateFormat.
 477  
      * 
 478  
      */
 479  
     @JSFProperty
 480  
     public String getPattern()
 481  
     {
 482  0
         return _pattern;
 483  
     }
 484  
 
 485  
     public void setPattern(String pattern)
 486  
     {
 487  0
         _pattern = pattern;
 488  0
         clearInitialState();
 489  0
     }
 490  
 
 491  
     public boolean isTransient()
 492  
     {
 493  0
         return _transient;
 494  
     }
 495  
 
 496  
     public void setTransient(boolean aTransient)
 497  
     {
 498  0
         _transient = aTransient;
 499  0
     }
 500  
 
 501  
     /**
 502  
      * The type of formatting/parsing to be performed.  Values include:
 503  
      * number, currency, and percent.  Default: number.
 504  
      * 
 505  
      */
 506  
     @JSFProperty
 507  
     public String getType()
 508  
     {
 509  0
         return _type;
 510  
     }
 511  
 
 512  
     public void setType(String type)
 513  
     {
 514  
         //TODO: validate type
 515  0
         _type = type;
 516  0
         clearInitialState();
 517  0
     }
 518  
 
 519  
     private static boolean checkJavaVersion14()
 520  
     {
 521  0
         String version = System.getProperty("java.version");
 522  0
         if (version == null)
 523  
         {
 524  0
             return false;
 525  
         }
 526  0
         byte java14 = 0;
 527  0
         for (int idx = version.indexOf('.'), i = 0; idx > 0 || version != null; i++)
 528  
         {
 529  0
             if (idx > 0)
 530  
             {
 531  0
                 byte value = Byte.parseByte(version.substring(0, 1));
 532  0
                 version = version.substring(idx + 1, version.length());
 533  0
                 idx = version.indexOf('.');
 534  0
                 switch (i)
 535  
                 {
 536  
                     case 0:
 537  0
                         if (value == 1)
 538  
                         {
 539  0
                             java14 = 1;
 540  0
                             break;
 541  
                         }
 542  0
                         else if (value > 1)
 543  
                         {
 544  0
                             java14 = 2;
 545  
                         }
 546  
                     case 1:
 547  0
                         if (java14 > 0 && value >= 4)
 548  
                         {
 549  0
                             java14 = 2;
 550  
                         }
 551  
                         ;
 552  
                     default:
 553  0
                         idx = 0;
 554  0
                         version = null;
 555  
                         break;
 556  
                 }
 557  0
             }
 558  
             else
 559  
             {
 560  0
                 byte value = Byte.parseByte(version.substring(0, 1));
 561  0
                 if (java14 > 0 && value >= 4)
 562  
                 {
 563  0
                     java14 = 2;
 564  
                 }
 565  
                 break;
 566  
             }
 567  
         }
 568  0
         return java14 == 2;
 569  
     }
 570  
 
 571  
 
 572  
     private DecimalFormatSymbols getDecimalFormatSymbols()
 573  
     {
 574  0
         return new DecimalFormatSymbols(getLocale());
 575  
     }
 576  
     
 577  0
     private boolean _initialStateMarked = false;
 578  
 
 579  
     public void clearInitialState()
 580  
     {
 581  0
         _initialStateMarked = false;
 582  0
     }
 583  
 
 584  
     public boolean initialStateMarked()
 585  
     {
 586  0
         return _initialStateMarked;
 587  
     }
 588  
 
 589  
     public void markInitialState()
 590  
     {
 591  0
         _initialStateMarked = true;
 592  0
     }
 593  
 }