/*
Copyright 1996-2008 Ariba, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
$Id: //ariba/platform/ui/widgets/ariba/ui/wizard/core/Wizard.java#4 $
*/
package ariba.ui.wizard.core;
import ariba.ui.aribaweb.core.AWConcreteServerApplication;
import ariba.ui.aribaweb.core.AWPageCacheMark;
import ariba.ui.aribaweb.core.AWRequestContext;
import ariba.ui.aribaweb.util.AWMultiLocaleResourceManager;
import ariba.ui.aribaweb.util.AWResourceManager;
import ariba.ui.aribaweb.util.AWSingleLocaleResourceManager;
import ariba.ui.aribaweb.util.AWSemanticKeyProvider;
import ariba.ui.wizard.component.WizardUtil;
import ariba.ui.wizard.meta.WizardActionMeta;
import ariba.ui.wizard.meta.WizardFrameMeta;
import ariba.ui.wizard.meta.WizardMeta;
import ariba.ui.wizard.meta.WizardStepMeta;
import ariba.util.core.ClassUtil;
import ariba.util.core.Assert;
import ariba.util.core.MapUtil;
import ariba.util.core.ListUtil;
import java.util.Map;
import java.util.List;
import java.util.Locale;
import java.util.Iterator;
/**
The Wizard class represents a user's trip through a particular wizard
instance. It keeps track of the current step and/or frame, which actions
are enabled, etc. It also manages the event handling via the per-frame
and per-wizard delegate classes provided by the application.
@aribaapi ariba
*/
public class Wizard implements WizardStepsParent
{
/*-----------------------------------------------------------------------
Constants
-----------------------------------------------------------------------*/
// generic error messages
private final static String ErrorDelegateCreation =
"error creating wizard delegate of type '%s'";
private final static String NoVisibleStep =
"no visible frame in current step '%s'";
private final static String NullCurrentStep =
"current step should never be null";
// action error messages
private final static String InvalidStepFrameAction = "invalid action for step frame";
private final static String InvalidDialogAction = "invalid action for dialog frame";
private final static String InvalidActionResult =
"null action target for action '%s'";
/** @aribaapi private */
public final WizardAction next =
new WizardAction(WizardMeta.NextActionMeta, this, true);
/** @aribaapi private */
public final WizardAction prev =
new WizardAction(WizardMeta.PrevActionMeta, this, true);
/** @aribaapi private */
public final WizardAction exit =
new WizardAction(WizardMeta.ExitActionMeta, this, true);
/** @aribaapi private */
public final WizardAction ok =
new WizardAction(WizardMeta.OkActionMeta, this, true);
/** @aribaapi private */
public final WizardAction cancel =
new WizardAction(WizardMeta.CancelActionMeta, this, false);
/** @aribaapi private */
public final WizardAction refresh =
new WizardAction(WizardMeta.RefreshActionMeta, this, true);
/*-----------------------------------------------------------------------
Fields
-----------------------------------------------------------------------*/
private String _label;
private String _commandBar;
private Object _context;
private WizardMeta _meta;
private WizardDelegate _delegate;
private Map _actions;
private List _stepList;
private Map _frames;
private Map _steps;
private WizardFrame _selectionsFrame;
private AWResourceManager _resourceManager;
private Map _attributes;
private WizardFrame _exitFrame;
// fields that track the runtime state of the wizard
private WizardActionTarget _currentActionTarget;
private boolean _topLevelChanged = true;
private int _numVisibleTopLevelSteps;
// these shouldn't be here since they're UI-related...
private AWPageCacheMark _pageCacheMark;
private boolean _terminated = false;
/*-----------------------------------------------------------------------
Static State & Initialization
-----------------------------------------------------------------------*/
static
{
AWMultiLocaleResourceManager resourceManager = multiLocaleResourceManager();
String packageName = ClassUtil.stripClassFromClassName(WizardUtil.class.getName());
resourceManager.registerPackageName(packageName, true);
AWSemanticKeyProvider.registerClassExtension(WizardAction.class,
new SemanticKeyProvider_WizardAction());
AWSemanticKeyProvider.registerClassExtension(WizardStep.class,
new SemanticKeyProvider_WizardStep());
}
private static AWMultiLocaleResourceManager multiLocaleResourceManager ()
{
AWConcreteServerApplication application =
(AWConcreteServerApplication)AWConcreteServerApplication.SharedInstance;
AWMultiLocaleResourceManager resourceManager = application.resourceManager();
return resourceManager;
}
/*-----------------------------------------------------------------------
Constructors
-----------------------------------------------------------------------*/
/**
Constructor
@aribaapi ariba
*/
public Wizard (String name,
Object context,
AWResourceManager resourceManager)
{
this(name, context, resourceManager, null);
}
/**
Constructor
@aribaapi private
*/
public Wizard (String name,
Object context,
AWResourceManager resourceManager,
String extensionDirectory)
{
// initialize
init(name, context, resourceManager, extensionDirectory);
}
protected Wizard ()
{
}
protected void init (String name,
Object context,
AWResourceManager resourceManager,
String extensionDirectory)
{
// get the resource manager from the session
if (extensionDirectory != null) {
// get the resource manager from the session
AWMultiLocaleResourceManager extensionResourceManager =
multiLocaleResourceManager();
extensionResourceManager.registerResourceDirectory(extensionDirectory, null);
AWMultiLocaleResourceManager multiResMgr =
((AWSingleLocaleResourceManager)resourceManager).multiLocaleResourceManager(); // OK
extensionResourceManager.setNextResourceManager(multiResMgr);
Locale locale = resourceManager.locale();
resourceManager = extensionResourceManager.resourceManagerForLocale(locale);
}
_resourceManager = resourceManager;
_commandBar = null;
_context = context;
// check to see if debug is turned on, read the meta info
boolean debug = AWConcreteServerApplication.SharedInstance.isDebuggingEnabled();
_meta = WizardMeta.loadWizardMeta(name, resourceManager, debug);
// initialize our delegate
String delegateName = _meta.delegate();
if (delegateName != null) {
_delegate = instantiateWizardDelegate(delegateName);
Assert.that(_delegate != null, ErrorDelegateCreation, delegateName);
}
// initialize our runtime state
_actions = MapUtil.map();
_frames = MapUtil.map();
_steps = MapUtil.map();
// create instances of built-in and custom actions
buildActions();
// create runtime representation of the steps
_stepList = buildSteps(_meta.steps(), this);
// create dialog frames
Iterator dialogs = _meta.dialogs();
while (dialogs.hasNext()) {
WizardFrameMeta frameMeta = (WizardFrameMeta)dialogs.next();
WizardFrame frame = new WizardFrame(frameMeta, null, this);
_frames.put(frame.getName(), frame);
}
// create a selections frame if given in the meta
if (_meta.selectionsFrame() != null) {
_selectionsFrame = getFrameWithName(_meta.selectionsFrame().name());
_frames.put(_selectionsFrame.getName(), _selectionsFrame);
}
// cache the exit frame
_exitFrame = new WizardFrame(_meta.exitFrame(), null, this);
}
/**
create action instances for both built-in and
*/
private void buildActions ()
{
_actions.put(next.getName(), next);
_actions.put(prev.getName(), prev);
_actions.put(exit.getName(), exit);
_actions.put(ok.getName(), ok);
_actions.put(cancel.getName(), cancel);
_actions.put(refresh.getName(), refresh);
Iterator actionMetas = _meta.actions();
while (actionMetas.hasNext()) {
WizardActionMeta actionMeta = (WizardActionMeta)actionMetas.next();
WizardAction action = new WizardAction(actionMeta, this);
_actions.put(action.getName(), action);
}
}
/**
@aribaapi private
*/
protected List buildSteps (List stepMetas, WizardStepsParent parent)
{
List stepList = ListUtil.list(stepMetas.size());
WizardStep previousStep = null;
for (int index = 0; index < stepMetas.size(); index++) {
WizardStepMeta stepMeta = (WizardStepMeta)stepMetas.get(index);
WizardStep step = new WizardStep(stepMeta, parent, this);
_steps.put(step.getName(), step);
stepList.add(step);
step.setPreviousSibling(previousStep);
if (previousStep != null) {
previousStep.setNextSibling(step);
}
WizardFrame frame = step.getFrame();
if (frame != null) {
_frames.put(frame.getName(), frame);
}
previousStep = step;
}
return stepList;
}
/*-----------------------------------------------------------------------
Public Methods - Customization
-----------------------------------------------------------------------*/
/**
@aribaapi ariba
*/
public void setCommandBar (String commandBar)
{
_commandBar = commandBar;
}
/**
@aribaapi ariba
*/
public String getCommandBar ()
{
return _commandBar;
}
/*-----------------------------------------------------------------------
Public Methods - Lookup
-----------------------------------------------------------------------*/
/**
@aribaapi ariba
*/
public WizardAction getActionWithName (String actionName)
{
WizardAction action = (WizardAction)_actions.get(actionName);
return action;
}
/**
added for use by demoshell
@aribaapi private
*/
public void addAction (String name, WizardAction action)
{
_actions.put(name, action);
}
/**
@aribaapi ariba
*/
public WizardFrame getFrameWithName (String frameName)
{
WizardFrame frame = (WizardFrame)_frames.get(frameName);
return frame;
}
/**
@aribaapi ariba
*/
public WizardStep getStepWithName (String stepName)
{
WizardStep step = (WizardStep)_steps.get(stepName);
return step;
}
public WizardFrame getExitFrame ()
{
return _exitFrame;
}
public void setExitFrame (WizardFrame frame)
{
_exitFrame = frame;
}
/*-----------------------------------------------------------------------
Public Methods - State
-----------------------------------------------------------------------*/
/**
@aribaapi ariba
*/
public Object getContext ()
{
return _context;
}
/**
@aribaapi private
*/
public String getLabel ()
{
if (_label == null) {
_label = localizedStringForKey(_meta.label());
}
return _label;
}
/**
@aribaapi private
*/
public void setLabel (String label)
{
_label = label;
}
/**
@aribaapi ariba
*/
public WizardDelegate getDelegate ()
{
return _delegate;
}
/**
@aribaapi private
*/
public String getSummarySource ()
{
return _meta.summary();
}
public String getPreTocSource ()
{
return _meta.preToc();
}
public String getPostTocSource ()
{
return _meta.postToc();
}
/**
@aribaapi ariba
*/
public WizardStep getCurrentStep ()
{
WizardFrame frame = getCurrentFrame();
WizardStep step = frame.getStep();
while (step == null && frame != null) {
frame = frame.getBackFrame();
step = frame.getStep();
}
Assert.that(step != null, NullCurrentStep);
return step;
}
/**
@aribaapi private
*/
public String getSelectionsLabel ()
{
return _meta.selectionsLabel();
}
/**
@aribaapi private
*/
public String getSelectionsIcon ()
{
return _meta.selectionsIcon();
}
/**
@aribaapi private
*/
public WizardFrame getSelectionsFrame ()
{
return _selectionsFrame;
}
/**
@aribaapi ariba
*/
public WizardActionTarget getCurrentActionTarget ()
{
return _currentActionTarget;
}
/**
@aribaapi ariba
*/
public WizardFrame getCurrentFrame ()
{
return _currentActionTarget != null ?
_currentActionTarget.getOriginatingFrame(): null;
}
/**
@aribaapi ariba
*/
public void setAttribute (Object key, Object value)
{
if (_attributes == null) {
_attributes = MapUtil.map();
}
_attributes.put(key, value);
}
/**
@aribaapi ariba
*/
public Object getAttribute (Object key)
{
if (_attributes != null) {
return _attributes.get(key);
}
return null;
}
/*-----------------------------------------------------------------------
Public Methods - Actions
-----------------------------------------------------------------------*/
/**
@aribaapi ariba
*/
public void start ()
{
if (_delegate != null) {
_delegate.initialize(this);
}
// default to starting at the first step, no validation
if (getCurrentActionTarget() == null) {
WizardStep firstStep = (WizardStep)ListUtil.firstElement(_stepList);
WizardFrame firstFrame = firstStep.getFirstFrameToDisplay();
if (firstFrame == null) {
firstStep = firstStep.getNextStepToDisplay();
Assert.that(firstStep != null, NoVisibleStep);
firstFrame = firstStep.getFrame();
}
setCurrentActionTarget(firstFrame);
}
}
/**
This take place after the take values phase. So the validation result
has already been stored in the current frame
@aribaapi private
*/
public WizardActionTarget invokeAction (
WizardAction action,
AWRequestContext requestContext)
{
WizardFrame currentFrame = getCurrentFrame();
if (!currentFrame.isValid() && !action.ignoreValidation()) {
currentFrame.setShowErrors(true);
return currentFrame;
}
if (currentFrame.isValid()) {
currentFrame.setShowErrors(false);
}
WizardFrameDelegate frameDelegate = currentFrame.getDelegate();
WizardActionTarget actionTarget = null;
// see if the frame delegate wants to tell us where to go
if (frameDelegate != null) {
actionTarget = frameDelegate.targetForAction(action);
}
// see if the wizard delegate wants to tell us where to go
if (actionTarget == null && _delegate != null) {
actionTarget = _delegate.targetForAction(action);
}
// do default navigation for the actions we know about
if (actionTarget == null) {
if (action == next) {
Assert.that(!currentFrame.isDialogFrame(), InvalidStepFrameAction);
WizardStep nextStep = getCurrentStep().getNextStepToDisplay();
Assert.that(nextStep != null, NoVisibleStep);
actionTarget = nextStep.getFrame();
}
else if (action == prev) {
Assert.that(!currentFrame.isDialogFrame(), InvalidStepFrameAction);
WizardStep prevStep = getCurrentStep().getPreviousStepToDisplay();
Assert.that(prevStep != null, NoVisibleStep);
actionTarget = prevStep.getFrame();
}
else if (action == ok || action == cancel) {
Assert.that(currentFrame.isDialogFrame(), InvalidDialogAction);
actionTarget = currentFrame.getBackFrame();
}
else if (action == exit) {
actionTarget = _exitFrame;
}
else if (action == refresh) {
actionTarget = currentFrame;
}
else {
String targetFrameName = action.getTarget();
if (targetFrameName != null) {
actionTarget = getFrameWithName(targetFrameName);
}
}
}
Assert.that(actionTarget != null, InvalidActionResult, action.getName());
setCurrentActionTarget(actionTarget);
return actionTarget;
}
/**
Tells the wizard to go to a particular step. It enforces validation.
In other words, the user will be forced to stay on the current frame
if there is any error.
@aribaapi ariba
*/
public WizardFrame gotoStep (WizardStep step)
{
return gotoFrame(step.getFirstFrameToDisplay());
}
/**
Tells the wizard to go to a particular frame. It enforces validation.
In other words, the user will be forced to stay on the current frame
if there is any error.
@aribaapi ariba
*/
public WizardFrame gotoFrame (WizardFrame frame)
{
WizardFrame currentFrame = getCurrentFrame();
if (currentFrame != null ) {
if (!currentFrame.isValid()) {
currentFrame.setShowErrors(true);
frame = currentFrame;
}
else {
currentFrame.setShowErrors(false);
}
}
setCurrentActionTarget(frame);
return frame;
}
/**
@aribaapi private
*/
public void cleanup ()
{
_terminated = true;
// remove wizard frame delegate
for (Iterator enum_Itr = _frames.values().iterator(); enum_Itr.hasNext();) {
WizardFrame frame = (WizardFrame)enum_Itr.next();
frame.cleanup();
}
// null out all wizard ivars
_delegate = null;
_actions = null;
_attributes = null;
_stepList = null;
_steps = null;
_selectionsFrame = null;
_resourceManager = null;
_exitFrame = null;
_currentActionTarget = null;
_pageCacheMark = null;
_label = null;
_commandBar = null;
_context = null;
_meta = null;
_frames = null;
}
/**
@aribaapi private
*/
public boolean isTerminated ()
{
return _terminated;
}
/*-----------------------------------------------------------------------
Public Methods - Dynamic Step/Frame creation
Implements the WizardStepsParent interface
-----------------------------------------------------------------------*/
/**
@aribaapi ariba
*/
public List getSteps ()
{
return _stepList;
}
/**
@aribaapi ariba
*/
public void insertStepBefore (WizardStep step, WizardStep beforeStep)
{
WizardStepsParent parent = beforeStep.getParent();
List steps = parent.getSteps();
insertStepAt(step, steps, steps.indexOf(beforeStep), parent);
}
/**
@aribaapi ariba
*/
public void insertStepAfter (WizardStep step, WizardStep afterStep)
{
WizardStepsParent parent = afterStep.getParent();
List steps = parent.getSteps();
insertStepAt(step, steps, steps.indexOf(afterStep)+ 1, parent);
}
/**
@aribaapi ariba
*/
public void insertStepAt (WizardStep step, int index)
{
insertStepAt(step, _stepList, index, this);
}
public void updateChildrenVisible () {
// no-op since Wizard class is always visible.
}
private void addStepToCache (WizardStep step)
{
_steps.put(step.getName(), step);
WizardFrame frame = step.getFrame();
if (frame != null) {
_frames.put(frame.getName(), frame);
}
List substeps = step.getSteps();
if (substeps != null) {
int size = substeps.size();
for (int i = 0; i < size; i++) {
addStepToCache((WizardStep)substeps.get(i));
}
}
}
/**
@aribaapi private
*/
protected void removeStepFromCache (WizardStep step)
{
_steps.remove(step.getName());
WizardFrame frame = step.getFrame();
if (frame != null) {
frame.setStep(null);
_frames.remove(frame.getName());
}
List substeps = step.getSteps();
if (substeps != null) {
int size = substeps.size();
for (int i = 0; i < size; i++) {
removeStepFromCache((WizardStep)substeps.get(i));
}
}
}
/**
@aribaapi private
*/
protected void insertStepAt (WizardStep step, List steps, int index,
WizardStepsParent parent)
{
// insert the new step
Assert.that(index >= 0 && index <= steps.size(), "invalid index in insertStepAt");
steps.add(index, step);
step.setParent(parent);
// register the steps and frames if the current step is
// hooked up to the wizard
WizardStepsParent ancestor = parent;
while (ancestor != this && ancestor != null) {
ancestor = ((WizardStep)ancestor).getParent();
}
if (ancestor == this) {
addStepToCache(step);
}
// make sure next/prev pointers are still correct
WizardStep stepBefore = null;
WizardStep stepAfter = null;
if (index > 0) {
stepBefore = (WizardStep)steps.get(index-1);
stepBefore.setNextSibling(step);
step.setPreviousSibling(stepBefore);
}
int indexAfter = index + 1;
if (indexAfter < steps.size()) {
stepAfter = (WizardStep)steps.get(indexAfter);
stepAfter.setPreviousSibling(step);
step.setNextSibling(stepAfter);
}
if (parent == this) {
setTopLevelChanged(true);
}
}
/**
@aribaapi ariba
*/
public void removeStep (WizardStep step)
{
WizardStepsParent parent = step.getParent();
List steps = parent.getSteps();
int stepIndex = steps.indexOf(step);
if (stepIndex != -1) {
int indexBefore = stepIndex - 1;
int indexAfter = stepIndex + 1;
WizardStep stepBefore = null;
WizardStep stepAfter = null;
if (indexBefore >= 0) {
stepBefore = (WizardStep)steps.get(indexBefore);
}
if (indexAfter < steps.size()) {
stepAfter = (WizardStep)steps.get(indexAfter);
stepAfter.setPreviousSibling(stepBefore);
}
if (stepBefore != null) {
stepBefore.setNextSibling(stepAfter);
}
steps.remove(stepIndex);
removeStepFromCache(step);
}
if (parent == this) {
setTopLevelChanged(true);
}
}
/*-----------------------------------------------------------------------
Private/Protected Utility Methods
-----------------------------------------------------------------------*/
/**
@aribaapi private
*/
public AWPageCacheMark getPageCacheMark ()
{
return _pageCacheMark;
}
/**
@aribaapi private
*/
public void setPageCacheMark (AWPageCacheMark pageCacheMark)
{
_pageCacheMark = pageCacheMark;
}
/**
@aribaapi private
*/
protected AWResourceManager resourceManager ()
{
return _resourceManager;
}
/**
@aribaapi private
*/
protected WizardMeta meta ()
{
return _meta;
}
/**
@aribaapi private
*/
protected String localizedStringForKey (String key)
{
if (_delegate != null && key != null) {
return _delegate.localizedStringForKey(key, this);
}
return key;
}
/**
@aribaapi private
*/
protected String urlForResourceNamed (String resource)
{
return _resourceManager.urlForResourceNamed(resource);
}
/**
@aribaapi private
*/
public void setCurrentActionTarget (WizardActionTarget target)
{
if (target == _currentActionTarget) {
return;
}
if (target instanceof WizardFrame) {
WizardFrame frameTarget = (WizardFrame)target;
if (_currentActionTarget != null) {
WizardFrame currentFrame = getCurrentFrame();
if (shouldSetBackFrame(currentFrame, frameTarget)) {
frameTarget.setBackFrame(currentFrame);
}
}
WizardStep step = frameTarget.getStep();
if (step != null) {
step.setHasBeenVisited(true);
prev.setEnabled(step.getPreviousStepToDisplay() != null);
next.setEnabled(step.getNextStepToDisplay() != null);
}
}
_currentActionTarget = target;
}
private boolean shouldSetBackFrame (WizardFrame currentFrame,
WizardFrame targetFrame)
{
if (!targetFrame.isDialogFrame()) {
return false;
}
if (!currentFrame.isDialogFrame()) {
return true;
}
WizardFrame backFrame = currentFrame;
while (backFrame != null) {
if (backFrame == targetFrame) {
return false;
}
backFrame = backFrame.getBackFrame();
}
return true;
}
/**
@aribaapi private
*/
protected void assignTopLevelStepIndexes ()
{
if (_topLevelChanged) {
int numSteps = _stepList.size();
int stepIndex = 0;
for (int i = 0; i < numSteps; i++) {
WizardStep step = (WizardStep)_stepList.get(i);
if (step.isVisible()) {
step.setIndex(stepIndex);
stepIndex ++;
}
else {
step.setIndex(-1);
}
}
_numVisibleTopLevelSteps = stepIndex;
_topLevelChanged = false;
}
}
/**
@aribaapi private
*/
protected void setTopLevelChanged (boolean changed)
{
_topLevelChanged = changed;
}
/**
@aribaapi private
*/
public int getVisibleTopLevelStepSize ()
{
assignTopLevelStepIndexes();
return _numVisibleTopLevelSteps;
}
protected WizardDelegate instantiateWizardDelegate
(String delegateName)
{
return (WizardDelegate)ClassUtil.newInstance(delegateName, false);
}
protected WizardFrameDelegate instantiateWizardFrameDelegate
(String frameDelegateName)
{
return (WizardFrameDelegate)ClassUtil.newInstance(frameDelegateName, false);
}
public boolean allowsClickableSteps ()
{
return meta().allowsClickableSteps();
}
public boolean showSteps ()
{
return meta().showSteps();
}
public String getName ()
{
return meta().name();
}
}