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; 017 018 import java.util.ArrayList; 019 import java.util.HashMap; 020 import java.util.Iterator; 021 import java.util.List; 022 import java.util.Map; 023 024 import org.apache.commons.logging.Log; 025 import org.apache.commons.logging.LogFactory; 026 import org.springframework.beans.factory.InitializingBean; 027 import org.springframework.rules.constraint.Constraint; 028 import org.springframework.core.style.ToStringCreator; 029 import org.springframework.rules.constraint.*; 030 import org.springframework.rules.constraint.property.CompoundPropertyConstraint; 031 import org.springframework.rules.constraint.property.PropertyConstraint; 032 import org.springframework.rules.constraint.property.PropertyValueConstraint; 033 import org.springframework.util.Assert; 034 import org.springframework.validation.Errors; 035 import org.springframework.validation.Validator; 036 037 /** 038 * A factory for creating rules. 039 * 040 * @author Keith Donald 041 */ 042 public class Rules extends ConstraintsAccessor implements Constraint, PropertyConstraintProvider, Validator, 043 InitializingBean { 044 private static final Log logger = LogFactory.getLog(Rules.class); 045 046 private Class domainObjectType; 047 048 /** All constraints keyed by property name. */ 049 private Map propertiesConstraints = new HashMap(); 050 051 /** Used to track the order in which rules are added so they can be evaluated 052 * in that same sequence. 053 */ 054 private List orderedConstraints = new ArrayList(); 055 056 public Rules() { 057 058 } 059 060 public Rules(Class domainObjectClass) { 061 setDomainObjectType(domainObjectClass); 062 } 063 064 public Rules(Class domainObjectClass, Map propertiesConstraints) { 065 setDomainObjectType(domainObjectClass); 066 setPropertiesConstraints(propertiesConstraints); 067 } 068 069 public void setDomainObjectType(Class domainObjectType) { 070 Assert.notNull(domainObjectType, "The domainObjectType property is required"); 071 this.domainObjectType = domainObjectType; 072 } 073 074 public Class getDomainObjectType() { 075 return domainObjectType; 076 } 077 078 public void afterPropertiesSet() { 079 initRules(); 080 } 081 082 protected void initRules() { 083 084 } 085 086 public void setPropertiesConstraints(Map propertiesConstraints) { 087 for (Iterator i = propertiesConstraints.entrySet().iterator(); i.hasNext();) { 088 Map.Entry entry = (Map.Entry)i.next(); 089 String propertyName = (String)entry.getKey(); 090 Object value = entry.getValue(); 091 if (value instanceof List) { 092 add(propertyName, (Constraint[])((List)value).toArray(new Constraint[0])); 093 } 094 else if (value instanceof PropertyConstraint) { 095 add((PropertyConstraint)value); 096 } 097 else if (value instanceof Constraint) { 098 add(propertyName, (Constraint)value); 099 } 100 } 101 } 102 103 /** 104 * Put a constraint into the collection. Store it in the map under the property 105 * name and add it to the ordered list. 106 * 107 * @param constraint to add 108 */ 109 private void putPropertyConstraint(PropertyConstraint constraint) { 110 And and = new And(); 111 and.add(constraint); 112 if (logger.isDebugEnabled()) { 113 logger.debug("Putting constraint for property '" + constraint.getPropertyName() + "', constraint -> [" 114 + constraint + "]"); 115 } 116 PropertyConstraint compoundConstraint = new CompoundPropertyConstraint(and); 117 propertiesConstraints.put(constraint.getPropertyName(), compoundConstraint); 118 orderedConstraints.add( compoundConstraint ); 119 } 120 121 public PropertyConstraint getPropertyConstraint(String property) { 122 if (propertiesConstraints.isEmpty()) { 123 initRules(); 124 } 125 return (PropertyConstraint)propertiesConstraints.get(property); 126 } 127 128 public Iterator iterator() { 129 if (orderedConstraints.isEmpty()) { 130 initRules(); 131 } 132 return orderedConstraints.iterator(); 133 } 134 135 /** 136 * Adds the provided bean property expression (constraint) to the list of 137 * constraints for the constrained property. 138 * 139 * @param constraint 140 * the bean property expression 141 * @return this, to support chaining. 142 */ 143 public Rules add(PropertyConstraint constraint) { 144 CompoundPropertyConstraint and = (CompoundPropertyConstraint)propertiesConstraints.get(constraint 145 .getPropertyName()); 146 if (and == null) { 147 putPropertyConstraint(constraint); 148 } 149 else { 150 and.add(constraint); 151 } 152 return this; 153 } 154 155 /** 156 * Adds a value constraint for the specified property. 157 * 158 * @param propertyName 159 * The property name. 160 * @param valueConstraint 161 * The value constraint. 162 */ 163 public void add(String propertyName, Constraint valueConstraint) { 164 add(new PropertyValueConstraint(propertyName, valueConstraint)); 165 } 166 167 /** 168 * Adds a value constraint for the specified property. 169 * 170 * @param propertyName 171 * The property name. 172 * @param valueConstraint 173 * The value constraint. 174 */ 175 public void add(String propertyName, Constraint[] valueConstraints) { 176 add(new PropertyValueConstraint(propertyName, all(valueConstraints))); 177 } 178 179 public void addRequired(String propertyName) { 180 add(propertyName, required()); 181 } 182 183 public void addRequired(String propertyName, Constraint otherConstraints) { 184 add(propertyName, and(required(), otherConstraints)); 185 } 186 187 public void addMaxLength(String propertyName, int maxLength) { 188 add(propertyName, maxLength(maxLength)); 189 } 190 191 public void addMinLength(String propertyName, int minLength) { 192 add(propertyName, minLength(minLength)); 193 } 194 195 public void addRange(String propertyName, Range range) { 196 add(propertyName, range); 197 } 198 199 /** 200 * Adds the provided compound predicate, composed of BeanPropertyExpression 201 * objects, as a bean property constraint. 202 * 203 * @param compoundPredicate 204 */ 205 public void add(CompoundConstraint compoundPredicate) { 206 add(new CompoundPropertyConstraint(compoundPredicate)); 207 } 208 209 public boolean test(Object bean) { 210 for (Iterator i = orderedConstraints.iterator(); i.hasNext();) { 211 PropertyConstraint propertyConstraint = (PropertyConstraint)i.next(); 212 if (!propertyConstraint.test(bean)) { 213 return false; 214 } 215 } 216 return true; 217 } 218 219 public boolean supports(Class type) { 220 return this.domainObjectType.isAssignableFrom(type); 221 } 222 223 public void validate(final Object bean, final Errors errors) { 224 225 } 226 227 public String toString() { 228 return new ToStringCreator(this).append("domainObjectClass", domainObjectType).append("propertyRules", 229 propertiesConstraints).toString(); 230 } 231 232 }