001: /**
002: * Wizard Framework
003: * Copyright 2004 - 2005 Andrew Pietsch
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *
019: * $Id: DynamicModel.java,v 1.6 2005/08/13 01:19:03 pietschy Exp $
020: */package org.pietschy.wizard.models;
021:
022: import org.pietschy.wizard.AbstractWizardModel;
023: import org.pietschy.wizard.WizardStep;
024: import org.pietschy.wizard.WizardModel;
025:
026: import java.util.*;
027: import java.beans.PropertyChangeListener;
028: import java.beans.PropertyChangeEvent;
029:
030: /**
031: * The DynamicModel is very similar to the static model, except that steps can be dynamically removed
032: * from the wizard flow.
033: * <pre>
034: * // create a subclass of DynamicModel
035: * MyDynamicModel model = new MyDynamicModel();
036: *
037: * // add the first step..
038: * model.add(new MyFirstStep());
039: *
040: * // add an optional step..
041: * model.add(new MyOptionalStep(), new Condition()
042: * {
043: * public boolean evaluate(WizardModel model)
044: * {
045: * return ((MyDynamicModel) model).isOptionalRequired();
046: * }
047: * });
048: *
049: * // add the last step.
050: * model.add(new MyLastStep());
051: *
052: * // now create the wizard and use it..
053: * Wizard wizard = new Wizard(model);
054: * </pre>
055: * It is also worth noting that steps that implement {@link Condition} can be added using the basic
056: * {@link #add(WizardStep)} and the model will automatically add them as an optional step.
057: * <p>
058: *
059: *
060: *
061: * @see #add(WizardStep)
062: * @see #add(WizardStep, Condition)
063: */
064: public class DynamicModel extends AbstractWizardModel {
065:
066: /** An implementation of {@link Condition} that always returns <tt>true</tt>. */
067: public static final Condition TRUE_CONDITION = new Condition() {
068: public boolean evaluate(WizardModel model) {
069: return true;
070: }
071: };
072:
073: private ArrayList steps = new ArrayList();
074: private ArrayList conditions = new ArrayList();
075:
076: private Stack history = new Stack();
077:
078: /**
079: * Creates a new DynamicModel.
080: */
081: public DynamicModel() {
082: }
083:
084: /**
085: * Adds the next step to the wizard. If the {@link WizardStep} implements {@link Condition}, then this method is
086: * equivalent to calling {@link #add(WizardStep, Condition) add(step, (Condition)step)}, other wise it is equivalent to
087: * calling {@link #add(org.pietschy.wizard.WizardStep, Condition) add(step, TRUE_CONDITION)}.
088: * <p>
089: * This allows the easy use of {@link WizardStep}s that determine the condition under which they are displayed.
090: *
091: * @param step the step to added.
092: */
093: public void add(WizardStep step) {
094: if (step instanceof Condition)
095: add(step, (Condition) step);
096: else
097: add(step, TRUE_CONDITION);
098: }
099:
100: /**
101: * Adds an optional step to the model. The step will only be displayed if the specified
102: * condition is met.
103: *
104: * @param step the {@link org.pietschy.wizard.WizardStep} to add.
105: * @param condition the {@link Condition} under which it should be included in the wizard.
106: */
107: public void add(WizardStep step, Condition condition) {
108: addCompleteListener(step);
109: steps.add(step);
110: conditions.add(condition);
111: }
112:
113: public void nextStep() {
114: WizardStep currentStep = getActiveStep();
115: history.push(currentStep);
116: setActiveStep(findNextVisibleStep(currentStep));
117: }
118:
119: public void previousStep() {
120: WizardStep step = (WizardStep) history.pop();
121: setActiveStep(step);
122: }
123:
124: public void lastStep() {
125: WizardStep activeStep = getActiveStep();
126: history.push(activeStep);
127: setActiveStep(findLastStep());
128: }
129:
130: public void reset() {
131: history.clear();
132: setActiveStep(findNextVisibleStep(null));
133: }
134:
135: public boolean isLastStep(WizardStep step) {
136: return findLastStep().equals(step);
137: }
138:
139: /**
140: * Forces the model to re-evaluate it's current state. This method will re-evalute the
141: * conditional steps by calling {@link Condition#evaluate(org.pietschy.wizard.WizardModel)}.
142: * <p>
143: * Subclasses that override this method must be sure to invoke <tt>super.refreshModelState()</tt>.
144: */
145: public void refreshModelState() {
146: WizardStep activeStep = getActiveStep();
147: setNextAvailable(activeStep != null && activeStep.isComplete()
148: && !isLastStep(activeStep));
149: setPreviousAvailable(activeStep != null && !history.isEmpty());
150: setLastAvailable(activeStep != null && allStepsComplete()
151: && !isLastStep(activeStep));
152: setCancelAvailable(true);
153: }
154:
155: /**
156: * Returns true if all included steps in the wizard return <tt>true</tt> from {@link WizardStep#isComplete}. This
157: * is primarily used to determine if the last button can be enabled.
158: * <p>
159: * Please note that this method ignores all steps for which their {@link Condition} returns false.
160: *
161: * @return <tt>true</tt> if all the visible steps in the wizard are complete, <tt>false</tt> otherwise.
162: */
163: public boolean allStepsComplete() {
164: for (int i = 0; i < steps.size(); i++) {
165: WizardStep step = (WizardStep) steps.get(i);
166: Condition condition = (Condition) conditions.get(i);
167:
168: if (condition.evaluate(this )) {
169: if (!step.isComplete())
170: return false;
171: }
172:
173: }
174:
175: return true;
176: }
177:
178: public Iterator stepIterator() {
179: return Collections.unmodifiableList(steps).iterator();
180: }
181:
182: /**
183: * @param currentStep
184: * @return
185: */
186: private WizardStep findNextVisibleStep(WizardStep currentStep) {
187: int startIndex = (currentStep == null) ? 0 : steps
188: .indexOf(currentStep) + 1;
189:
190: for (int i = startIndex; i < conditions.size(); i++) {
191: Condition condition = (Condition) conditions.get(i);
192: if (condition.evaluate(this )) {
193: return (WizardStep) steps.get(i);
194: }
195: }
196:
197: throw new IllegalStateException(
198: "Wizard contains no more visible steps");
199: }
200:
201: /**
202: * @return
203: */
204: private WizardStep findLastStep() {
205: for (int i = conditions.size() - 1; i >= 0; i--) {
206: Condition condition = (Condition) conditions.get(i);
207: if (condition.evaluate(this )) {
208: return (WizardStep) steps.get(i);
209: }
210: }
211:
212: throw new IllegalStateException(
213: "Wizard contains no visible steps");
214: }
215: }
|