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 }