1 | package dk.deepthought.sidious.supportsystem; |
2 | |
3 | import java.util.ArrayList; |
4 | import java.util.Collection; |
5 | |
6 | import org.apache.commons.logging.Log; |
7 | import org.apache.commons.logging.LogFactory; |
8 | |
9 | import dk.deepthought.sidious.explanation.Explanation; |
10 | |
11 | /** |
12 | * This class represents a list of <code>Adjustable</code> objects and their |
13 | * setting. |
14 | * |
15 | * @author Deepthought |
16 | * |
17 | */ |
18 | public class Step { |
19 | |
20 | /** |
21 | * The logger for this class. |
22 | */ |
23 | private static final Log logger = LogFactory.getLog(Step.class); |
24 | |
25 | /** |
26 | * The adjustables of this. |
27 | */ |
28 | private final Collection<Adjustable> adjustables; |
29 | |
30 | /** |
31 | * The explanation of this. |
32 | */ |
33 | private Explanation explanation; |
34 | |
35 | /** |
36 | * Constructs a new <code>Step</code> object, specified by the input |
37 | * <code>adjustables</code>. |
38 | * <p> |
39 | * The input adjustables are copied defensively. |
40 | * <p> |
41 | * If the input collection is <code>null</code> an empty step is created. |
42 | * This is to facilitate graphs that do not utilize steps. |
43 | * |
44 | * @param adjustables |
45 | * adjustables associated with this step |
46 | */ |
47 | public Step(final Collection<Adjustable> adjustables) { |
48 | if (logger.isDebugEnabled()) { |
49 | logger.debug("Step(Collection<Adjustable> adjustables=" |
50 | + adjustables + ")"); |
51 | } |
52 | if (adjustables != null) { |
53 | this.adjustables = new ArrayList<Adjustable>(adjustables); |
54 | if (adjustables.isEmpty()) { |
55 | String fail = "Empty adjustable list not allowed"; |
56 | logger.error(fail); |
57 | throw new IllegalArgumentException(fail); |
58 | } |
59 | } else { |
60 | this.adjustables = new ArrayList<Adjustable>(); |
61 | } |
62 | } |
63 | |
64 | /** |
65 | * Returns all possible steps emanating from this step. |
66 | * <p> |
67 | * The possible steps are calculated by collecting and combining all |
68 | * possible adjustments of all adjustables of this step. |
69 | * |
70 | * @return all possible steps emanating from this step |
71 | */ |
72 | public Collection<Step> getSteps() { |
73 | if (logger.isDebugEnabled()) { |
74 | logger.debug("getSteps() - start"); |
75 | } |
76 | Collection<Step> steps = new ArrayList<Step>(); |
77 | ArrayList<Adjustable> proxyAdjustables = new ArrayList<Adjustable>( |
78 | adjustables); |
79 | for (Adjustable adj : adjustables) { |
80 | proxyAdjustables.remove(adj); |
81 | Collection<Adjustable> resultingAdjustables = adj |
82 | .possibleAdjustments(); |
83 | for (Adjustable newAdj : resultingAdjustables) { |
84 | proxyAdjustables.add(newAdj); |
85 | steps.add(new Step(proxyAdjustables)); |
86 | proxyAdjustables.remove(newAdj); |
87 | } |
88 | proxyAdjustables.add(adj); |
89 | } |
90 | if (logger.isDebugEnabled()) { |
91 | logger.debug("getSteps() - returned " + steps.size() |
92 | + " new possible steps"); |
93 | } |
94 | steps.add(this);// "virgin" step |
95 | return steps; |
96 | } |
97 | |
98 | /** |
99 | * Calculates the consequence of applying the adjustments of this step to |
100 | * the input state. |
101 | * |
102 | * @param state |
103 | * the input state |
104 | * @return the calculated consequence |
105 | */ |
106 | public State consequence(State state) { |
107 | if (logger.isDebugEnabled()) { |
108 | logger.debug("consequence(State state=" + state + ") - start"); |
109 | } |
110 | if (state == null) { |
111 | return null; |
112 | } |
113 | Collection<State> states = new ArrayList<State>(); |
114 | for (Adjustable adj : adjustables) { |
115 | states.add(adj.consequence(state)); |
116 | } |
117 | State returnState = state.impact(states); |
118 | if (logger.isDebugEnabled()) { |
119 | logger.debug("consequence(State state=" + state |
120 | + ") - end - return value=" + returnState); |
121 | } |
122 | return returnState; |
123 | } |
124 | |
125 | /** |
126 | * Returns the adjustables of this step. |
127 | * |
128 | * @return the adjustables of this step |
129 | */ |
130 | public Collection<Adjustable> getAdjustables() { |
131 | return new ArrayList<Adjustable>(adjustables); |
132 | } |
133 | |
134 | /** |
135 | * Sets the explanation of this. |
136 | * |
137 | * @param explanation |
138 | * the explanation |
139 | */ |
140 | public void setExplanation(Explanation explanation) { |
141 | this.explanation = explanation; |
142 | } |
143 | |
144 | /** |
145 | * Gets the explanation of this. |
146 | * |
147 | * @return the explanation |
148 | */ |
149 | public Explanation getExplanation() { |
150 | return explanation; |
151 | } |
152 | |
153 | /* |
154 | * (non-Javadoc) |
155 | * |
156 | * @see java.lang.Object#hashCode() |
157 | */ |
158 | @Override |
159 | public int hashCode() { |
160 | final int PRIME = 31; |
161 | int result = 1; |
162 | result = PRIME * result |
163 | + ((adjustables == null) ? 0 : adjustables.hashCode()); |
164 | |
165 | return result; |
166 | } |
167 | |
168 | /* |
169 | * (non-Javadoc) |
170 | * |
171 | * @see java.lang.Object#equals(java.lang.Object) |
172 | */ |
173 | @Override |
174 | public boolean equals(Object obj) { |
175 | if (this == obj) { |
176 | return true; |
177 | } |
178 | if (obj == null) { |
179 | return false; |
180 | } |
181 | if (getClass() != obj.getClass()) { |
182 | return false; |
183 | } |
184 | final Step other = (Step) obj; |
185 | if (adjustables == null) { |
186 | if (other.adjustables != null) { |
187 | return false; |
188 | } |
189 | } else if (!adjustables.equals(other.adjustables)) { |
190 | return false; |
191 | } |
192 | return true; |
193 | } |
194 | |
195 | /* |
196 | * (non-Javadoc) |
197 | * |
198 | * @see java.lang.Object#toString() |
199 | */ |
200 | @Override |
201 | public String toString() { |
202 | return getClass().getSimpleName() + "[adjustables=" + adjustables |
203 | + ", explanation=" + explanation + "]"; |
204 | } |
205 | |
206 | } |