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.reporting;
017    
018    import java.util.Iterator;
019    
020    import org.apache.commons.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import org.springframework.core.ReflectiveVisitorHelper;
023    import org.springframework.rules.closure.Closure;
024    import org.springframework.rules.constraint.Constraint;
025    import org.springframework.core.style.ToStringCreator;
026    import org.springframework.rules.constraint.And;
027    import org.springframework.rules.constraint.ClosureResultConstraint;
028    import org.springframework.rules.constraint.Not;
029    import org.springframework.rules.constraint.Or;
030    
031    /**
032     * @author Keith Donald
033     */
034    public class ValidationResultsCollector {
035        protected static final Log logger = LogFactory
036                .getLog(ValidationResultsCollector.class);
037    
038        protected ReflectiveVisitorHelper visitorSupport = new ReflectiveVisitorHelper();
039    
040        private ValidationResultsBuilder resultsBuilder;
041    
042        private ValidationResults results;
043    
044        private boolean collectAllErrors;
045    
046        private Object argument;
047    
048        public ValidationResultsCollector() {
049        }
050    
051        public ValidationResults collect(final Object argument,
052                final Constraint constraint) {
053            this.resultsBuilder = new ValidationResultsBuilder() {
054                public void constraintSatisfied() {
055                }
056    
057                public void constraintViolated(Constraint constraint) {
058                    results = new ValueValidationResults(argument, constraint);
059                }
060            };
061            if (results == null) {
062                results = new ValueValidationResults(argument);
063            }
064            this.argument = argument;
065            visitorSupport.invokeVisit(this, constraint);
066            return results;
067        }
068    
069        public void setCollectAllErrors(boolean collectAllErrors) {
070            this.collectAllErrors = collectAllErrors;
071        }
072    
073        protected ValidationResultsBuilder getResultsBuilder() {
074            return resultsBuilder;
075        }
076        
077        protected void setResultsBuilder(ValidationResultsBuilder resultsBuilder) {
078            this.resultsBuilder = resultsBuilder;
079        }
080    
081        protected void setArgument(Object argument) {
082            this.argument = argument;
083        }
084    
085        boolean visit(And and) {
086            resultsBuilder.pushAnd();
087            if (logger.isDebugEnabled()) {
088                logger.debug("Starting [and]...");
089            }
090            boolean result = true;
091            Iterator it = and.iterator();
092            while (it.hasNext()) {
093                boolean test = ((Boolean)visitorSupport.invokeVisit(
094                        ValidationResultsCollector.this, it.next())).booleanValue();
095                if (!test) {
096                    if (!collectAllErrors) {
097                        resultsBuilder.pop(false);
098                        return false;
099                    }
100    
101                    if (result) {
102                        result = false;
103                    }
104                }
105            }
106            if (logger.isDebugEnabled()) {
107                logger.debug("Finished [and]...");
108            }
109            resultsBuilder.pop(result);
110            return result;
111        }
112    
113        boolean visit(Or or) {
114            resultsBuilder.pushOr();
115            if (logger.isDebugEnabled()) {
116                logger.debug("Starting [or]...");
117            }
118            Iterator it = or.iterator();
119            while (it.hasNext()) {
120                boolean result = ((Boolean)visitorSupport.invokeVisit(
121                        ValidationResultsCollector.this, it.next())).booleanValue();
122                if (result) {
123                    resultsBuilder.pop(result);
124                    return true;
125                }
126            }
127            if (logger.isDebugEnabled()) {
128                logger.debug("Finished [or]...");
129            }
130            resultsBuilder.pop(false);
131            return false;
132        }
133    
134        Boolean visit(Not not) {
135            resultsBuilder.pushNot();
136            if (logger.isDebugEnabled()) {
137                logger.debug("Starting [not]...");
138            }
139            Boolean result = (Boolean)visitorSupport.invokeVisit(this, not
140                    .getConstraint());
141            if (logger.isDebugEnabled()) {
142                logger.debug("Finished [not]...");
143            }
144            resultsBuilder.pop(result.booleanValue());
145            return result;
146        }
147    
148        Boolean visit(ClosureResultConstraint ofConstraint) {
149            Closure f = ofConstraint.getFunction();
150            if (logger.isDebugEnabled()) {
151                logger.debug("Invoking function with argument " + argument);
152            }
153            setArgument(f.call(argument));
154            return (Boolean)visitorSupport.invokeVisit(this, ofConstraint
155                    .getPredicate());
156        }
157    
158        boolean visit(Constraint constraint) {
159            if (logger.isDebugEnabled()) {
160                logger.debug("Testing constraint [" + constraint + "] with argument '"
161                        + argument + "']");
162            }
163            boolean result = constraint.test(argument);
164            result = applyAnyNegation(result);
165            if (!result) {
166                resultsBuilder.push(constraint);
167            }
168            if (logger.isDebugEnabled()) {
169                logger.debug("Constraint [" + constraint + "] "
170                        + (result ? "passed" : "failed"));
171            }
172            return result;
173        }
174    
175        protected boolean applyAnyNegation(boolean result) {
176            boolean negated = resultsBuilder.negated();
177            if (logger.isDebugEnabled()) {
178                if (negated) {
179                    logger.debug("[negate result]");
180                }
181                else {
182                    logger.debug("[no negation]");
183                }
184            }
185            return negated ? !result : result;
186        }
187    
188        public String toString() {
189            return new ToStringCreator(this).append("collectAllErrors",
190                    collectAllErrors).append("validationResultsBuilder",
191                    resultsBuilder).toString();
192        }
193    
194    }