Coverage Report - org.kuali.student.r2.core.class1.process.krms.KRMSProcessEvaluator
 
Classes in this File Line Coverage Branch Coverage Complexity
KRMSProcessEvaluator
0%
0/178
0%
0/70
5.643
 
 1  
 /**
 2  
  * Copyright 2011 The Kuali Foundation Licensed under the
 3  
  * Educational Community License, Version 2.0 (the "License"); you may
 4  
  * not use this file except in compliance with the License. You may
 5  
  * obtain a copy of the License at
 6  
  *
 7  
  * http://www.osedu.org/licenses/ECL-2.0
 8  
  *
 9  
  * Unless required by applicable law or agreed to in writing,
 10  
  * software distributed under the License is distributed on an "AS IS"
 11  
  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 12  
  * or implied. See the License for the specific language governing
 13  
  * permissions and limitations under the License.
 14  
  */
 15  
 package org.kuali.student.r2.core.class1.process.krms;
 16  
 
 17  
 import org.joda.time.DateTime;
 18  
 import org.kuali.rice.krms.api.engine.EngineResults;
 19  
 import org.kuali.rice.krms.api.engine.ExecutionFlag;
 20  
 import org.kuali.rice.krms.api.engine.ExecutionOptions;
 21  
 import org.kuali.rice.krms.api.engine.ResultEvent;
 22  
 import org.kuali.rice.krms.api.engine.SelectionCriteria;
 23  
 import org.kuali.rice.krms.api.engine.TermResolver;
 24  
 import org.kuali.rice.krms.api.repository.LogicalOperator;
 25  
 import org.kuali.rice.krms.framework.engine.Agenda;
 26  
 import org.kuali.rice.krms.framework.engine.AgendaTreeEntry;
 27  
 import org.kuali.rice.krms.framework.engine.BasicAgenda;
 28  
 import org.kuali.rice.krms.framework.engine.BasicAgendaTree;
 29  
 import org.kuali.rice.krms.framework.engine.BasicAgendaTreeEntry;
 30  
 import org.kuali.rice.krms.framework.engine.BasicContext;
 31  
 import org.kuali.rice.krms.framework.engine.BasicRule;
 32  
 import org.kuali.rice.krms.framework.engine.CompoundProposition;
 33  
 import org.kuali.rice.krms.framework.engine.Context;
 34  
 import org.kuali.rice.krms.framework.engine.ContextProvider;
 35  
 import org.kuali.rice.krms.framework.engine.Proposition;
 36  
 import org.kuali.rice.krms.framework.engine.ProviderBasedEngine;
 37  
 import org.kuali.student.common.util.krms.ManualContextProvider;
 38  
 import org.kuali.student.common.util.krms.RulesExecutionConstants;
 39  
 import org.kuali.student.enrollment.acal.dto.TermInfo;
 40  
 import org.kuali.student.enrollment.acal.service.AcademicCalendarService;
 41  
 import org.kuali.student.r2.common.dto.ContextInfo;
 42  
 import org.kuali.student.r2.common.dto.ValidationResultInfo;
 43  
 import org.kuali.student.r2.common.exceptions.DoesNotExistException;
 44  
 import org.kuali.student.r2.common.exceptions.InvalidParameterException;
 45  
 import org.kuali.student.r2.common.exceptions.MissingParameterException;
 46  
 import org.kuali.student.r2.common.exceptions.OperationFailedException;
 47  
 import org.kuali.student.r2.common.exceptions.PermissionDeniedException;
 48  
 import org.kuali.student.r2.common.infc.ValidationResult;
 49  
 import org.kuali.student.r2.common.util.constants.ExemptionServiceConstants;
 50  
 import org.kuali.student.r2.common.util.constants.ProcessServiceConstants;
 51  
 import org.kuali.student.r2.core.class1.process.context.CourseRegistrationProcessContextInfo;
 52  
 import org.kuali.student.r2.core.class1.process.evaluator.ProcessEvaluator;
 53  
 import org.kuali.student.r2.core.class1.process.krms.proposition.ExemptionAwareProposition;
 54  
 import org.kuali.student.r2.core.class1.process.krms.proposition.MilestoneDateComparisonProposition;
 55  
 import org.kuali.student.r2.core.class1.process.krms.proposition.PersonLivingProposition;
 56  
 import org.kuali.student.r2.core.class1.process.krms.proposition.RegistrationHoldProposition;
 57  
 import org.kuali.student.r2.core.class1.process.krms.proposition.SubProcessProposition;
 58  
 import org.kuali.student.r2.core.class1.process.krms.proposition.SummerTermProposition;
 59  
 import org.kuali.student.r2.core.class1.process.krms.proposition.MilestoneDateComparisonProposition.DateComparisonType;
 60  
 import org.kuali.student.r2.core.class1.process.util.InstructionComparator;
 61  
 import org.kuali.student.r2.core.exemption.dto.ExemptionInfo;
 62  
 import org.kuali.student.r2.core.exemption.infc.DateOverride;
 63  
 import org.kuali.student.r2.core.exemption.service.ExemptionService;
 64  
 import org.kuali.student.r2.core.hold.dto.HoldInfo;
 65  
 import org.kuali.student.r2.core.hold.service.HoldService;
 66  
 import org.kuali.student.r2.core.population.service.PopulationService;
 67  
 import org.kuali.student.r2.core.process.dto.CheckInfo;
 68  
 import org.kuali.student.r2.core.process.dto.InstructionInfo;
 69  
 import org.kuali.student.r2.core.process.service.ProcessService;
 70  
 
 71  
 import java.util.ArrayList;
 72  
 import java.util.Arrays;
 73  
 import java.util.Collections;
 74  
 import java.util.Date;
 75  
 import java.util.HashMap;
 76  
 import java.util.LinkedHashMap;
 77  
 import java.util.List;
 78  
 import java.util.Map;
 79  
 
 80  
 /**
 81  
  * Utility class to build up a Proposition tree for to evaluate checks from a Process
 82  
  *
 83  
  * @author alubbers
 84  
  */
 85  0
 public class KRMSProcessEvaluator implements ProcessEvaluator<CourseRegistrationProcessContextInfo> {
 86  
 
 87  
     public static final String EXEMPTION_WAS_USED_MESSAGE_SUFFIX = " (exemption applied)";
 88  
 
 89  
     private AcademicCalendarService acalService;
 90  
     private ProcessService processService;
 91  
     private PopulationService populationService;
 92  
     private ExemptionService exemptionService;
 93  
     private HoldService holdService;
 94  
     private List<TermResolver<?>> termResolvers;
 95  
     private ExecutionOptions executionOptions;
 96  
     private SelectionCriteria selectionCriteria;
 97  
 
 98  
     public void setAcalService(AcademicCalendarService acalService) {
 99  0
         this.acalService = acalService;
 100  0
     }
 101  
 
 102  
     public void setProcessService(ProcessService processService) {
 103  0
         this.processService = processService;
 104  0
     }
 105  
 
 106  
     public void setPopulationService(PopulationService populationService) {
 107  0
         this.populationService = populationService;
 108  0
     }
 109  
 
 110  
     public void setExemptionService(ExemptionService exemptionService) {
 111  0
         this.exemptionService = exemptionService;
 112  0
     }
 113  
 
 114  
     public void setHoldService(HoldService holdService) {
 115  0
         this.holdService = holdService;
 116  0
     }
 117  
 
 118  
     @Override
 119  
     public List<ValidationResultInfo> evaluate(CourseRegistrationProcessContextInfo processContext, ContextInfo context)
 120  
             throws OperationFailedException {
 121  
 
 122  
         List<InstructionInfo> instructions;
 123  
         try {
 124  0
             instructions = processService.getInstructionsByProcess(processContext.getProcessKey(), context);
 125  0
         } catch (OperationFailedException ex) {
 126  0
             throw ex;
 127  0
         } catch (Exception ex) {
 128  0
             throw new OperationFailedException("unexpected", ex);
 129  0
         }
 130  
 
 131  0
         TermInfo term = null;
 132  
 
 133  0
         if (processContext.getTermKey() != null) {
 134  
             try {
 135  0
                 term = acalService.getTerm(processContext.getTermKey(), context);
 136  0
             } catch (OperationFailedException ex) {
 137  0
                 throw ex;
 138  0
             } catch (Exception ex) {
 139  0
                 throw new OperationFailedException("unexpected", ex);
 140  0
             }
 141  
         }
 142  
 
 143  0
         List<InstructionInfo> sortedInstructions = new ArrayList<InstructionInfo>();
 144  
 
 145  
         // filter out Instructions that do apply to the context
 146  0
         for (InstructionInfo instruction : instructions) {
 147  
 
 148  
             // filter out by term
 149  0
             if (term != null && !instruction.getAppliedAtpTypeKeys().isEmpty() && !instruction.getAppliedAtpTypeKeys().contains(term.getTypeKey())) {
 150  0
                 continue;
 151  
             }
 152  
 
 153  
             // filter out by applicable Population
 154  0
             boolean skipInstruction = true;
 155  0
             for (String popKey : instruction.getAppliedPopulationKeys()) {
 156  
                 try {
 157  0
                     if (populationService.isMember(processContext.getStudentId(), popKey, context)) {
 158  0
                         skipInstruction = false;
 159  0
                         break;
 160  
                     }
 161  0
                 } catch (OperationFailedException ex) {
 162  0
                     throw ex;
 163  0
                 } catch (Exception ex) {
 164  0
                     throw new OperationFailedException("unexpected", ex);
 165  0
                 }
 166  
             }
 167  
 
 168  0
             Date asOfDate = context.getCurrentDate();
 169  0
             if (asOfDate == null) {
 170  0
                 asOfDate = new Date ();
 171  
             }
 172  
             // check for any direct exemptions the student may have for this check
 173  
             List<ExemptionInfo> exemptions;
 174  
             try {
 175  0
                 exemptions = exemptionService.getActiveExemptionsByTypeProcessAndCheckForPerson(ExemptionServiceConstants.CHECK_EXEMPTION_TYPE_KEY, 
 176  
                         processContext.getProcessKey(),
 177  
                         instruction.getCheckKey(), 
 178  
                         processContext.getStudentId(), 
 179  
                         asOfDate, 
 180  
                         context);
 181  0
             } catch (OperationFailedException ex) {
 182  0
                 throw ex;
 183  0
             } catch (Exception ex) {
 184  0
                 throw new OperationFailedException("unexpected", ex);
 185  0
             }
 186  0
             if (!exemptions.isEmpty()) {
 187  0
                 skipInstruction = true;
 188  
             }
 189  
 
 190  0
             if (skipInstruction) {
 191  0
                 continue;
 192  
             }
 193  
 
 194  0
             sortedInstructions.add(instruction);
 195  0
         }
 196  
 
 197  0
         Collections.sort(sortedInstructions, new InstructionComparator());
 198  
 
 199  
         // build a list of propositions based on the sorted instructions, using a LinkedHashMap to maintain the same sorting order
 200  0
         Map<Proposition, InstructionInfo> propositions = new LinkedHashMap<Proposition, InstructionInfo>(sortedInstructions.size());
 201  0
         for (InstructionInfo instruction : sortedInstructions) {
 202  
 
 203  
             CheckInfo check;
 204  
             try {
 205  0
                 check = processService.getCheck(instruction.getCheckKey(), context);
 206  0
             } catch (OperationFailedException ex) {
 207  0
                 throw ex;
 208  0
             } catch (Exception ex) {
 209  0
                 throw new OperationFailedException("unexpected", ex);
 210  0
             }
 211  
 
 212  
             // if a sub-process is found,
 213  0
             if (check.getTypeKey().equals(ProcessServiceConstants.PROCESS_CHECK_TYPE_KEY)) {
 214  
 
 215  0
                 CourseRegistrationProcessContextInfo checkContext = CourseRegistrationProcessContextInfo.createForRegistrationEligibility(processContext.getStudentId(), processContext.getTermKey());
 216  0
                 checkContext.setProcessKey(check.getProcessKey());
 217  
 
 218  0
                 propositions.put(new SubProcessProposition(checkContext, this), instruction);
 219  
             }
 220  
 
 221  0
             if (check.getTypeKey().equals(ProcessServiceConstants.HOLD_CHECK_TYPE_KEY)) {
 222  0
                 propositions.put(new RegistrationHoldProposition(check.getIssueId()), instruction);
 223  
 
 224  
                 
 225  
                 /*
 226  
                   need to handle these differently
 227  
                   
 228  
                   } else if (check.getKey().equals(ProcessServiceConstants.CHECK_KEY_IS_ALIVE)) {
 229  
                   propositions.put(new PersonLivingProposition(), instruction);
 230  
                   } else if (check.getKey().equals(ProcessServiceConstants.CHECK_KEY_IS_NOT_SUMMER_TERM)) {
 231  
                   propositions.put(new SummerTermProposition(term), instruction); 
 232  
 
 233  
                 */
 234  
 
 235  0
             } else if (check.getTypeKey().equals(ProcessServiceConstants.START_DATE_CHECK_TYPE_KEY)) {
 236  0
                 propositions.put(buildMilestoneCheckProposition(check, DateComparisonType.AFTER, processContext, context), instruction);
 237  0
             } else if (check.getTypeKey().equals(ProcessServiceConstants.DEADLINE_CHECK_TYPE_KEY)) {
 238  0
                 propositions.put(buildMilestoneCheckProposition(check, DateComparisonType.BEFORE, processContext, context), instruction);
 239  0
             } else if (check.getTypeKey().equals(ProcessServiceConstants.TIME_PERIOD_CHECK_TYPE_KEY)) {
 240  0
                 propositions.put(buildMilestoneCheckProposition(check, DateComparisonType.BETWEEN, processContext, context), instruction);
 241  
             }
 242  0
         }
 243  
 
 244  
         // if all instructions are skipped, and no propositions were generated, return one "success" message
 245  0
         if(propositions.isEmpty()) {
 246  0
             ValidationResultInfo success = new ValidationResultInfo();
 247  0
             success.setLevel(ValidationResult.ErrorLevel.OK);
 248  0
             return Collections.singletonList(success);
 249  
         }
 250  
 
 251  
         // Build the list of known facts prior to execution
 252  0
         Map<String, Object> executionFacts = buildExecutionFacts(processContext, context);
 253  
 
 254  
         // Combine all propositions into a CompoundProposition and evaluate the results
 255  0
         EngineResults krmsResults = evaluateProposition(new CompoundProposition(LogicalOperator.AND, new ArrayList<Proposition>(propositions.keySet())), executionFacts);
 256  
 
 257  
 
 258  
         List<ValidationResultInfo> results;
 259  
         try {
 260  0
             results = buildValidationResultsFromEngineResults(krmsResults, propositions, context);   
 261  0
         } catch (OperationFailedException ex) {
 262  0
             throw ex;
 263  0
         } catch (Exception ex) {
 264  0
             throw new OperationFailedException("unexpected", ex);
 265  0
         }
 266  
 
 267  0
         return results;
 268  
     }
 269  
 
 270  
     private MilestoneDateComparisonProposition buildMilestoneCheckProposition(CheckInfo check, DateComparisonType comparisonType, CourseRegistrationProcessContextInfo processContext, ContextInfo context)
 271  
             throws OperationFailedException {
 272  
         List<ExemptionInfo> exemptions;
 273  0
         Date asOfDate = context.getCurrentDate();
 274  0
             if (asOfDate == null) {
 275  0
                 asOfDate = new Date ();
 276  
             }
 277  
         try {
 278  0
             exemptions = exemptionService.getActiveExemptionsByTypeProcessAndCheckForPerson(ExemptionServiceConstants.MILESTONE_DATE_EXEMPTION_TYPE_KEY, 
 279  
                     processContext.getProcessKey(), 
 280  
                     check.getId(), 
 281  
                     processContext.getStudentId(),
 282  
                     asOfDate,
 283  
                     context);
 284  0
         } catch (OperationFailedException ex) {
 285  0
             throw ex;
 286  0
         } catch (Exception ex) {
 287  0
             throw new OperationFailedException("unexpected", ex);
 288  0
         }
 289  
 
 290  0
         if (exemptions.isEmpty()) {
 291  0
             return new MilestoneDateComparisonProposition(RulesExecutionConstants.CURRENT_DATE_TERM_NAME, comparisonType, check.getMilestoneTypeKey(), processContext.getTermKey(), true, null);
 292  
         }
 293  
 
 294  
         // For now, assume there is only one active Exemption
 295  0
         DateOverride dateOverrideInfo = exemptions.get(0).getDateOverride();
 296  
 
 297  0
         return new MilestoneDateComparisonProposition(RulesExecutionConstants.CURRENT_DATE_TERM_NAME, comparisonType, check.getMilestoneTypeKey(), processContext.getTermKey(), true, dateOverrideInfo);
 298  
     }
 299  
 
 300  
     private EngineResults evaluateProposition(Proposition proposition, Map<String, Object> executionFacts) {
 301  
 
 302  
         // Build the KRMS agenda and other startup objects to execute
 303  0
         List<AgendaTreeEntry> treeEntries = new ArrayList<AgendaTreeEntry>(1);
 304  0
         treeEntries.add(new BasicAgendaTreeEntry(new BasicRule(proposition, null)));
 305  
 
 306  0
         Map<String, String> qualifiers = Collections.emptyMap();
 307  0
         Agenda agenda = new BasicAgenda(qualifiers, new BasicAgendaTree(treeEntries));
 308  
 
 309  0
         Context context = new BasicContext(Arrays.asList(agenda), termResolvers);
 310  0
         ContextProvider contextProvider = new ManualContextProvider(context);
 311  
 
 312  0
         ProviderBasedEngine engine = new ProviderBasedEngine();
 313  0
         engine.setContextProvider(contextProvider);
 314  
 
 315  0
         if (executionOptions == null) {
 316  0
             executionOptions = new ExecutionOptions();
 317  0
             executionOptions.setFlag(ExecutionFlag.LOG_EXECUTION, true);
 318  0
             executionOptions.setFlag(ExecutionFlag.EVALUATE_ALL_PROPOSITIONS, true);
 319  
         }
 320  
 
 321  0
         if (selectionCriteria == null) {
 322  0
             Map<String, String> contextQualifiers = Collections.singletonMap(RulesExecutionConstants.DOCTYPE_CONTEXT_QUALIFIER, RulesExecutionConstants.STUDENT_ELIGIBILITY_DOCTYPE);
 323  
 
 324  0
             Map<String, String> empty = Collections.emptyMap();
 325  0
             selectionCriteria = SelectionCriteria.createCriteria(new DateTime(), contextQualifiers, empty);
 326  
         }
 327  
 
 328  0
         return engine.execute(selectionCriteria, executionFacts, executionOptions);
 329  
     }
 330  
 
 331  
     private List<ValidationResultInfo> buildValidationResultsFromEngineResults(EngineResults engineResults, Map<Proposition, InstructionInfo> propositionInstructionMap, ContextInfo context) throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException {
 332  0
         List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 333  
 
 334  
         // go through all the results from the Propositions, and build validation results based on any propositions that failed
 335  0
         List<ResultEvent> events = engineResults.getResultsOfType(ResultEvent.PROPOSITION_EVALUATED);
 336  0
         for (ResultEvent e : events) {
 337  0
             Proposition prop = (Proposition) e.getSource();
 338  0
             InstructionInfo instruction = propositionInstructionMap.get(prop);
 339  0
             ValidationResultInfo result = new ValidationResultInfo();
 340  0
             String message = instruction.getMessage().getPlain();
 341  0
             ExemptionAwareProposition exemptionProp = null;
 342  
 
 343  0
             if(prop instanceof SubProcessProposition) {
 344  0
                 List<ValidationResultInfo> subResults = (List<ValidationResultInfo>) e.getResultDetails().get(RulesExecutionConstants.SUBPROCESS_EVALUATION_RESULTS);
 345  0
                 results.addAll(subResults);
 346  0
             }
 347  
             else {
 348  
                 // if the proposition is could have an exemption, check for the exemption and add a suffix to the message
 349  0
                 if(prop instanceof ExemptionAwareProposition) {
 350  0
                     exemptionProp = (ExemptionAwareProposition)prop;
 351  0
                     if(exemptionProp.isExemptionUsed()) {
 352  0
                         message += EXEMPTION_WAS_USED_MESSAGE_SUFFIX;
 353  
                     }
 354  
                 }
 355  0
                 if (e.getResult()) {
 356  0
                     result.setLevel(ValidationResult.ErrorLevel.OK);
 357  
                     // add a message to an OK result only if an exemption was used
 358  0
                     if(exemptionProp != null && exemptionProp.isExemptionUsed()) {
 359  0
                         result.setMessage(message);
 360  
                     }
 361  
                 }
 362  
                 else {
 363  
 
 364  0
                     if (instruction.getIsWarning()) {
 365  0
                         result.setWarn(message);
 366  
                     } else {
 367  0
                         result.setError(message);
 368  
                     }
 369  
                 }
 370  
 
 371  0
                 results.add(result);
 372  
             }
 373  
 
 374  0
             if (!e.getResult() && !instruction.getContinueOnFail()) {
 375  0
                 break;
 376  
             }
 377  0
         }
 378  
 
 379  
         // Now check if there are any warnings from Holds that are marked as warning only
 380  0
         List<String> warningHoldIds = (List<String>) engineResults.getAttribute(RulesExecutionConstants.REGISTRATION_HOLD_WARNINGS_ATTRIBUTE);
 381  
 
 382  0
         if (warningHoldIds != null && !warningHoldIds.isEmpty()) {
 383  0
             for (String holdId : warningHoldIds) {
 384  0
                 HoldInfo hold = holdService.getHold(holdId, context);
 385  0
                 ValidationResultInfo result = new ValidationResultInfo();
 386  0
                 result.setWarn("The following hold was found on the student's account, but set as a warning: " + hold.getDescr().getPlain());
 387  0
                 results.add(result);
 388  0
             }
 389  
         }
 390  
 
 391  0
         return results;
 392  
     }
 393  
 
 394  
     public List<ValidationResultInfo> evaluateStudentAliveRule(InstructionInfo instruction, CourseRegistrationProcessContextInfo processContext, ContextInfo context) throws InvalidParameterException, MissingParameterException, DoesNotExistException, OperationFailedException, PermissionDeniedException {
 395  0
         List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 396  
 
 397  
         // Build the list of known facts prior to execution
 398  0
         Map<String, Object> executionFacts = buildExecutionFacts(processContext, context);
 399  
 
 400  
         // evaluate the results for just the one proposition
 401  0
         Proposition prop = new PersonLivingProposition();
 402  0
         EngineResults engineResults = evaluateProposition(prop, executionFacts);
 403  
 
 404  0
         Map<Proposition, InstructionInfo> propInstructionMap = Collections.singletonMap(prop, instruction);
 405  
 
 406  0
         return buildValidationResultsFromEngineResults(engineResults, propInstructionMap, context);
 407  
     }
 408  
 
 409  
     public List<ValidationResultInfo> evaluateSummerTermRule(InstructionInfo instruction, CourseRegistrationProcessContextInfo processContext, ContextInfo context) throws InvalidParameterException, MissingParameterException, DoesNotExistException, OperationFailedException, PermissionDeniedException {
 410  0
         List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>();
 411  
 
 412  0
         TermInfo term = acalService.getTerm(processContext.getTermKey(), context);
 413  
 
 414  
         // Build the list of known facts prior to execution
 415  0
         Map<String, Object> executionFacts = buildExecutionFacts(processContext, context);
 416  
 
 417  
         // evaluate the results for just the one proposition
 418  0
         Proposition prop = new SummerTermProposition(term);
 419  0
         EngineResults engineResults = evaluateProposition(prop, executionFacts);
 420  
 
 421  0
         Map<Proposition, InstructionInfo> propInstructionMap = Collections.singletonMap(prop, instruction);
 422  
 
 423  0
         return buildValidationResultsFromEngineResults(engineResults, propInstructionMap, context);
 424  
     }
 425  
 
 426  
     private Map<String, Object> buildExecutionFacts(CourseRegistrationProcessContextInfo processContext, ContextInfo context) {
 427  0
         Map<String, Object> executionFacts = new HashMap<String, Object>();
 428  0
         executionFacts.put(RulesExecutionConstants.STUDENT_ID_TERM_NAME, processContext.getStudentId());
 429  0
         executionFacts.put(RulesExecutionConstants.CONTEXT_INFO_TERM_NAME, context);
 430  0
         if (processContext.getTermKey() != null) {
 431  0
             executionFacts.put(RulesExecutionConstants.REGISTRATION_TERM_TERM_NAME, processContext.getTermKey());
 432  
         }
 433  0
         return executionFacts;
 434  
     }
 435  
 
 436  
     public void setTermResolvers(List<TermResolver<?>> termResolvers) {
 437  0
         this.termResolvers = termResolvers;
 438  0
     }
 439  
 
 440  
     public void setExecutionOptions(ExecutionOptions executionOptions) {
 441  0
         this.executionOptions = executionOptions;
 442  0
     }
 443  
 }