1
2
3
4
5
6
7
8
9
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
110
111 if (workingSet.size() == 0) {
112 return null;
113 }
114
115
116
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 }