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.rules.support;
017    
018    import java.util.HashMap;
019    import java.util.Iterator;
020    import java.util.List;
021    import java.util.Map;
022    
023    import org.apache.commons.logging.Log;
024    import org.apache.commons.logging.LogFactory;
025    import org.springframework.core.style.ToStringCreator;
026    import org.springframework.richclient.util.ClassUtils;
027    import org.springframework.rules.Rules;
028    import org.springframework.rules.RulesSource;
029    import org.springframework.rules.constraint.ConstraintsAccessor;
030    import org.springframework.rules.constraint.property.PropertyConstraint;
031    import org.springframework.util.Assert;
032    import org.springframework.util.CachingMapDecorator;
033    import org.springframework.util.StringUtils;
034    
035    /**
036     * A default rules source implementation which is simply a in-memory registry for bean validation rules backed by a map.
037     * 
038     * @author Keith Donald
039     */
040    public class DefaultRulesSource extends ConstraintsAccessor implements RulesSource {
041        protected final Log logger = LogFactory.getLog(getClass());
042    
043        private static final String DEFAULT_CONTEXT_ID = "default";
044    
045        private Map ruleContexts = new CachingMapDecorator() {
046            protected Object create(Object key) {
047                return new HashMap();
048            }
049        };
050    
051        /**
052         * Add or update the rules for a single bean class.
053         * 
054         * @param rules
055         *            The rules.
056         */
057        public void addRules(Rules rules) {
058            if (logger.isDebugEnabled()) {
059                logger.debug("Adding rules -> " + rules);
060            }
061            addRules(DEFAULT_CONTEXT_ID, rules);
062        }
063    
064        public void addRules(String contextId, Rules rules) {
065            Assert.notNull(contextId);
066            Assert.notNull(rules);
067            Map context = getRuleContext(contextId);
068            context.put(rules.getDomainObjectType(), rules);
069        }
070    
071        private Map getRuleContext(String contextId) {
072            return (Map) ruleContexts.get(contextId);
073        }
074    
075        /**
076         * Set the list of rules retrievable by this source, where each item in the list is a <code>Rules</code> object
077         * which maintains validation rules for a bean class.
078         * 
079         * @param rules
080         *            The list of rules.
081         */
082        public void setRules(List rules) {
083            Assert.notNull(rules);
084            if (logger.isDebugEnabled()) {
085                logger.debug("Configuring rules in source...");
086            }
087            getRuleContext(DEFAULT_CONTEXT_ID).clear();
088            for (Iterator i = rules.iterator(); i.hasNext();) {
089                addRules((Rules) i.next());
090            }
091        }
092    
093        public Rules getRules(Class bean) {
094            return getRules(bean, DEFAULT_CONTEXT_ID);
095        }
096    
097        public Rules getRules(Class beanType, String contextId) {
098            Assert.notNull(beanType);
099            if (!StringUtils.hasText(contextId)) {
100                contextId = DEFAULT_CONTEXT_ID;
101            }
102            return (Rules) ClassUtils.getValueFromMapForClass(beanType, getRuleContext(contextId));
103        }
104    
105        public PropertyConstraint getPropertyConstraint(Class bean, String propertyName) {
106            return getPropertyConstraint(bean, propertyName, DEFAULT_CONTEXT_ID);
107        }
108    
109        public PropertyConstraint getPropertyConstraint(Class bean, String propertyName, String contextId) {
110            if (logger.isDebugEnabled()) {
111                logger.debug("Retrieving rules for bean '" + bean + "', context = " + contextId + ", property '"
112                        + propertyName + "'");
113            }
114            Rules rules = getRules(bean, contextId);
115            if (rules != null)
116                return rules.getPropertyConstraint(propertyName);
117    
118            return null;
119        }
120    
121        public String toString() {
122            return new ToStringCreator(this).append("rules", ruleContexts).toString();
123        }
124    
125    }