001 /* 002 * Copyright 2002-2004 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://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, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016 package org.springframework.richclient.application.support; 017 018 import java.beans.PropertyEditor; 019 import java.lang.reflect.Modifier; 020 import java.math.BigDecimal; 021 import java.text.DateFormat; 022 import java.util.Date; 023 import java.util.HashMap; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.Properties; 028 import java.util.Set; 029 030 import org.apache.commons.logging.Log; 031 import org.apache.commons.logging.LogFactory; 032 import org.springframework.beans.propertyeditors.ClassEditor; 033 import org.springframework.beans.propertyeditors.CustomBooleanEditor; 034 import org.springframework.beans.propertyeditors.CustomDateEditor; 035 import org.springframework.beans.propertyeditors.CustomNumberEditor; 036 import org.springframework.richclient.util.ClassUtils; 037 import org.springframework.util.Assert; 038 039 /** 040 * This provides a default implementation of {@link PropertyEditorRegistry} 041 * 042 * @author Jim Moore 043 */ 044 public class DefaultPropertyEditorRegistry 045 //implements PropertyEditorRegistry 046 { 047 private static final Log logger = LogFactory.getLog( 048 DefaultPropertyEditorRegistry.class); 049 050 private Map propertyEditorByClass = new HashMap(); 051 052 private Map propertyEditorByClassAndProperty = new HashMap(); 053 054 public DefaultPropertyEditorRegistry() { 055 initDefaultEditors(); 056 } 057 058 /** 059 * Initialize the default property editors, covering primitive and other basic value types. 060 */ 061 protected void initDefaultEditors() { 062 setPropertyEditor(int.class, DefaultIntegerEditor.class); 063 setPropertyEditor(Integer.class, DefaultIntegerEditor.class); 064 setPropertyEditor(long.class, DefaultLongEditor.class); 065 setPropertyEditor(Long.class, DefaultLongEditor.class); 066 setPropertyEditor(float.class, DefaultFloatEditor.class); 067 setPropertyEditor(Float.class, DefaultFloatEditor.class); 068 setPropertyEditor(double.class, DefaultDoubleEditor.class); 069 setPropertyEditor(Double.class, DefaultDoubleEditor.class); 070 setPropertyEditor(BigDecimal.class, DefaultBigDecimalEditor.class); 071 setPropertyEditor(Date.class, DefaultDateEditor.class); 072 } 073 074 private static class DefaultIntegerEditor extends CustomNumberEditor { 075 public DefaultIntegerEditor() { 076 super(Integer.class, true); 077 } 078 } 079 080 private static class DefaultLongEditor extends CustomNumberEditor { 081 public DefaultLongEditor() { 082 super(Long.class, true); 083 } 084 } 085 086 private static class DefaultFloatEditor extends CustomNumberEditor { 087 public DefaultFloatEditor() { 088 super(Float.class, true); 089 } 090 } 091 092 private static class DefaultDoubleEditor extends CustomNumberEditor { 093 public DefaultDoubleEditor() { 094 super(Double.class, true); 095 } 096 } 097 098 private static class DefaultBigDecimalEditor extends CustomNumberEditor { 099 public DefaultBigDecimalEditor() { 100 super(BigDecimal.class, true); 101 } 102 } 103 104 private static class DefaultBooleanEditor extends CustomBooleanEditor { 105 public DefaultBooleanEditor() { 106 super(true); 107 } 108 } 109 110 private static class DefaultDateEditor extends CustomDateEditor { 111 public DefaultDateEditor() { 112 super(DateFormat.getDateInstance(), true); 113 } 114 } 115 116 /** 117 * Adds a list of property editors to the registry extracting the object 118 * class, property name and property editor class from the properties 119 * "objectClass", "propertyName" and "propertyEditorClass". 120 * 121 * @param propertyEditors 122 * the list of property editors. Each element is expected to be 123 * an instance of <code>java.lang.Properties</code>. 124 * @see DefaultPropertyEditorRegistry#setPropertyEditor(Properties) 125 */ 126 public void setPropertyEditors(List propertyEditors) { 127 for (Iterator i = propertyEditors.iterator(); i.hasNext();) { 128 setPropertyEditor((Properties)i.next()); 129 } 130 } 131 132 /** 133 * Adds a property editor to the registry extracting the object class, 134 * property name and property editor class from the properties 135 * "objectClass", "propertyName" and "propertyEditorClass". 136 * 137 * @param properties 138 * the properties 139 */ 140 public void setPropertyEditor(Properties properties) { 141 ClassEditor classEditor = new ClassEditor(); 142 143 Class objectClass = null; 144 String propertyName = null; 145 Class propertyEditorClass = null; 146 147 if (properties.get("objectClass") != null) { 148 classEditor.setAsText((String) properties.get("objectClass")); 149 objectClass = (Class) classEditor.getValue(); 150 } 151 propertyName = (String) properties.get("propertyName"); 152 if (properties.get("propertyEditorClass") != null) { 153 classEditor.setAsText((String) properties.get("propertyEditorClass")); 154 propertyEditorClass = (Class) classEditor.getValue(); 155 } else { 156 throw new IllegalArgumentException("propertyEditorClass is required"); 157 } 158 159 if (propertyName != null) { 160 setPropertyEditor(objectClass, propertyName, propertyEditorClass); 161 } else if (objectClass != null) { 162 setPropertyEditor(objectClass, propertyEditorClass); 163 } else { 164 throw new IllegalArgumentException("objectClass and/or propertyName are required"); 165 } 166 } 167 168 169 public void setPropertyEditor(final Class typeClass, 170 final Class propertyEditorClass) { 171 Assert.notNull(typeClass); 172 verifyPropertyEditorClass(propertyEditorClass); 173 174 if (logger.isDebugEnabled()) { 175 logger.debug("Setting " + propertyEditorClass + 176 " as the property editor for " + typeClass); 177 } 178 this.propertyEditorByClass.put(typeClass, propertyEditorClass); 179 } 180 181 182 private void verifyPropertyEditorClass(final Class propertyEditorClass) { 183 // do some checks so we "fail fast" 184 Assert.notNull(propertyEditorClass); 185 Assert.isTrue(PropertyEditor.class.isAssignableFrom(propertyEditorClass), 186 propertyEditorClass + " is not a " + PropertyEditor.class); 187 try { 188 Assert.isTrue(Modifier.isPublic(propertyEditorClass.getConstructor(null).getModifiers()), 189 propertyEditorClass + " does not have a public no-arg constructor"); 190 } 191 catch (NoSuchMethodException e) { 192 throw new IllegalArgumentException(propertyEditorClass + 193 " does not have a no-arg constructor"); 194 } 195 } 196 197 198 public void setPropertyEditor(final Class objectType, 199 final String propertyName, 200 final Class propertyEditorClass) { 201 Assert.notNull(objectType); 202 Assert.notNull(propertyName); 203 Assert.isTrue(ClassUtils.isAProperty(objectType, propertyName), "'" + propertyName + "' is not a property of " + objectType); 204 verifyPropertyEditorClass(propertyEditorClass); 205 if (logger.isDebugEnabled()) { 206 logger.debug("Setting " + propertyEditorClass + 207 " as the property editor for the '" + propertyName + 208 "' property of " + objectType); 209 } 210 final ClassAndPropertyKey key = 211 new ClassAndPropertyKey(objectType, propertyName); 212 this.propertyEditorByClassAndProperty.put(key, propertyEditorClass); 213 } 214 215 216 public PropertyEditor getPropertyEditor(final Class typeClass) { 217 final Class editorClass = (Class)ClassUtils.getValueFromMapForClass( 218 typeClass, this.propertyEditorByClass); 219 220 if (editorClass == null) { 221 if (logger.isDebugEnabled()) { 222 logger.debug("Could not find a property editor for " + 223 typeClass); 224 } 225 return null; 226 } 227 228 return instantiatePropertyEditor(editorClass); 229 } 230 231 232 public PropertyEditor getPropertyEditor(final Class objectType, 233 final String propertyName) { 234 final ClassAndPropertyKey key = new ClassAndPropertyKey(objectType, 235 propertyName); 236 Class editorClass = 237 (Class)this.propertyEditorByClassAndProperty.get(key); 238 239 if (editorClass != null) { 240 return instantiatePropertyEditor(editorClass); 241 } 242 243 // maybe it's registered under a different class... 244 final Set keys = this.propertyEditorByClassAndProperty.keySet(); 245 final Map map = new HashMap(); 246 for (Iterator iterator = keys.iterator(); iterator.hasNext();) { 247 ClassAndPropertyKey propertyKey = (ClassAndPropertyKey)iterator.next(); 248 if (propertyName.equals(propertyKey.getPropertyName())) { 249 map.put(propertyKey.getTheClass(), 250 this.propertyEditorByClassAndProperty.get(propertyKey)); 251 } 252 } 253 254 editorClass = 255 (Class)ClassUtils.getValueFromMapForClass(objectType, map); 256 if (editorClass != null) { 257 // remember the lookup so it doesn't have to be discovered again 258 setPropertyEditor(objectType, propertyName, editorClass); 259 return instantiatePropertyEditor(editorClass); 260 } 261 262 if (logger.isDebugEnabled()) { 263 logger.debug("Could not find a property editor for the " + 264 propertyName + " property of " + objectType + 265 ", so looking for it by class type"); 266 } 267 268 // didn't find it directly, so look for it by the class 269 final Class propertyClass = 270 ClassUtils.getPropertyClass(objectType, propertyName); 271 272 return getPropertyEditor(propertyClass); 273 } 274 275 276 private static PropertyEditor instantiatePropertyEditor(Class propEdClass) { 277 try { 278 return (PropertyEditor)propEdClass.newInstance(); 279 } 280 catch (InstantiationException e) { 281 IllegalStateException exp = new IllegalStateException( 282 "Could not instantiate " + propEdClass); 283 exp.initCause(e); 284 throw exp; 285 } 286 catch (IllegalAccessException e) { 287 IllegalStateException exp = new IllegalStateException( 288 "Could not instantiate " + propEdClass); 289 exp.initCause(e); 290 throw exp; 291 } 292 } 293 294 295 //*********************************************************************** 296 // INNER CLASSES 297 //*********************************************************************** 298 299 private static class ClassAndPropertyKey { 300 private Class theClass; 301 private String propertyName; 302 303 public ClassAndPropertyKey(Class theClass, String propertyName) { 304 if (theClass == null || propertyName == null) throw new NullPointerException(); 305 this.propertyName = propertyName; 306 this.theClass = theClass; 307 } 308 309 public String getPropertyName() { 310 return propertyName; 311 } 312 313 public Class getTheClass() { 314 return theClass; 315 } 316 317 318 public boolean equals(Object o) { 319 if (this == o) return true; 320 if (!(o instanceof ClassAndPropertyKey)) return false; 321 322 final ClassAndPropertyKey classAndPropertyKey = (ClassAndPropertyKey)o; 323 324 if (propertyName != null ? 325 !propertyName.equals(classAndPropertyKey.propertyName) : 326 classAndPropertyKey.propertyName != null) 327 return false; 328 if (theClass != null ? 329 !theClass.equals(classAndPropertyKey.theClass) : 330 classAndPropertyKey.theClass != null) 331 return false; 332 333 return true; 334 } 335 336 337 public int hashCode() { 338 int result; 339 result = (theClass != null ? theClass.hashCode() : 0); 340 result = 29 * result + 341 (propertyName != null ? propertyName.hashCode() : 0); 342 return result; 343 } 344 345 } 346 347 }