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.Collections;
019    import java.util.HashMap;
020    import java.util.Map;
021    import java.util.Map.Entry;
022    import java.util.Set;
023    import java.util.HashSet;
024    
025    import org.apache.commons.lang.ObjectUtils;
026    import org.kuali.rice.krms.api.engine.EngineResults;
027    import org.kuali.rice.krms.api.engine.ExecutionEnvironment;
028    import org.kuali.rice.krms.api.engine.ExecutionOptions;
029    import org.kuali.rice.krms.api.engine.SelectionCriteria;
030    import org.kuali.rice.krms.api.engine.Term;
031    import org.kuali.rice.krms.api.engine.TermResolutionEngine;
032    import org.kuali.rice.krms.api.engine.TermResolutionException;
033    import org.kuali.rice.krms.api.engine.TermResolver;
034    import 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     */
040    public 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    }