001 package dk.deepthought.sidious.supportsystem;
002 
003 import java.util.ArrayList;
004 import java.util.Collection;
005 
006 import org.apache.commons.logging.Log;
007 import org.apache.commons.logging.LogFactory;
008 
009 import dk.deepthought.sidious.explanation.Explanation;
010 
011 /**
012  * This class represents a list of <code>Adjustable</code> objects and their
013  * setting.
014  
015  @author Deepthought
016  
017  */
018 public class Step {
019 
020     /**
021      * The logger for this class.
022      */
023     private static final Log logger = LogFactory.getLog(Step.class);
024 
025     /**
026      * The adjustables of this.
027      */
028     private final Collection<Adjustable> adjustables;
029 
030     /**
031      * The explanation of this.
032      */
033     private Explanation explanation;
034 
035     /**
036      * Constructs a new <code>Step</code> object, specified by the input
037      <code>adjustables</code>.
038      <p>
039      * The input adjustables are copied defensively.
040      <p>
041      * If the input collection is <code>null</code> an empty step is created.
042      * This is to facilitate graphs that do not utilize steps.
043      
044      @param adjustables
045      *            adjustables associated with this step
046      */
047     public Step(final Collection<Adjustable> adjustables) {
048         if (logger.isDebugEnabled()) {
049             logger.debug("Step(Collection<Adjustable> adjustables="
050                     + adjustables + ")");
051         }
052         if (adjustables != null) {
053             this.adjustables = new ArrayList<Adjustable>(adjustables);
054             if (adjustables.isEmpty()) {
055                 String fail = "Empty adjustable list not allowed";
056                 logger.error(fail);
057                 throw new IllegalArgumentException(fail);
058             }
059         else {
060             this.adjustables = new ArrayList<Adjustable>();
061         }
062     }
063 
064     /**
065      * Returns all possible steps emanating from this step.
066      <p>
067      * The possible steps are calculated by collecting and combining all
068      * possible adjustments of all adjustables of this step.
069      
070      @return all possible steps emanating from this step
071      */
072     public Collection<Step> getSteps() {
073         if (logger.isDebugEnabled()) {
074             logger.debug("getSteps() - start");
075         }
076         Collection<Step> steps = new ArrayList<Step>();
077         ArrayList<Adjustable> proxyAdjustables = new ArrayList<Adjustable>(
078                 adjustables);
079         for (Adjustable adj : adjustables) {
080             proxyAdjustables.remove(adj);
081             Collection<Adjustable> resultingAdjustables = adj
082                     .possibleAdjustments();
083             for (Adjustable newAdj : resultingAdjustables) {
084                 proxyAdjustables.add(newAdj);
085                 steps.add(new Step(proxyAdjustables));
086                 proxyAdjustables.remove(newAdj);
087             }
088             proxyAdjustables.add(adj);
089         }
090         if (logger.isDebugEnabled()) {
091             logger.debug("getSteps() - returned " + steps.size()
092                     " new possible steps");
093         }
094         steps.add(this);// "virgin" step
095         return steps;
096     }
097 
098     /**
099      * 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: 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 = (Stepobj;
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 }