001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.extensions.wizard;
018:
019: import java.util.ArrayList;
020: import java.util.Iterator;
021: import java.util.List;
022:
023: import org.apache.wicket.IClusterable;
024: import org.apache.wicket.util.collections.ArrayListStack;
025:
026: /**
027: * Default implementation of {@link IWizardModel}, which models a semi-static
028: * wizard. This means that all steps should be known upfront, and added to the
029: * model on construction. Steps can be optional by using {@link ICondition}.
030: * The wizard is initialized with a wizard model through calling method
031: * {@link Wizard#init(IWizardModel)}.
032: * <p>
033: * Steps can be added to this model directly using either the
034: * {@link #add(IWizardStep) normal add method} or
035: * {@link #add(IWizardStep, ICondition) the conditional add method}.
036: * </p>
037: *
038: * <p>
039: * <a href="https://wizard-framework.dev.java.net/">Swing Wizard Framework</a>
040: * served as a valuable source of inspiration.
041: * </p>
042: *
043: * @author Eelco Hillenius
044: */
045: public class WizardModel extends AbstractWizardModel {
046: /**
047: * Interface for conditional displaying of wizard steps.
048: */
049: public interface ICondition extends IClusterable {
050: /**
051: * Evaluates the current state and returns whether the step that is
052: * coupled to this condition is available.
053: *
054: * @return True if the step this condition is coupled to is available,
055: * false otherwise
056: */
057: public boolean evaluate();
058: }
059:
060: /**
061: * Condition that always evaluates true.
062: */
063: public static final ICondition TRUE = new ICondition() {
064: private static final long serialVersionUID = 1L;
065:
066: /**
067: * Always returns true.
068: *
069: * @return True
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: /** Conditions with steps. */
082: private List conditions = new ArrayList();
083:
084: /** State history. */
085: private final ArrayListStack history = new ArrayListStack();
086:
087: /** The wizard steps. */
088: private List steps = new ArrayList();
089:
090: /**
091: * Construct.
092: */
093: public WizardModel() {
094: }
095:
096: /**
097: * Adds the next step to the wizard. If the {@link WizardStep} implements
098: * {@link ICondition}, then this method is equivalent to calling
099: * {@link #add(IWizardStep, ICondition) add(step, (ICondition)step)}.
100: *
101: * @param step
102: * the step to added.
103: */
104: public void add(IWizardStep step) {
105: if (step instanceof ICondition)
106: add(step, (ICondition) step);
107: else
108: add(step, TRUE);
109: }
110:
111: /**
112: * Adds an optional step to the model. The step will only be displayed if
113: * the specified condition is met.
114: *
115: * @param step
116: * The step to add
117: * @param condition
118: * the {@link ICondition} under which it should be included in
119: * the wizard.
120: */
121: public void add(IWizardStep step, ICondition condition) {
122: steps.add(step);
123: conditions.add(condition);
124: }
125:
126: /**
127: * Gets the current active step the wizard should display.
128: *
129: * @return the active step.
130: */
131: public final IWizardStep getActiveStep() {
132: return activeStep;
133: }
134:
135: /**
136: * Checks if the last button should be enabled.
137: *
138: * @return <tt>true</tt> if the last button should be enabled,
139: * <tt>false</tt> otherwise.
140: * @see IWizardModel#isLastVisible
141: */
142: public boolean isLastAvailable() {
143: return allStepsComplete() && !isLastStep(activeStep);
144: }
145:
146: /**
147: * @see org.apache.wicket.extensions.wizard.IWizardModel#isLastStep(org.apache.wicket.extensions.wizard.IWizardStep)
148: */
149: public boolean isLastStep(IWizardStep step) {
150: return findLastStep().equals(step);
151: }
152:
153: /**
154: * Checks if the next button should be enabled.
155: *
156: * @return <tt>true</tt> if the next button should be enabled,
157: * <tt>false</tt> otherwise.
158: */
159: public boolean isNextAvailable() {
160: return activeStep.isComplete() && !isLastStep(activeStep);
161: }
162:
163: /**
164: * Checks if the previous button should be enabled.
165: *
166: * @return <tt>true</tt> if the previous button should be enabled,
167: * <tt>false</tt> otherwise.
168: */
169: public boolean isPreviousAvailable() {
170: return !history.isEmpty();
171: }
172:
173: /**
174: * @see org.apache.wicket.extensions.wizard.IWizardModel#last()
175: */
176: public void last() {
177: history.push(getActiveStep());
178: IWizardStep lastStep = findLastStep();
179: setActiveStep(lastStep);
180: }
181:
182: /**
183: * @see org.apache.wicket.extensions.wizard.IWizardModel#next()
184: */
185: public void next() {
186: history.push(getActiveStep());
187: IWizardStep step = findNextVisibleStep();
188: setActiveStep(step);
189: }
190:
191: /**
192: * @see org.apache.wicket.extensions.wizard.IWizardModel#previous()
193: */
194: public void previous() {
195: IWizardStep step = (IWizardStep) history.pop();
196: setActiveStep(step);
197: }
198:
199: /**
200: * @see org.apache.wicket.extensions.wizard.IWizardModel#reset()
201: */
202: public void reset() {
203: history.clear();
204: this .activeStep = null;
205: setActiveStep(findNextVisibleStep());
206: }
207:
208: /**
209: * Sets the active step.
210: *
211: * @param step
212: * the new active step step.
213: */
214: public void setActiveStep(IWizardStep step) {
215: if (this .activeStep != null && step != null
216: && activeStep.equals(step)) {
217: return;
218: }
219:
220: this .activeStep = step;
221:
222: fireActiveStepChanged(step);
223: }
224:
225: /**
226: * @see IWizardModel#stepIterator()
227: */
228: public final Iterator stepIterator() {
229: return steps.iterator();
230: }
231:
232: /**
233: * Returns true if all the steps in the wizard return <tt>true</tt> from
234: * {@link IWizardStep#isComplete}. This is primarily used to determine if
235: * the last button can be enabled.
236: *
237: * @return <tt>true</tt> if all the steps in the wizard are complete,
238: * <tt>false</tt> otherwise.
239: */
240: protected final boolean allStepsComplete() {
241: for (Iterator iterator = stepIterator(); iterator.hasNext();) {
242: if (!((IWizardStep) iterator.next()).isComplete()) {
243: return false;
244: }
245: }
246:
247: return true;
248: }
249:
250: /**
251: * Finds the last step in this model.
252: *
253: * @return The last step
254: */
255: protected final IWizardStep findLastStep() {
256: for (int i = conditions.size() - 1; i >= 0; i--) {
257: ICondition condition = (ICondition) conditions.get(i);
258: if (condition.evaluate()) {
259: return (IWizardStep) steps.get(i);
260: }
261: }
262:
263: throw new IllegalStateException(
264: "Wizard contains no visible steps");
265: }
266:
267: /**
268: * Finds the next visible step based on the active step.
269: *
270: * @return The next visible step based on the active step
271: */
272: protected final IWizardStep findNextVisibleStep() {
273: int startIndex = (activeStep == null) ? 0 : steps
274: .indexOf(activeStep) + 1;
275:
276: for (int i = startIndex; i < conditions.size(); i++) {
277: ICondition condition = (ICondition) conditions.get(i);
278: if (condition.evaluate()) {
279: return (IWizardStep) steps.get(i);
280: }
281: }
282:
283: throw new IllegalStateException(
284: "Wizard contains no more visible steps");
285: }
286: }
|