001/* 002 * Copyright 2002-2018 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.lang.Nullable; 023import org.springframework.util.NumberUtils; 024import org.springframework.util.StringUtils; 025 026/** 027 * Property editor for any Number subclass such as Short, Integer, Long, 028 * BigInteger, Float, Double, BigDecimal. Can use a given NumberFormat for 029 * (locale-specific) parsing and rendering, or alternatively the default 030 * {@code decode} / {@code valueOf} / {@code toString} methods. 031 * 032 * <p>This is not meant to be used as system PropertyEditor but rather 033 * as locale-specific number editor within custom controller code, 034 * parsing user-entered number strings into Number properties of beans 035 * and rendering them in the UI form. 036 * 037 * <p>In web MVC code, this editor will typically be registered with 038 * {@code binder.registerCustomEditor} calls. 039 * 040 * @author Juergen Hoeller 041 * @since 06.06.2003 042 * @see Number 043 * @see java.text.NumberFormat 044 * @see org.springframework.validation.DataBinder#registerCustomEditor 045 */ 046public class CustomNumberEditor extends PropertyEditorSupport { 047 048 private final Class<? extends Number> numberClass; 049 050 @Nullable 051 private final NumberFormat numberFormat; 052 053 private final boolean allowEmpty; 054 055 056 /** 057 * Create a new CustomNumberEditor instance, using the default 058 * {@code valueOf} methods for parsing and {@code toString} 059 * methods for rendering. 060 * <p>The "allowEmpty" parameter states if an empty String should 061 * be allowed for parsing, i.e. get interpreted as {@code null} value. 062 * Else, an IllegalArgumentException gets thrown in that case. 063 * @param numberClass the Number subclass to generate 064 * @param allowEmpty if empty strings should be allowed 065 * @throws IllegalArgumentException if an invalid numberClass has been specified 066 * @see org.springframework.util.NumberUtils#parseNumber(String, Class) 067 * @see Integer#valueOf 068 * @see Integer#toString 069 */ 070 public CustomNumberEditor(Class<? extends Number> numberClass, boolean allowEmpty) throws IllegalArgumentException { 071 this(numberClass, null, allowEmpty); 072 } 073 074 /** 075 * Create a new CustomNumberEditor instance, using the given NumberFormat 076 * for parsing and rendering. 077 * <p>The allowEmpty parameter states if an empty String should 078 * be allowed for parsing, i.e. get interpreted as {@code null} value. 079 * Else, an IllegalArgumentException gets thrown in that case. 080 * @param numberClass the Number subclass to generate 081 * @param numberFormat the NumberFormat to use for parsing and rendering 082 * @param allowEmpty if empty strings should be allowed 083 * @throws IllegalArgumentException if an invalid numberClass has been specified 084 * @see org.springframework.util.NumberUtils#parseNumber(String, Class, java.text.NumberFormat) 085 * @see java.text.NumberFormat#parse 086 * @see java.text.NumberFormat#format 087 */ 088 public CustomNumberEditor(Class<? extends Number> numberClass, 089 @Nullable NumberFormat numberFormat, boolean allowEmpty) throws IllegalArgumentException { 090 091 if (!Number.class.isAssignableFrom(numberClass)) { 092 throw new IllegalArgumentException("Property class must be a subclass of Number"); 093 } 094 this.numberClass = numberClass; 095 this.numberFormat = numberFormat; 096 this.allowEmpty = allowEmpty; 097 } 098 099 100 /** 101 * Parse the Number from the given text, using the specified NumberFormat. 102 */ 103 @Override 104 public void setAsText(String text) throws IllegalArgumentException { 105 if (this.allowEmpty && !StringUtils.hasText(text)) { 106 // Treat empty String as null value. 107 setValue(null); 108 } 109 else if (this.numberFormat != null) { 110 // Use given NumberFormat for parsing text. 111 setValue(NumberUtils.parseNumber(text, this.numberClass, this.numberFormat)); 112 } 113 else { 114 // Use default valueOf methods for parsing text. 115 setValue(NumberUtils.parseNumber(text, this.numberClass)); 116 } 117 } 118 119 /** 120 * Coerce a Number value into the required target class, if necessary. 121 */ 122 @Override 123 public void setValue(@Nullable Object value) { 124 if (value instanceof Number) { 125 super.setValue(NumberUtils.convertNumberToTargetClass((Number) value, this.numberClass)); 126 } 127 else { 128 super.setValue(value); 129 } 130 } 131 132 /** 133 * Format the Number as String, using the specified NumberFormat. 134 */ 135 @Override 136 public String getAsText() { 137 Object value = getValue(); 138 if (value == null) { 139 return ""; 140 } 141 if (this.numberFormat != null) { 142 // Use NumberFormat for rendering value. 143 return this.numberFormat.format(value); 144 } 145 else { 146 // Use toString method for rendering value. 147 return value.toString(); 148 } 149 } 150 151}