001/* 002 * Copyright 2002-2012 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.beans.propertyeditors; 018 019import java.beans.PropertyEditorSupport; 020import java.text.NumberFormat; 021 022import org.springframework.util.NumberUtils; 023import org.springframework.util.StringUtils; 024 025/** 026 * Property editor for any Number subclass such as Short, Integer, Long, 027 * BigInteger, Float, Double, BigDecimal. Can use a given NumberFormat for 028 * (locale-specific) parsing and rendering, or alternatively the default 029 * {@code decode} / {@code valueOf} / {@code toString} methods. 030 * 031 * <p>This is not meant to be used as system PropertyEditor but rather 032 * as locale-specific number editor within custom controller code, 033 * parsing user-entered number strings into Number properties of beans 034 * and rendering them in the UI form. 035 * 036 * <p>In web MVC code, this editor will typically be registered with 037 * {@code binder.registerCustomEditor} calls. 038 * 039 * @author Juergen Hoeller 040 * @since 06.06.2003 041 * @see Number 042 * @see java.text.NumberFormat 043 * @see org.springframework.validation.DataBinder#registerCustomEditor 044 */ 045public class CustomNumberEditor extends PropertyEditorSupport { 046 047 private final Class<? extends Number> numberClass; 048 049 private final NumberFormat numberFormat; 050 051 private final boolean allowEmpty; 052 053 054 /** 055 * Create a new CustomNumberEditor instance, using the default 056 * {@code valueOf} methods for parsing and {@code toString} 057 * methods for rendering. 058 * <p>The "allowEmpty" parameter states if an empty String should 059 * be allowed for parsing, i.e. get interpreted as {@code null} value. 060 * Else, an IllegalArgumentException gets thrown in that case. 061 * @param numberClass Number subclass to generate 062 * @param allowEmpty if empty strings should be allowed 063 * @throws IllegalArgumentException if an invalid numberClass has been specified 064 * @see org.springframework.util.NumberUtils#parseNumber(String, Class) 065 * @see Integer#valueOf 066 * @see Integer#toString 067 */ 068 public CustomNumberEditor(Class<? extends Number> numberClass, boolean allowEmpty) throws IllegalArgumentException { 069 this(numberClass, null, allowEmpty); 070 } 071 072 /** 073 * Create a new CustomNumberEditor instance, using the given NumberFormat 074 * for parsing and rendering. 075 * <p>The allowEmpty parameter states if an empty String should 076 * be allowed for parsing, i.e. get interpreted as {@code null} value. 077 * Else, an IllegalArgumentException gets thrown in that case. 078 * @param numberClass Number subclass to generate 079 * @param numberFormat NumberFormat to use for parsing and rendering 080 * @param allowEmpty if empty strings should be allowed 081 * @throws IllegalArgumentException if an invalid numberClass has been specified 082 * @see org.springframework.util.NumberUtils#parseNumber(String, Class, java.text.NumberFormat) 083 * @see java.text.NumberFormat#parse 084 * @see java.text.NumberFormat#format 085 */ 086 public CustomNumberEditor(Class<? extends Number> numberClass, 087 NumberFormat numberFormat, boolean allowEmpty) throws IllegalArgumentException { 088 089 if (numberClass == null || !Number.class.isAssignableFrom(numberClass)) { 090 throw new IllegalArgumentException("Property class must be a subclass of Number"); 091 } 092 this.numberClass = numberClass; 093 this.numberFormat = numberFormat; 094 this.allowEmpty = allowEmpty; 095 } 096 097 098 /** 099 * Parse the Number from the given text, using the specified NumberFormat. 100 */ 101 @Override 102 public void setAsText(String text) throws IllegalArgumentException { 103 if (this.allowEmpty && !StringUtils.hasText(text)) { 104 // Treat empty String as null value. 105 setValue(null); 106 } 107 else if (this.numberFormat != null) { 108 // Use given NumberFormat for parsing text. 109 setValue(NumberUtils.parseNumber(text, this.numberClass, this.numberFormat)); 110 } 111 else { 112 // Use default valueOf methods for parsing text. 113 setValue(NumberUtils.parseNumber(text, this.numberClass)); 114 } 115 } 116 117 /** 118 * Coerce a Number value into the required target class, if necessary. 119 */ 120 @Override 121 public void setValue(Object value) { 122 if (value instanceof Number) { 123 super.setValue(NumberUtils.convertNumberToTargetClass((Number) value, this.numberClass)); 124 } 125 else { 126 super.setValue(value); 127 } 128 } 129 130 /** 131 * Format the Number as String, using the specified NumberFormat. 132 */ 133 @Override 134 public String getAsText() { 135 Object value = getValue(); 136 if (value == null) { 137 return ""; 138 } 139 if (this.numberFormat != null) { 140 // Use NumberFormat for rendering value. 141 return this.numberFormat.format(value); 142 } 143 else { 144 // Use toString method for rendering value. 145 return value.toString(); 146 } 147 } 148 149}