View Javadoc

1   /* ===================================================================
2    * Copyright 2002-04 SRI International.
3    * Released under the MOZILLA PUBLIC LICENSE Version 1.1
4    * which can be obtained at http://www.mozilla.org/MPL/MPL-1.1.html
5    *
6    * This program is distributed in the hope that it will be useful, but
7    * WITHOUT ANY WARRANTY; without even the implied warranty of
8    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9    * See the MOZILLA PUBLIC LICENSE for more details.
10   * =================================================================== */
11  
12  package com.sri.emo.controller;
13  
14  import com.jcorporate.expresso.core.dataobjects.jdbc.JoinedDataObject;
15  import com.jcorporate.expresso.core.db.DBException;
16  import com.sri.emo.dbobj.WizDecisionEntities;
17  import com.sri.emo.dbobj.WizDecisionSet;
18  
19  import java.util.*;
20  
21  /***
22   * Class that allows for execution of the decision matrix.  It pre-parses
23   * the database objects so that when getDecision() is called, it is
24   * much faster.
25   * <p><b>Performance Note:</b>  Working with the Decision Matrix is not
26   * a simple task since very few things are constant.  Picking a result from
27   * a decision matrix requires a lot of set manipulation or database
28   * reads.  For performance, this implementation goes with high memory usage
29   * and few database reads.  CPU-wise, it is an O(n^2) algorithm, so it may
30   * not scale over large models.</p>
31   * <p>Memory optimization is possible, but has been not performed for
32   * construction speed's sake at this point in time.</p>
33   *
34   * @author Michael Rimov
35   */
36  public class DecisionMatrix {
37      /***
38       * This is a set of sets.
39       */
40      private final Map allItems;
41  
42      /***
43       * Constructor that takes a wizard id as an argument.  The constructor
44       * takes quite a bit of pre-processing work, executing a joined statement
45       * and parsing the results into the <code>allItems</code> map.
46       *
47       * @param wizardId the wizard id.
48       * @throws DBException upon database error.
49       */
50      public DecisionMatrix(final String wizardId) throws DBException {
51          this(Integer.parseInt(wizardId));
52      }
53  
54      /***
55       * Constructor that takes a wizard id as an argument.  The constructor
56       * takes quite a bit of pre-processing work, executing a joined statement
57       * and parsing the results into the <code>allItems</code> map.
58       *
59       * @param wizardId int the wizard id.
60       * @throws DBException upon database error.
61       */
62      public DecisionMatrix(final int wizardId) throws DBException {
63          if (wizardId < 0) {
64              throw new IllegalArgumentException("Need to have positive id number");
65          }
66  
67          JoinedDataObject join = new JoinedDataObject();
68          join.setDefinitionName("/com/sri/emo/dbobj/DecisionMatrixContentsJoin.xml");
69          join.set("DecisionSet.WizId", Integer.toString(wizardId));
70          List results = join.searchAndRetrieveList("DecisionSet.Id");
71  
72          allItems = new HashMap(results.size());
73          for (int i = 0; i < results.size(); i++) {
74              JoinedDataObject oneObj = (JoinedDataObject) results.get(i);
75              List theDataObjects = oneObj.getDataObjects();
76              assert theDataObjects.size() == 2;
77              WizDecisionSet theSet = (WizDecisionSet) theDataObjects.get(0);
78              WizDecisionEntities theEntity = (WizDecisionEntities) theDataObjects.get(1);
79              if (!allItems.containsKey(theSet)) {
80                  allItems.put(theSet, new HashSet());
81              }
82  
83              Set curSet = (Set) allItems.get(theSet);
84              curSet.add(theEntity);
85          }
86      }
87  
88      /***
89       * Retrieves the decision.  Retrieves a WizDecisionMatrix instance or
90       * null if no node matches the decision specified.
91       *
92       * @param decisionRow Map that has a key of the Wizard Step Id (as Integer),
93       *                    and the value as the picked key value from the picklist (or attribute). (Also
94       *                    as Integer)
95       * @return WizDecisionMatrix or null if there are no matches.
96       * @throws DBException upon database access error.
97       */
98      public WizDecisionSet getDecision(final Map decisionRow) throws DBException {
99          Map workingSet = new HashMap(allItems);
100 
101         for (Iterator i = decisionRow.keySet().iterator(); (i.hasNext() && workingSet.size() > 0);) {
102             Integer stepId = (Integer) i.next();
103             Integer value = (Integer) decisionRow.get(stepId);
104             workingSet = prunePossibilities(workingSet,
105                     stepId.intValue(),
106                     value.intValue());
107         }
108 
109         //This is the case for a blank wizard.  We will never go into
110         //the loop above.
111         if (workingSet.size() == 0) {
112             return null;
113         }
114 
115         //At this point we were SUPPOSED to have things narrowed down
116         //to one item.
117         assert workingSet.size() == 1;
118         return (WizDecisionSet) workingSet.keySet().iterator().next();
119     }
120 
121 
122     /***
123      * Iterates through the sets and removes all items that do not match
124      * the step id and picklist value.
125      *
126      * @param workingSet    Map the working set that we keep pruning with each call
127      * @param stepId        int the step id we're working with.
128      * @param picklistValue int the picklist value used for the step id.
129      * @return Map A pruned version of the working set.
130      * @throws DBException upon database error.
131      */
132     private Map prunePossibilities(final Map workingSet, final int stepId, final int picklistValue) throws DBException {
133         Map returnValue = new HashMap(workingSet);
134         for (Iterator i = workingSet.keySet().iterator(); i.hasNext();) {
135             WizDecisionSet oneKey = (WizDecisionSet) i.next();
136             Set setOfValues = (Set) workingSet.get(oneKey);
137             if (setOfValues.isEmpty()) {
138                 returnValue.remove(oneKey);
139             }
140 
141             boolean foundAMatch = false;
142             for (Iterator j = setOfValues.iterator(); j.hasNext();) {
143                 WizDecisionEntities oneEntry = (WizDecisionEntities) j.next();
144                 if (oneEntry.getStepId() == stepId && oneEntry.getDecisionValue() == picklistValue) {
145                     foundAMatch = true;
146                     break;
147                 }
148             }
149 
150             if (!foundAMatch) {
151                 returnValue.remove(oneKey);
152             }
153 
154         }
155 
156         return returnValue;
157     }
158 
159 }