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    }