001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.coerce; 016 017 import org.apache.hivemind.ApplicationRuntimeException; 018 import org.apache.hivemind.util.ConstructorUtils; 019 import org.apache.hivemind.util.Defense; 020 021 import java.beans.PropertyEditor; 022 import java.beans.PropertyEditorManager; 023 import java.util.HashMap; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.Map; 027 028 /** 029 * Implementation of {@link org.apache.tapestry.coerce.ValueConverter}. Selects an appropriate type 030 * converter and delegates to it. 031 * 032 * @author Howard M. Lewis Ship 033 * @since 4.0 034 */ 035 public class ValueConverterImpl implements ValueConverter 036 { 037 /** List of {@link org.apache.tapestry.coerce.TypeConverterContribution}. */ 038 039 public List _contributions; 040 041 private Map _converterMap = new HashMap(); 042 043 private Map _primitiveToWrapper = new HashMap(); 044 045 private Map _wrapperToPrimitive = new HashMap(); 046 047 { 048 store(boolean.class, Boolean.class); 049 store(byte.class, Byte.class); 050 store(short.class, Short.class); 051 store(char.class, Character.class); 052 store(int.class, Integer.class); 053 store(long.class, Long.class); 054 store(float.class, Float.class); 055 store(double.class, Double.class); 056 } 057 058 private void store(Class primitive, Class wrapper) 059 { 060 _primitiveToWrapper.put(primitive, wrapper); 061 062 _wrapperToPrimitive.put(wrapper, primitive); 063 } 064 065 public void initializeService() 066 { 067 Iterator i = _contributions.iterator(); 068 while (i.hasNext()) 069 { 070 TypeConverterContribution c = (TypeConverterContribution) i.next(); 071 072 _converterMap.put(c.getSubjectClass(), c.getConverter()); 073 } 074 } 075 076 public Object coerceValue(Object value, Class desiredType) 077 { 078 Defense.notNull(desiredType, "desiredType"); 079 080 Class effectiveType = convertType(desiredType); 081 082 // Already the correct type? Go no further! 083 084 if (value != null && effectiveType.isAssignableFrom(value.getClass())) 085 return value; 086 087 Object result = convertNumberToNumber(value, effectiveType); 088 089 if (result != null) 090 return result; 091 092 result = convertUsingPropertyEditor(value, effectiveType); 093 094 if (result != null) 095 return result; 096 097 TypeConverter converter = (TypeConverter) _converterMap.get(effectiveType); 098 099 // null value and no converter for the given type? Just return null. 100 101 if (value == null && converter == null) 102 return null; 103 104 if (converter == null) 105 throw new ApplicationRuntimeException(CoerceMessages.noConverter(value.getClass(), effectiveType)); 106 107 return converter.convertValue(value); 108 } 109 110 /** 111 * Attempts to use {@link java.beans.PropertyEditor}to perform a conversion from a string to a 112 * numeric type. Returns null if no property editor can be found. 113 * 114 * @param value 115 * The value to convert 116 * @param targetType 117 * The type to convert to (must be a wrapper type, not a primitive type) 118 */ 119 120 private Number convertUsingPropertyEditor(Object value, Class targetType) 121 { 122 // Convert from wrapper type back to primitive type, because 123 // PropertyEditorManager expects primitive types not 124 // wrapper types. 125 126 if (value == null || value.getClass() != String.class 127 || !Number.class.isAssignableFrom(targetType)) 128 return null; 129 130 Class primitiveType = (Class) _wrapperToPrimitive.get(targetType); 131 132 // Note a primitive type. 133 134 if (primitiveType == null) 135 return null; 136 137 // Looks like a conversion from String to Number, let's see. 138 139 PropertyEditor editor = PropertyEditorManager.findEditor(primitiveType); 140 141 // This should not happen, since we've filtered down to just the 142 // primitive types that do have property editors. 143 144 if (editor == null) 145 return null; 146 147 String text = (String) value; 148 149 try 150 { 151 editor.setAsText(text); 152 153 return (Number) editor.getValue(); 154 } 155 catch (Exception ex) 156 { 157 throw new ApplicationRuntimeException(CoerceMessages.stringToNumberConversionError( 158 text, 159 targetType, 160 ex), ex); 161 } 162 163 } 164 165 private Number convertNumberToNumber(Object value, Class targetType) 166 { 167 if (value == null || !Number.class.isAssignableFrom(value.getClass()) 168 || !Number.class.isAssignableFrom(targetType)) 169 return null; 170 171 String valueAsString = value.toString(); 172 173 return (Number) ConstructorUtils.invokeConstructor(targetType, new Object[] { valueAsString }); 174 } 175 176 private Class convertType(Class possiblePrimitiveType) 177 { 178 Class wrapperType = (Class) _primitiveToWrapper.get(possiblePrimitiveType); 179 180 return wrapperType == null ? possiblePrimitiveType : wrapperType; 181 } 182 183 public void setContributions(List contributions) 184 { 185 _contributions = contributions; 186 } 187 }