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 }