001 // Copyright 2006-2007 Daniel Gredler 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 net.sf.beanform.validator; 016 017 import java.util.ArrayList; 018 import java.util.HashMap; 019 import java.util.List; 020 import java.util.Map; 021 022 import net.sf.beanform.prop.BeanProperty; 023 024 import org.apache.commons.logging.Log; 025 import org.apache.commons.logging.LogFactory; 026 import org.apache.tapestry.IComponent; 027 import org.apache.tapestry.event.ReportStatusEvent; 028 import org.apache.tapestry.form.validator.Validator; 029 import org.apache.tapestry.form.validator.ValidatorFactory; 030 031 /** 032 * Default implementation of the {@link CachingValidatorFactory} interface. 033 * 034 * @author Daniel Gredler 035 */ 036 public class CachingValidatorFactoryImpl implements CachingValidatorFactory { 037 038 private final static Log LOG = LogFactory.getLog( CachingValidatorFactoryImpl.class ); 039 040 private String serviceId; 041 private ValidatorFactory validatorFactory; 042 private Map<BeanProperty, List<Validator>> cache = new HashMap<BeanProperty, List<Validator>>(); 043 044 public void setServiceId( String serviceId ) { 045 this.serviceId = serviceId; 046 } 047 048 public void setValidatorFactory( ValidatorFactory validatorFactory ) { 049 this.validatorFactory = validatorFactory; 050 } 051 052 @SuppressWarnings( "unchecked" ) 053 public List<Validator> constructValidatorList( IComponent component, BeanProperty property ) { 054 List<Validator> validators; 055 synchronized( this.cache ) { 056 validators = this.cache.get( property ); 057 if( validators == null ) { 058 // Build up lists of inherent validators and user-defined validators (defined explicitly or via annotations). 059 List<Validator> inherent = this.getInherentValidators( component, property ); 060 List<Validator> userDefined = this.validatorFactory.constructValidatorList( component, property.getValidators() ); 061 // Combine these validator lists into a single master list. 062 validators = new ArrayList<Validator>(); 063 validators.addAll( inherent ); 064 validators.addAll( userDefined ); 065 // Cache it and we're done! 066 this.cache.put( property, validators ); 067 if( LOG.isDebugEnabled() ) { 068 LOG.debug( "Bean property '" + property.getName() + "' " + 069 "has " + validators.size() + " validators: " + validators ); 070 } 071 } 072 } 073 return validators; 074 } 075 076 public void resetEventDidOccur() { 077 synchronized( this.cache ) { 078 this.cache.clear(); 079 } 080 } 081 082 public void reportStatus( ReportStatusEvent event ) { 083 event.title( this.serviceId ); 084 synchronized( this.cache ) { 085 event.property( "cached validator count", this.cache.size() ); 086 event.collection( "cached validators", this.cache.keySet() ); 087 } 088 } 089 090 /** 091 * Returns validators for the specified property that are inherent to the property type (ie, "this 092 * property is a short, so its minimum value must be {@link Short#MIN_VALUE}"). It's important that 093 * the number validation come before the min/max value validations. 094 */ 095 @SuppressWarnings( "unchecked" ) 096 private List<Validator> getInherentValidators( IComponent component, BeanProperty property ) { 097 098 String number = null; 099 if( property.isNumber() ) { 100 if( property.isFloat() || property.isDouble() ) number = NumberValidator.NAME; 101 else number = WholeNumberValidator.NAME; 102 } 103 104 String minValue; 105 if( property.isShort() ) minValue = String.valueOf( Short.MIN_VALUE ); 106 else if( property.isInteger() ) minValue = String.valueOf( Integer.MIN_VALUE ); 107 else if( property.isLong() ) minValue = String.valueOf( Long.MIN_VALUE ); 108 else if( property.isFloat() ) minValue = String.valueOf( Float.MIN_VALUE ); 109 else if( property.isDouble() ) minValue = String.valueOf( Double.MIN_VALUE ); 110 else minValue = null; 111 112 String maxValue; 113 if( property.isShort() ) maxValue = String.valueOf( Short.MAX_VALUE ); 114 else if( property.isInteger() ) maxValue = String.valueOf( Integer.MAX_VALUE ); 115 else if( property.isLong() ) maxValue = String.valueOf( Long.MAX_VALUE ); 116 else if( property.isFloat() ) maxValue = String.valueOf( Float.MAX_VALUE ); 117 else if( property.isDouble() ) maxValue = String.valueOf( Double.MAX_VALUE ); 118 else maxValue = null; 119 120 StringBuilder sb = new StringBuilder(); 121 if( number != null ) sb.append( number ); 122 if( minValue != null ) sb.append( sb.length() > 0 ? "," : "" ).append( StringMin.NAME ).append( "=" ).append( minValue ); 123 if( maxValue != null ) sb.append( sb.length() > 0 ? "," : "" ).append( StringMax.NAME ).append( "=" ).append( maxValue ); 124 125 String expression = sb.toString(); 126 return this.validatorFactory.constructValidatorList( component, expression ); 127 } 128 129 }