001: /*
002: * $Id: org.eclipse.jdt.ui.prefs 5004 2006-03-17 20:47:08 -0800 (Fri, 17 Mar
003: * 2006) eelco12 $ $Revision: 5004 $ $Date: 2006-03-17 20:47:08 -0800 (Fri, 17
004: * Mar 2006) $
005: *
006: * ==============================================================================
007: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
008: * use this file except in compliance with the License. You may obtain a copy of
009: * the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
015: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
016: * License for the specific language governing permissions and limitations under
017: * the License.
018: */
019: package wicket.extensions.wizard;
020:
021: import java.io.Serializable;
022: import java.util.ArrayList;
023: import java.util.Iterator;
024: import java.util.List;
025:
026: import wicket.util.collections.ArrayListStack;
027:
028: /**
029: * Default implementation of {@link IWizardModel}.
030: * <p>
031: * Steps can be added to this model directly using either the
032: * {@link #add(IWizardStep) normal add method} or
033: * {@link #add(IWizardStep, wicket.extensions.wizard.WizardModel.ICondition) the conditional add method}.
034: * </p>
035: *
036: * <p>
037: * <a href="https://wizard-framework.dev.java.net/">Swing Wizard Framework</a>
038: * served as a valuable source of inspiration.
039: * </p>
040: *
041: * @author Eelco Hillenius
042: */
043: public class WizardModel implements IWizardModel {
044: /**
045: * Interface for conditional displaying of wizard steps.
046: */
047: public interface ICondition extends Serializable {
048: /**
049: * Evaluates the current state and returns whether the step that is
050: * coupled to this condition is available.
051: *
052: * @return True if the step this condition is coupled to is available,
053: * false otherwise
054: */
055: public boolean evaluate();
056: }
057:
058: /**
059: * Condition that always evaluates true.
060: */
061: public static final ICondition TRUE = new ICondition() {
062: private static final long serialVersionUID = 1L;
063:
064: /**
065: * Always returns true.
066: *
067: * @return True
068: *
069: * @see ICondition#evaluate()
070: */
071: public boolean evaluate() {
072: return true;
073: }
074: };
075:
076: private static final long serialVersionUID = 1L;
077:
078: /** The currently active step. */
079: private IWizardStep activeStep;
080:
081: /** Whether cancel functionality is available. */
082: private boolean cancelVisible = true;
083:
084: /** Conditions with steps. */
085: private List conditions = new ArrayList();
086:
087: /** State history. */
088: private final ArrayListStack history = new ArrayListStack();
089:
090: /** Whether the last button should be shown at all; false by default. */
091: private boolean lastVisible = false;
092:
093: /** The wizard steps. */
094: private List steps = new ArrayList();
095:
096: /** Listeners for {@link IWizardModelListener model events}. */
097: private final List wizardModelListeners = new ArrayList(1);
098:
099: /**
100: * Construct.
101: */
102: public WizardModel() {
103: }
104:
105: /**
106: * Adds the next step to the wizard. If the {@link WizardStep} implements
107: * {@link ICondition}, then this method is equivalent to calling
108: * {@link #add(IWizardStep, ICondition) add(step, (ICondition)step)}.
109: *
110: * @param step
111: * the step to added.
112: */
113: public void add(IWizardStep step) {
114: if (step instanceof ICondition)
115: add(step, (ICondition) step);
116: else
117: add(step, TRUE);
118: }
119:
120: /**
121: * Adds an optional step to the model. The step will only be displayed if
122: * the specified condition is met.
123: *
124: * @param step
125: * The step to add
126: * @param condition
127: * the {@link ICondition} under which it should be included in
128: * the wizard.
129: */
130: public void add(IWizardStep step, ICondition condition) {
131: steps.add(step);
132: conditions.add(condition);
133: }
134:
135: /**
136: * Adds a wizard model listener.
137: *
138: * @param listener
139: * The listener to add
140: */
141: public final void addListener(IWizardModelListener listener) {
142: this .wizardModelListeners.add(listener);
143: }
144:
145: /**
146: * This implementation just fires
147: * {@link IWizardModelListener#onCancel() a cancel event}. Though this
148: * isn't a very strong contract, it gives all the power to the user of this
149: * model.
150: *
151: * @see wicket.extensions.wizard.IWizardModel#cancel()
152: */
153: public void cancel() {
154: fireWizardCancelled();
155: }
156:
157: /**
158: * This implementation just fires
159: * {@link IWizardModelListener#onFinish() a finish event}. Though this
160: * isn't a very strong contract, it gives all the power to the user of this
161: * model.
162: *
163: * @see wicket.extensions.wizard.IWizardModel#finish()
164: */
165: public void finish() {
166: fireWizardFinished();
167: }
168:
169: /**
170: * Gets the current active step the wizard should display.
171: *
172: * @return the active step.
173: */
174: public final IWizardStep getActiveStep() {
175: return activeStep;
176: }
177:
178: /**
179: * Gets whether cancel functionality is available.
180: *
181: * @return Whether cancel functionality is available
182: */
183: public boolean isCancelVisible() {
184: return cancelVisible;
185: }
186:
187: /**
188: * Checks if the last button should be enabled.
189: *
190: * @return <tt>true</tt> if the last button should be enabled,
191: * <tt>false</tt> otherwise.
192: * @see #isLastVisible
193: */
194: public final boolean isLastAvailable() {
195: return allStepsComplete() && !isLastStep(activeStep);
196: }
197:
198: /**
199: * @see wicket.extensions.wizard.IWizardModel#isLastStep(wicket.extensions.wizard.IWizardStep)
200: */
201: public boolean isLastStep(IWizardStep step) {
202: return findLastStep().equals(step);
203: }
204:
205: /**
206: * Checks if the last button should be displayed. This method should only
207: * return true if the {@link #isLastAvailable} will return true at any
208: * point. Returning false will prevent the last button from appearing on the
209: * wizard at all.
210: *
211: * @return <tt>true</tt> if the previou last should be displayed,
212: * <tt>false</tt> otherwise.
213: */
214: public boolean isLastVisible() {
215: return lastVisible;
216: }
217:
218: /**
219: * Checks if the next button should be enabled.
220: *
221: * @return <tt>true</tt> if the next button should be enabled,
222: * <tt>false</tt> otherwise.
223: */
224: public final boolean isNextAvailable() {
225: return activeStep.isComplete() && !isLastStep(activeStep);
226: }
227:
228: /**
229: * Checks if the previous button should be enabled.
230: *
231: * @return <tt>true</tt> if the previous button should be enabled,
232: * <tt>false</tt> otherwise.
233: */
234: public final boolean isPreviousAvailable() {
235: return !history.isEmpty();
236: }
237:
238: /**
239: * @see wicket.extensions.wizard.IWizardModel#lastStep()
240: */
241: public void lastStep() {
242: history.push(getActiveStep());
243: IWizardStep lastStep = findLastStep();
244: setActiveStep(lastStep);
245: }
246:
247: /**
248: * @see wicket.extensions.wizard.IWizardModel#next()
249: */
250: public void next() {
251: history.push(getActiveStep());
252: IWizardStep step = findNextVisibleStep();
253: setActiveStep(step);
254: }
255:
256: /**
257: * @see wicket.extensions.wizard.IWizardModel#previous()
258: */
259: public void previous() {
260: IWizardStep step = (IWizardStep) history.pop();
261: setActiveStep(step);
262: }
263:
264: /**
265: * Removes a wizard model listener.
266: *
267: * @param listener
268: * The listener to remove
269: */
270: public final void removeListener(IWizardModelListener listener) {
271: this .wizardModelListeners.remove(listener);
272: }
273:
274: /**
275: * @see wicket.extensions.wizard.IWizardModel#reset()
276: */
277: public void reset() {
278: history.clear();
279: this .activeStep = null;
280: setActiveStep(findNextVisibleStep());
281: }
282:
283: /**
284: * Sets the active step.
285: *
286: * @param step
287: * the new active step step.
288: */
289: public void setActiveStep(IWizardStep step) {
290: if (this .activeStep != null && step != null
291: && activeStep.equals(step)) {
292: return;
293: }
294: IWizardStep old = this .activeStep;
295: this .activeStep = step;
296:
297: fireActiveStepChanged(step);
298: }
299:
300: /**
301: * Sets whether cancel functionality is available.
302: *
303: * @param cancelVisible
304: * Whether cancel functionality is available
305: */
306: public void setCancelVisible(boolean cancelVisible) {
307: this .cancelVisible = cancelVisible;
308: }
309:
310: /**
311: * Configures if the last button should be displayed.
312: *
313: * @param lastVisible
314: * <tt>true</tt> to display the last button, <tt>false</tt>
315: * otherwise.
316: * @see #isLastVisible
317: */
318: public void setLastVisible(boolean lastVisible) {
319: this .lastVisible = lastVisible;
320: }
321:
322: /**
323: * @see wicket.extensions.wizard.IWizardModel#stepIterator()
324: */
325: public final Iterator stepIterator() {
326: return steps.iterator();
327: }
328:
329: /**
330: * Returns true if all the steps in the wizard return <tt>true</tt> from
331: * {@link IWizardStep#isComplete}. This is primarily used to determine if
332: * the last button can be enabled.
333: *
334: * @return <tt>true</tt> if all the steps in the wizard are complete,
335: * <tt>false</tt> otherwise.
336: */
337: protected final boolean allStepsComplete() {
338: for (Iterator iterator = stepIterator(); iterator.hasNext();) {
339: if (!((IWizardStep) iterator.next()).isComplete()) {
340: return false;
341: }
342: }
343:
344: return true;
345: }
346:
347: /**
348: * Finds the last step in this model.
349: *
350: * @return The last step
351: */
352: protected final IWizardStep findLastStep() {
353: for (int i = conditions.size() - 1; i >= 0; i--) {
354: ICondition condition = (ICondition) conditions.get(i);
355: if (condition.evaluate()) {
356: return (IWizardStep) steps.get(i);
357: }
358: }
359:
360: throw new IllegalStateException(
361: "Wizard contains no visible steps");
362: }
363:
364: /**
365: * Finds the next visible step based on the active step.
366: *
367: * @return The next visible step based on the active step
368: */
369: protected final IWizardStep findNextVisibleStep() {
370: int startIndex = (activeStep == null) ? 0 : steps
371: .indexOf(activeStep) + 1;
372:
373: for (int i = startIndex; i < conditions.size(); i++) {
374: ICondition condition = (ICondition) conditions.get(i);
375: if (condition.evaluate()) {
376: return (IWizardStep) steps.get(i);
377: }
378: }
379:
380: throw new IllegalStateException(
381: "Wizard contains no more visible steps");
382: }
383:
384: /**
385: * Notify listeners that the active step has changed.
386: *
387: * @param step
388: * The new step
389: */
390: protected final void fireActiveStepChanged(IWizardStep step) {
391: for (Iterator i = wizardModelListeners.iterator(); i.hasNext();) {
392: IWizardModelListener listener = (IWizardModelListener) i
393: .next();
394: listener.onActiveStepChanged(step);
395: }
396: }
397:
398: /**
399: * Notify listeners that the wizard is finished.
400: */
401: protected final void fireWizardCancelled() {
402: for (Iterator i = wizardModelListeners.iterator(); i.hasNext();) {
403: IWizardModelListener listener = (IWizardModelListener) i
404: .next();
405: listener.onCancel();
406: }
407: }
408:
409: /**
410: * Notify listeners that the wizard is finished.
411: */
412: protected final void fireWizardFinished() {
413: for (Iterator i = wizardModelListeners.iterator(); i.hasNext();) {
414: IWizardModelListener listener = (IWizardModelListener) i
415: .next();
416: listener.onFinish();
417: }
418: }
419: }
|