001/**
002 * Copyright 2005-2016 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 java.util.Collections;
019import java.util.HashMap;
020import java.util.Map;
021import java.util.Map.Entry;
022import java.util.Set;
023import java.util.HashSet;
024
025import org.apache.commons.lang.ObjectUtils;
026import org.kuali.rice.krms.api.engine.EngineResults;
027import org.kuali.rice.krms.api.engine.ExecutionEnvironment;
028import org.kuali.rice.krms.api.engine.ExecutionOptions;
029import org.kuali.rice.krms.api.engine.SelectionCriteria;
030import org.kuali.rice.krms.api.engine.Term;
031import org.kuali.rice.krms.api.engine.TermResolutionEngine;
032import org.kuali.rice.krms.api.engine.TermResolutionException;
033import org.kuali.rice.krms.api.engine.TermResolver;
034import org.kuali.rice.krms.api.engine.ResultEvent;
035
036/**
037 * An implementation of {@link ExecutionEnvironment} given {@link SelectionCriteria}, facts (Map<{@link Term}, Object> ), {@link ExecutionOptions} and {@link TermResolutionEngine}.
038 * @author Kuali Rice Team (rice.collab@kuali.org)
039 */
040public final class BasicExecutionEnvironment implements ExecutionEnvironment {
041
042        private final SelectionCriteria selectionCriteria;
043        private final Map<Term, Object> facts;
044        private final ExecutionOptions executionOptions;
045        private final EngineResults engineResults;
046        private final TermResolutionEngine termResolutionEngine;
047        private Map<Object, Set<Term>> termPropositionMap;
048
049    /**
050     * Constructor for a BasicExecutionEnvironment with the given {@link SelectionCriteria}, facts, {@link ExecutionOptions} and {@link TermResolutionEngine}
051     * @param selectionCriteria to set selectionCriteria to, cannot be null
052     * @param facts to set facts to, cannot be null
053     * @param executionOptions to set executionOptions to
054     * @param termResolutionEngine to set termResolutionEngine to
055     * @throws IllegalArgumentException if the selectionCriteria or facts are null
056     */
057        public BasicExecutionEnvironment(SelectionCriteria selectionCriteria, Map<Term, Object> facts, ExecutionOptions executionOptions, TermResolutionEngine termResolutionEngine) {
058                if (selectionCriteria == null) {
059                        throw new IllegalArgumentException("Selection criteria must not be null.");
060                }
061                if (facts == null) {
062                        throw new IllegalArgumentException("Facts must not be null.");
063                }
064                this.selectionCriteria = selectionCriteria;
065                this.executionOptions = new ExecutionOptions(executionOptions);
066                this.engineResults = new EngineResultsImpl();
067                                
068                this.termResolutionEngine = new TermResolutionEngineImpl();
069                
070                // Add facts
071                this.facts = new HashMap<Term, Object>(facts.size());
072                this.facts.putAll(facts);
073                
074                for (Entry<Term, Object> factsEntry : facts.entrySet()) {
075                        this.termResolutionEngine.addTermValue(factsEntry.getKey(), factsEntry.getValue());
076                }
077        }
078        
079        @Override
080        public SelectionCriteria getSelectionCriteria() {
081                return this.selectionCriteria;
082        }
083        
084        @Override
085        public Map<Term, Object> getFacts() {
086                return Collections.unmodifiableMap(facts);
087        }
088        
089        @Override
090        public void addTermResolver(TermResolver<?> termResolver) {
091                termResolutionEngine.addTermResolver(termResolver);
092        }
093        
094        @Override
095        public <T> T resolveTerm(Term term, Object caller) throws TermResolutionException {
096                T value;
097                
098                // This looks funny, but works around a javac bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954
099                // Specifically, using <T> below works around it.
100                value = termResolutionEngine.<T>resolveTerm(term);
101                
102                if (caller != null) {
103                    if(termPropositionMap == null) {
104                        termPropositionMap = new HashMap<Object, Set<Term>>();
105                    }
106
107                    // update the Proposition-Term mapping
108                    if(termPropositionMap.containsKey(caller)) {
109                        termPropositionMap.get(caller).add(term);
110                    } else {
111                        termPropositionMap.put(caller, new HashSet<Term>());
112                        termPropositionMap.get(caller).add(term);
113                    }
114                }
115                
116                publishFact(term, value);               
117                
118                return value;
119        }
120
121    @Override
122        public Set<Term> getTermsForCaller(Object caller) {
123                return termPropositionMap.get(caller);
124        }
125        
126        @Override
127        public boolean publishFact(Term factName, Object factValue) {
128                if (facts.containsKey(factName) && ObjectUtils.equals(facts.get(factName), factValue)) {
129                        return false;
130                }
131                facts.put(factName, factValue);
132                termResolutionEngine.addTermValue(factName, factValue);
133                return true;
134        }
135
136        @Override
137        public ExecutionOptions getExecutionOptions() {
138                return executionOptions;
139        }
140        
141        @Override
142        public EngineResults getEngineResults() {
143                return engineResults;
144        }
145        
146
147}