001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
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,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krms.framework.engine;
017    
018    import java.util.ArrayList;
019    import java.util.Collections;
020    import java.util.List;
021    
022    import org.kuali.rice.krms.api.engine.ExecutionEnvironment;
023    import org.kuali.rice.krms.api.engine.ResultEvent;
024    import org.kuali.rice.krms.api.engine.ExecutionFlag;
025    import org.kuali.rice.krms.api.repository.LogicalOperator;
026    import org.kuali.rice.krms.framework.engine.result.BasicResult;
027    
028    /**
029     *
030     * An implementation of {@link Proposition} which holds other Propositions and a {@link LogicalOperator}.
031     *
032     * NOTE - this is a patched version of CompoundProposition which fixes bugs in evaluation "OR"-ed propositions
033     *
034     * @author Kuali Rice Team (rice.collab@kuali.org)
035     */
036    public final class CompoundProposition implements Proposition {
037            
038        private static final ResultLogger LOG = ResultLogger.getInstance();
039        
040            private final LogicalOperator logicalOperator;
041            private final List<Proposition> propositions;
042    
043        /**
044         * Create a CompoundProposition with the given values
045         * @param logicalOperator {@link LogicalOperator} to set logicalOperator to
046         * @param propositions to set the propositions to
047         */
048            public CompoundProposition(LogicalOperator logicalOperator, List<Proposition> propositions) {
049                                    
050                    if (propositions == null || propositions.isEmpty()) {
051                            throw new IllegalArgumentException("Propositions must be non-null and non-empty.");
052                    }
053                    if (logicalOperator == null) {
054                            throw new IllegalArgumentException("Logical operator must be non-null.");
055                    }
056                    this.logicalOperator = logicalOperator;
057                    this.propositions = new ArrayList<Proposition>(propositions);
058            }
059            
060            @Override
061            public PropositionResult evaluate(ExecutionEnvironment environment) {
062                    
063                    PropositionResult result = evaluateInner(environment);
064                    
065                    // handle compound proposition result logging
066                    if (LOG.isEnabled(environment)) { 
067                LOG.logResult(new BasicResult(ResultEvent.PROPOSITION_EVALUATED, this, environment, result.getResult()));
068            }
069                    
070                    return result;
071            }
072    
073        /**
074         * Evaluates then {@link ExecutionEnvironment}
075         *
076         * @param environment {@link ExecutionEnvironment} to use for evaluation
077         * @return PropositionResult {@link PropositionResult} the results of the evaluation
078         * @throws IllegalStateException if the logicalOperator is invalid.
079         */
080            
081        private PropositionResult evaluateInner(ExecutionEnvironment environment) {
082            
083            boolean collatedResult;
084            boolean evaluateAll = environment.getExecutionOptions().getFlag(ExecutionFlag.EVALUATE_ALL_PROPOSITIONS);
085            
086            if (logicalOperator == LogicalOperator.AND) {
087    
088                collatedResult = true;
089    
090                            for (Proposition proposition : propositions) {
091                                    
092                                    PropositionResult singleResult = proposition.evaluate(environment);
093                                    logPropositionResult(proposition, singleResult, environment);
094                                                                    
095                                    if (!singleResult.getResult()) {
096                                            collatedResult = false;
097                                            if(!evaluateAll) break;
098                                    }
099                            }
100                            
101                            return new PropositionResult(collatedResult);
102                            
103                    } else if (logicalOperator == LogicalOperator.OR) {
104                            
105                        collatedResult = false;
106                            
107                            for (Proposition proposition : propositions) {
108                                    
109                                PropositionResult singleResult = proposition.evaluate(environment);
110                                    logPropositionResult(proposition, singleResult, environment);
111                                    
112                                    if (singleResult.getResult()) {
113                                            collatedResult = true;
114                                            if(!evaluateAll) break;
115                                    }
116                            }
117                            
118                            return new PropositionResult(collatedResult);
119                    }
120                    throw new IllegalStateException("Invalid logical operator: " + logicalOperator);
121        }
122        
123        /*
124         * Logs only if the proposition is not compound
125         * and have the compound proposition log its own result
126         * @param proposition {@link Proposition} to log.  Compound Propositions will not log.
127         * @param propositionResult {@link PropositionResult} to log the result and execution details of
128         * @param environment {@link ExecutionEnvironment} to log
129         */
130        
131        public void logPropositionResult(Proposition proposition, PropositionResult propositionResult, ExecutionEnvironment environment) {
132                    
133            if(!proposition.isCompound()) {
134                LOG.logResult(new BasicResult(propositionResult.getExecutionDetails(), ResultEvent.PROPOSITION_EVALUATED, proposition, environment, propositionResult.getResult()));
135            }
136            
137        }
138    
139        /**
140         * Returns an unmodifiableList of {@link Proposition}s.
141         * @return an unmodifiableList of {@link Proposition}s
142         */
143        @Override
144        public List<Proposition> getChildren() {
145            return Collections.unmodifiableList(propositions);
146        }
147        
148        @Override
149        public boolean isCompound() {
150            return true;
151        }
152    
153    }