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 */
016package org.kuali.rice.krms.framework.engine;
017
018import org.kuali.rice.krms.api.engine.ExecutionEnvironment;
019import org.kuali.rice.krms.api.engine.ExecutionFlag;
020import org.kuali.rice.krms.api.engine.ResultEvent;
021import org.kuali.rice.krms.api.repository.LogicalOperator;
022import org.kuali.rice.krms.framework.engine.result.BasicResult;
023
024import java.util.ArrayList;
025import java.util.Collections;
026import java.util.List;
027
028/**
029 *
030 * An implementation of {@link org.kuali.rice.krms.framework.engine.Proposition} which holds other Propositions and a {@link org.kuali.rice.krms.api.repository.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 */
036public 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 org.kuali.rice.krms.api.repository.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 org.kuali.rice.krms.api.engine.ExecutionEnvironment}
075     *
076     * @param environment {@link org.kuali.rice.krms.api.engine.ExecutionEnvironment} to use for evaluation
077     * @return PropositionResult {@link org.kuali.rice.krms.framework.engine.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 org.kuali.rice.krms.framework.engine.Proposition}s.
141     * @return an unmodifiableList of {@link org.kuali.rice.krms.framework.engine.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}