0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU General
0007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
0008: * License("CDDL") (collectively, the "License"). You may not use this file except in
0009: * compliance with the License. You can obtain a copy of the License at
0010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
0011: * License for the specific language governing permissions and limitations under the
0012: * License. When distributing the software, include this License Header Notice in
0013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
0014: * designates this particular file as subject to the "Classpath" exception as
0015: * provided by Sun in the GPL Version 2 section of the License file that
0016: * accompanied this code. If applicable, add the following below the License Header,
0017: * with the fields enclosed by brackets [] replaced by your own identifying
0018: * information: "Portions Copyrighted [year] [name of copyright owner]"
0019: *
0020: * Contributor(s):
0021: *
0022: * The Original Software is NetBeans. The Initial Developer of the Original Software
0023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
0024: * Rights Reserved.
0025: *
0026: * If you wish your version of this file to be governed by only the CDDL or only the
0027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
0028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
0029: * you do not indicate a single choice of license, a recipient has the option to
0030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
0031: * to extend the choice of license to its licensees as provided above. However, if
0032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
0033: * the option applies only if the new code is made subject to such option by the
0034: * copyright holder.
0035: */
0036:
0037: package org.netbeans.installer.wizard;
0038:
0039: import java.io.File;
0040: import java.io.IOException;
0041: import java.lang.reflect.InvocationTargetException;
0042: import java.util.LinkedList;
0043: import java.util.List;
0044: import javax.swing.SwingUtilities;
0045: import javax.xml.XMLConstants;
0046: import javax.xml.parsers.DocumentBuilderFactory;
0047: import javax.xml.parsers.ParserConfigurationException;
0048: import javax.xml.validation.Schema;
0049: import javax.xml.validation.SchemaFactory;
0050: import org.netbeans.installer.utils.helper.PropertyContainer;
0051: import org.netbeans.installer.utils.helper.UiMode;
0052: import org.netbeans.installer.wizard.components.WizardComponent;
0053: import org.netbeans.installer.utils.ErrorManager;
0054: import org.netbeans.installer.utils.FileProxy;
0055: import org.netbeans.installer.utils.ResourceUtils;
0056: import org.netbeans.installer.utils.UiUtils;
0057: import org.netbeans.installer.utils.XMLUtils;
0058: import org.netbeans.installer.utils.exceptions.DownloadException;
0059: import org.netbeans.installer.utils.exceptions.InitializationException;
0060: import org.netbeans.installer.utils.exceptions.ParseException;
0061: import org.netbeans.installer.utils.helper.FinishHandler;
0062: import org.netbeans.installer.utils.helper.Context;
0063: import org.netbeans.installer.wizard.containers.WizardContainer;
0064: import org.netbeans.installer.wizard.containers.SwingFrameContainer;
0065: import org.w3c.dom.Document;
0066: import org.w3c.dom.Element;
0067: import org.xml.sax.SAXException;
0068:
0069: /**
0070: * This is the main class of the NBI Wizard framework. It represents the wizard as
0071: * a whole.
0072: *
0073: * <p>
0074: * The wizard serves a the main source of the data input by the user. It is
0075: * organized as a series of pages, each either informing the user about something
0076: * or displaying an input field. The input validation also happens here.
0077: *
0078: * <p>
0079: * The wizard is a singleton in some sense, i.e. only one instance of {@link Wizard}
0080: * can be created directly, via the {@link #getInstance()} method. All other
0081: * instances that may be created during the course of wizard's workflow will be the
0082: * so-called child wizards.
0083: *
0084: * <p>
0085: * The wizard operates over a list of {@link WizardComponent}s. In order to add
0086: * "depth", the user needs to create sub-wizards which would have their own
0087: * sequences of components, but will appear as a single component to the parent
0088: * wizard. This effect can be obtained via the
0089: * {@link org.netbeans.installer.wizard.components.WizardSequence} component.
0090: *
0091: * <p>
0092: * Context. Each wizard can have an associated context. A {@link Context} is a
0093: * simple collection of objects which can be fetched by their class. The context is
0094: * assigned to a wizard at runtime, thus it is possible to switch contexts basing
0095: * on some conditions. The context is intended to be read-only, though it is
0096: * possible to add new objects to it. The most common usecase for a context would
0097: * be passing an instance of an object to all the components of a wizard.
0098: *
0099: * <p>
0100: * Property container. Since the princial use case for a wizard is collecting some
0101: * user input, the values entered by the user need to be stored somewhere. For this
0102: * purpose a wizard has an associated {@link PropertyContainer}. It serves as a
0103: * storage for the user-entered strings.
0104: *
0105: * <p>
0106: * The root wizard instance will load its list of components from an URI defined in
0107: * the system property {@link #COMPONENTS_INSTANCE_URI_PROPERTY}, if the property is
0108: * not set, then it falls back to the {@link #DEFAULT_COMPONENTS_INSTANCE_URI}. For
0109: * child wizards, created with one of the
0110: * {@link #createSubWizard(List,int)} methods, expect that their lists of components
0111: * will be passed in directly. The list of components can be constructed using the
0112: * {@link #loadWizardComponents(String)} method.
0113: *
0114: * @author Kirill Sorokin
0115: * @since 1.0
0116: */
0117: public class Wizard {
0118: /////////////////////////////////////////////////////////////////////////////////
0119: // Static
0120: /**
0121: * The instance of the root wizard.
0122: */
0123: private static Wizard instance;
0124:
0125: /**
0126: * URI which points to the list of components for the root wizard instance.
0127: */
0128: private static String componentsInstanceUri;
0129:
0130: /**
0131: * URI which points to the XML Schema which will be used to validate the list
0132: * of wizard components stored in XML format.
0133: */
0134: private static String componentsSchemaUri;
0135:
0136: /**
0137: * Returns the instance of the root {@link Wizard}. If the instance does not
0138: * exist - it is created and all system properties that make sense for the
0139: * {@link Wizard} are parsed: {@link #COMPONENTS_INSTANCE_URI_PROPERTY} and
0140: * {@link #COMPONENTS_SCHEMA_URI_PROPERTY}.
0141: *
0142: * @return The instance of the root {@link Wizard}.
0143: */
0144: public static synchronized Wizard getInstance() {
0145: if (instance == null) {
0146: // initialize uri for root wizard's components list
0147: if (System.getProperty(COMPONENTS_INSTANCE_URI_PROPERTY) != null) {
0148: componentsInstanceUri = System
0149: .getProperty(COMPONENTS_INSTANCE_URI_PROPERTY);
0150: } else {
0151: componentsInstanceUri = DEFAULT_COMPONENTS_INSTANCE_URI;
0152: }
0153:
0154: // initialize uri for components list xml schema
0155: if (System.getProperty(COMPONENTS_SCHEMA_URI_PROPERTY) != null) {
0156: componentsSchemaUri = System
0157: .getProperty(COMPONENTS_SCHEMA_URI_PROPERTY);
0158: } else {
0159: componentsSchemaUri = DEFAULT_COMPONENTS_SCHEMA_URI;
0160: }
0161:
0162: // create the root wizard and load its components
0163: instance = new Wizard();
0164: try {
0165: instance.components = loadWizardComponents(componentsInstanceUri);
0166: } catch (InitializationException e) {
0167: ErrorManager.notifyCritical(ResourceUtils.getString(
0168: Wizard.class,
0169: RESOURCE_FAILED_TO_CREATE_INSTANCE), e);
0170: }
0171: }
0172:
0173: return instance;
0174: }
0175:
0176: /**
0177: * Loads the list of {@link WizardComponent} from an XML file identified by its
0178: * URI. The URI can be of any scheme supported by the
0179: * {@link org.netbeans.installer.downloader.DownloadManager}.
0180: *
0181: * @param componentsUri URI of the XML file which contains the list of
0182: * {@link WizardComponent}s.
0183: * @return The list of {@link WizardComponent} defined in the XML file.
0184: * @throws org.netbeans.installer.utils.exceptions.InitializationException If an
0185: * error occurs during loading of the list.
0186: */
0187: public static List<WizardComponent> loadWizardComponents(
0188: final String componentsUri) throws InitializationException {
0189: return loadWizardComponents(componentsUri, Wizard.class
0190: .getClassLoader());
0191: }
0192:
0193: /**
0194: * Loads the list of {@link WizardComponent} from an XML file identified by its
0195: * URI and using the specified {@link ClassLoader} to load the components'
0196: * classes. The URI can be of any scheme supported by the
0197: * {@link org.netbeans.installer.downloader.DownloadManager}.
0198: *
0199: * @param componentsUri URI of the XML file which contains the list of
0200: * {@link WizardComponent}.
0201: * @param classLoader Instance of {@link ClassLoader} which should be used for
0202: * loading the components' classes.
0203: * @return The list of {@link WizardComponent} defined in the XML file.
0204: * @throws org.netbeans.installer.utils.exceptions.InitializationException If an
0205: * error occurs during loading of the list.
0206: */
0207: public static List<WizardComponent> loadWizardComponents(
0208: final String componentsUri, final ClassLoader classLoader)
0209: throws InitializationException {
0210: try {
0211: final File schemaFile = FileProxy.getInstance().getFile(
0212: componentsSchemaUri, classLoader, true);
0213: final File componentsFile = FileProxy.getInstance()
0214: .getFile(componentsUri, classLoader, true);
0215:
0216: final Schema schema = SchemaFactory.newInstance(
0217: XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
0218: schemaFile);
0219:
0220: final DocumentBuilderFactory factory = DocumentBuilderFactory
0221: .newInstance();
0222: try {
0223: factory.setSchema(schema);
0224: } catch (UnsupportedOperationException e) {
0225: // if the parser does not support schemas, let it be -- we can do
0226: // without it anyway -- just log it and proceed
0227: ErrorManager.notifyDebug(ResourceUtils.getString(
0228: Wizard.class,
0229: RESOURCE_PARSER_UNSUPPORTS_SCHEMAS, factory
0230: .getClass()), e);
0231: }
0232: factory.setNamespaceAware(true);
0233:
0234: final Document document = factory.newDocumentBuilder()
0235: .parse(componentsFile);
0236:
0237: return loadWizardComponents(document.getDocumentElement(),
0238: classLoader);
0239: } catch (DownloadException e) {
0240: throw new InitializationException(ResourceUtils.getString(
0241: Wizard.class, RESOURCE_FAILED_TO_LOAD_COMPONENTS,
0242: componentsUri, classLoader), e);
0243: } catch (ParserConfigurationException e) {
0244: throw new InitializationException(ResourceUtils.getString(
0245: Wizard.class, RESOURCE_FAILED_TO_LOAD_COMPONENTS,
0246: componentsUri, classLoader), e);
0247: } catch (SAXException e) {
0248: throw new InitializationException(ResourceUtils.getString(
0249: Wizard.class, RESOURCE_FAILED_TO_LOAD_COMPONENTS,
0250: componentsUri, classLoader), e);
0251: } catch (IOException e) {
0252: throw new InitializationException(ResourceUtils.getString(
0253: Wizard.class, RESOURCE_FAILED_TO_LOAD_COMPONENTS,
0254: componentsUri, classLoader), e);
0255: }
0256: }
0257:
0258: // private //////////////////////////////////////////////////////////////////////
0259: /**
0260: * Loads the list of {@link WizardComponent} from the DOM tree identified by its
0261: * root {@link Element} using the specified {@link ClassLoader} to load the
0262: * components' classes.
0263: *
0264: * @param element Root of the DOM tree from which to load the list.
0265: * @param classLoader Instance of {@link ClassLoader} which should be used for
0266: * loading the components' classes.
0267: * @return The list of {@link WizardComponent} defined in the DOM tree.
0268: * @throws org.netbeans.installer.utils.exceptions.InitializationException If an
0269: * error occurs during loading of the list.
0270: */
0271: private static List<WizardComponent> loadWizardComponents(
0272: final Element element, final ClassLoader classLoader)
0273: throws InitializationException {
0274: final List<WizardComponent> components = new LinkedList<WizardComponent>();
0275:
0276: for (Element child : XMLUtils.getChildren(element,
0277: TAG_COMPONENT)) {
0278: components.add(loadWizardComponent(child, classLoader));
0279: }
0280:
0281: return components;
0282: }
0283:
0284: /**
0285: * Loads a single {@link WizardComponent} from a DOM {@link Element} using the
0286: * specified {@link ClassLoader} to load the component's class.
0287: *
0288: * @param element DOM {@link Element} from which the component's data should be
0289: * loaded.
0290: * @param classLoader Instance of {@link ClassLoader} which should be used for
0291: * loading the component's class.
0292: * @return The loaded {@link WizardComponent}.
0293: * @throws org.netbeans.installer.utils.exceptions.InitializationException If an
0294: * error occurs during loading of the component.
0295: */
0296: private static WizardComponent loadWizardComponent(
0297: final Element element, final ClassLoader classLoader)
0298: throws InitializationException {
0299: final WizardComponent component;
0300:
0301: try {
0302: component = (WizardComponent) classLoader.loadClass(
0303: element.getAttribute(ATTRIBUTE_CLASS))
0304: .newInstance();
0305:
0306: Element child = XMLUtils.getChild(element, TAG_COMPONENTS);
0307: if (child != null) {
0308: component.addChildren(loadWizardComponents(child,
0309: classLoader));
0310: }
0311:
0312: child = XMLUtils.getChild(element, TAG_PROPERTIES);
0313: if (child != null) {
0314: component.getProperties().putAll(
0315: XMLUtils.parseNbiProperties(child));
0316: }
0317: } catch (ParseException e) {
0318: throw new InitializationException(ResourceUtils.getString(
0319: Wizard.class, RESOURCE_FAILED_TO_LOAD_COMPONENT,
0320: element, classLoader), e);
0321: } catch (ClassNotFoundException e) {
0322: throw new InitializationException(ResourceUtils.getString(
0323: Wizard.class, RESOURCE_FAILED_TO_LOAD_COMPONENT,
0324: element, classLoader), e);
0325: } catch (IllegalAccessException e) {
0326: throw new InitializationException(ResourceUtils.getString(
0327: Wizard.class, RESOURCE_FAILED_TO_LOAD_COMPONENT,
0328: element, classLoader), e);
0329: } catch (InstantiationException e) {
0330: throw new InitializationException(ResourceUtils.getString(
0331: Wizard.class, RESOURCE_FAILED_TO_LOAD_COMPONENT,
0332: element, classLoader), e);
0333: }
0334:
0335: return component;
0336: }
0337:
0338: /////////////////////////////////////////////////////////////////////////////////
0339: // Instance
0340: /**
0341: * List of the {@link WizardComponent} over which the {@link Wizard} should
0342: * iterate.
0343: */
0344: private List<WizardComponent> components;
0345:
0346: /**
0347: * Container which should display the UI of the wizard.
0348: */
0349: private WizardContainer container;
0350:
0351: /**
0352: * Storage for the user input in the panels of the wizard.
0353: */
0354: private PropertyContainer propertyContainer;
0355:
0356: /**
0357: * {@link Wizard}'s {@link Context}.
0358: */
0359: private Context context;
0360:
0361: /**
0362: * {@link ClassLoader} that should be used by the wizard. E.g. for loading
0363: * resources.
0364: */
0365: private ClassLoader classLoader;
0366:
0367: /**
0368: * Index of the currently active component.
0369: */
0370: private int index;
0371:
0372: /**
0373: * Parent of the {@link Wizard}. It is <code>null</code> if the current
0374: * {@link Wizard} is the root one.
0375: */
0376: private Wizard parent;
0377:
0378: /**
0379: * Handler of the <code>cancel</code> and <code>finish</code> requests which
0380: * happen either by user request or as part of the normal wizard work flow.
0381: */
0382: private FinishHandler finishHandler;
0383:
0384: /**
0385: * Specifies whether the wizard is opened in blocking mode. If it is, the
0386: * opening method {@link #openBlocking()} will not return intil the wizard is
0387: * closed from another thread.
0388: */
0389: private boolean blocking;
0390:
0391: // constructors /////////////////////////////////////////////////////////////////
0392: /**
0393: * Default constructor. Performs initialization of the basic properties, which,
0394: * however, is not enough for normal operation - the list of
0395: * {@link WizardComponent} is not initialized.
0396: */
0397: public Wizard() {
0398: this .index = -1;
0399: this .context = new Context();
0400: this .classLoader = getClass().getClassLoader();
0401:
0402: this .blocking = false;
0403: }
0404:
0405: /**
0406: * Constructs a new instance of {@link Wizard} with the given parent. Most of
0407: * the properties of the new instance are inherited from the parent.
0408: *
0409: * @param parent Parent {@link Wizard}.
0410: */
0411: public Wizard(final Wizard parent) {
0412: this ();
0413:
0414: this .parent = parent;
0415: if (this .parent != null) {
0416: this .container = parent.container;
0417:
0418: this .propertyContainer = parent.propertyContainer;
0419: this .context = new Context(parent.context);
0420: this .classLoader = parent.classLoader;
0421:
0422: this .finishHandler = parent.finishHandler;
0423: }
0424: }
0425:
0426: /**
0427: * Constructs a new instance of {@link Wizard} with the given parent, list of
0428: * {@link WizardComponent} and inital index of the active component.
0429: *
0430: * @param parent Parent {@link Wizard}.
0431: * @param components List of {@link WizardComponent}s over which the wizard
0432: * should iterate.
0433: * @param index Initial index of the active component.
0434: */
0435: public Wizard(final Wizard parent,
0436: final List<WizardComponent> components, int index) {
0437: this (parent);
0438:
0439: this .components = components;
0440: this .index = index;
0441: }
0442:
0443: /**
0444: * Constructs a new instance of {@link Wizard} with the given parent, list of
0445: * {@link WizardComponent}, inital index of the active component, properties
0446: * container and classloader.
0447: *
0448: * @param parent Parent {@link Wizard}.
0449: * @param components List of {@link WizardComponent} over which the wizard
0450: * should iterate.
0451: * @param index Initial index of the active component.
0452: * @param propertyContainer {@link PropertyContainer} which should be used by
0453: * the wizard.
0454: * @param classLoader {@link ClassLoader} which should be used by the wizard.
0455: */
0456: public Wizard(final Wizard parent,
0457: final List<WizardComponent> components, final int index,
0458: final PropertyContainer propertyContainer,
0459: final ClassLoader classLoader) {
0460: this (parent, components, index);
0461:
0462: this .propertyContainer = propertyContainer;
0463: this .classLoader = classLoader;
0464: }
0465:
0466: // wizard lifecycle control methods /////////////////////////////////////////////
0467: /**
0468: * Opens the wizard. Depending on the current UI mode, an appropriate
0469: * {@link WizardContainer} is chosen, initialized and set to be visible. No
0470: * wizard container is created if the UI Mode is {@link UiMode#SILENT}.
0471: *
0472: * <p>
0473: * If the current wizard is not the root one - the parent's {@link #open()}
0474: * method is called.
0475: */
0476: public void open() {
0477: // if a parent exists, ask it - it knows better
0478: if (parent != null) {
0479: parent.open();
0480: return;
0481: }
0482:
0483: // init the look and feel
0484: try {
0485: UiUtils.initializeLookAndFeel();
0486: } catch (InitializationException e) {
0487: ErrorManager.notifyWarning(e.getMessage(), e.getCause());
0488: }
0489:
0490: // then create the container according to the current UI mode
0491: switch (UiMode.getCurrentUiMode()) {
0492: case SWING:
0493: container = new SwingFrameContainer();
0494:
0495: try {
0496: SwingUtilities.invokeAndWait(new Runnable() {
0497: public void run() {
0498: Thread.currentThread()
0499: .setUncaughtExceptionHandler(
0500: ErrorManager
0501: .getExceptionHandler());
0502: }
0503: });
0504: } catch (InvocationTargetException e) {
0505: ErrorManager.notifyDebug(ResourceUtils.getString(
0506: Wizard.class,
0507: RESOURCE_FAILED_TO_ATTACH_ERROR_HANDLER), e);
0508: } catch (InterruptedException e) {
0509: ErrorManager.notifyDebug(ResourceUtils.getString(
0510: Wizard.class,
0511: RESOURCE_FAILED_TO_ATTACH_ERROR_HANDLER), e);
0512: }
0513:
0514: SwingUtilities.invokeLater(new Runnable() {
0515: public void run() {
0516: container.setVisible(true);
0517: }
0518: });
0519: break;
0520: case SILENT:
0521: // we don't have to initialize anything for silent mode
0522: break;
0523: default:
0524: ErrorManager.notifyCritical(ResourceUtils.getString(
0525: Wizard.class, RESOURCE_UNKNOWN_UI_MODE, UiMode
0526: .getCurrentUiMode()));
0527: }
0528:
0529: next();
0530: }
0531:
0532: /**
0533: * Opens the wizard in a blocking mode. As opposed to {@link #open()}, this
0534: * method will not return the wizard is closed from another thread.
0535: *
0536: * @see #open()
0537: */
0538: public void openBlocking() {
0539: this .blocking = true;
0540:
0541: open();
0542:
0543: while (blocking) {
0544: try {
0545: wait();
0546: } catch (InterruptedException e) {
0547: ErrorManager.notifyDebug(ResourceUtils.getString(
0548: Wizard.class, RESOURCE_INTERRUPTED), e);
0549: }
0550: }
0551: }
0552:
0553: /**
0554: * Closes the wizard. The current {@link WizardContainer} is hidden and
0555: * deinitialized. No real action is taken if the UI mode is
0556: * {@link UiMode#SILENT}.
0557: *
0558: * <p>
0559: * If the current wizard is not the ro ot one - the parent's {@link #close()}
0560: * method is called.
0561: */
0562: public void close() {
0563: // if a parent exists, ask it - it knows better
0564: if (parent != null) {
0565: parent.close();
0566: return;
0567: }
0568:
0569: switch (UiMode.getCurrentUiMode()) {
0570: case SWING:
0571: // if the container has not yet been initialized -- we do not need to
0572: // do anything with it
0573: if (container != null) {
0574: container.setVisible(false);
0575: }
0576: break;
0577: case SILENT:
0578: // we don't have to initialize anything for silent mode
0579: break;
0580: default:
0581: ErrorManager.notifyCritical(ResourceUtils.getString(
0582: Wizard.class, RESOURCE_UNKNOWN_UI_MODE, UiMode
0583: .getCurrentUiMode()));
0584: }
0585:
0586: if (blocking) {
0587: blocking = false;
0588: notifyAll();
0589: }
0590: }
0591:
0592: // component flow control methods ///////////////////////////////////////////////
0593: /**
0594: * Proceeds to the next element in the wizard. If the next element is not
0595: * available and the current wizard instance is the root one, then the wizard is
0596: * considered to be at the last element and the wizard's
0597: * {@link FinishHandler#finish()} is called.
0598: *
0599: * <p>
0600: * If the current wizard is not the root instance and there is no next component,
0601: * then the parent wizard's {@link #next()} method is called.
0602: */
0603: public void next() {
0604: final WizardComponent component = getNext();
0605:
0606: // if there is no next component in the current wizard, try to delegate
0607: // the call to the parent wizard, and if there is no parent wizard... finish
0608: // the sequence, and call the finish handler
0609: if (component != null) {
0610: index = components.indexOf(component);
0611:
0612: component.setWizard(this );
0613: component.initialize();
0614:
0615: switch (UiMode.getCurrentUiMode()) {
0616: case SWING:
0617: if (component.getWizardUi() != null) {
0618: container.updateWizardUi(component.getWizardUi());
0619: }
0620: break;
0621: case SILENT:
0622: // nothing special should be done for silent mode
0623: break;
0624: default:
0625: ErrorManager.notifyCritical(ResourceUtils.getString(
0626: Wizard.class, RESOURCE_UNKNOWN_UI_MODE, UiMode
0627: .getCurrentUiMode()));
0628: }
0629:
0630: System.setProperty(CURRENT_COMPONENT_CLASSNAME_PROPERTY,
0631: component.getClass().getName());
0632:
0633: component.executeForward();
0634: } else if (parent != null) {
0635: parent.next();
0636: } else {
0637: finishHandler.finish();
0638: }
0639: }
0640:
0641: /**
0642: * Moves to the previous element in the wizard. If the previous component is not
0643: * available and the current wizard instance is the root one a critical error is
0644: * shown and the application terminates.
0645: *
0646: * <p>
0647: * If the current wizard is not the root one and there is no previous component
0648: * in the current sequence, the parent's {@link #previous()} method is called.
0649: */
0650: public void previous() {
0651: final WizardComponent component = getPrevious();
0652:
0653: // if there is no previous component in the current wizard, try to delegate
0654: // the call to the parent wizard, and if there is no parent wizard... we
0655: // should be here in the first place
0656: if (component != null) {
0657: index = components.indexOf(component);
0658:
0659: component.setWizard(this );
0660: component.initialize();
0661:
0662: switch (UiMode.getCurrentUiMode()) {
0663: case SWING:
0664: if (component.getWizardUi() != null) {
0665: container.updateWizardUi(component.getWizardUi());
0666: }
0667: break;
0668: case SILENT:
0669: ErrorManager.notifyCritical(ResourceUtils.getString(
0670: Wizard.class,
0671: RESOURCE_CANNOT_MOVE_BACKWARD_SILENT));
0672: break;
0673: default:
0674: ErrorManager.notifyCritical(ResourceUtils.getString(
0675: Wizard.class, RESOURCE_UNKNOWN_UI_MODE, UiMode
0676: .getCurrentUiMode()));
0677: }
0678:
0679: System.setProperty(CURRENT_COMPONENT_CLASSNAME_PROPERTY,
0680: component.getClass().getName());
0681:
0682: component.executeBackward();
0683: } else if (parent != null) {
0684: parent.previous();
0685: } else {
0686: ErrorManager.notifyCritical(ResourceUtils.getString(
0687: Wizard.class,
0688: RESOURCE_CANNOT_MOVE_BACKWARD_AT_FIRST));
0689: }
0690: }
0691:
0692: // informational methods ////////////////////////////////////////////////////////
0693: /**
0694: * Checks whether there is exists a component which can be reached using the
0695: * {@link #next()} method. This method checks both the components of the
0696: * current wizard and the parent one.
0697: *
0698: * @return <code>true</code> is there is a next element, <code>false</code>
0699: * otherwise.
0700: */
0701: public boolean hasNext() {
0702: // if there is no next component in the current wizard, we should check the
0703: // parent wizard if it has one
0704: return (getNext() != null)
0705: || ((parent != null) && parent.hasNext());
0706: }
0707:
0708: /**
0709: * Checks whether there is exists a component which can be reached using the
0710: * {@link #previous()} method. This method checks both the components of the
0711: * current wizard and the parent one.
0712: *
0713: * @return <code>true</code> is there is a previous element, <code>false</code>
0714: * otherwise.
0715: */
0716: public boolean hasPrevious() {
0717: // if current component is a point of no return - we cannot move backwards,
0718: // i.e. there is no previous component
0719: if ((getCurrent() != null) && getCurrent().isPointOfNoReturn()) {
0720: return false;
0721: }
0722:
0723: for (int i = index - 1; i > -1; i--) {
0724: final WizardComponent component = components.get(i);
0725:
0726: // if the component can be executed backward it is the previous one
0727: if (component.canExecuteBackward()) {
0728: return true;
0729: }
0730:
0731: // if the currently examined component is a point of no return and it
0732: // cannot be executed (since we passed the previous statement) - we have
0733: // no previous component
0734: if (component.isPointOfNoReturn()) {
0735: return false;
0736: }
0737: }
0738:
0739: // if we got this far, there is not previous component in the current wizard,
0740: // but no points of no return we encountered either. thus we should ask the
0741: // parent wizard if it has a previous component
0742: return (parent != null) && parent.hasPrevious();
0743: }
0744:
0745: // getters/setters //////////////////////////////////////////////////////////////
0746: /**
0747: * Returns the index of the currently active component.
0748: *
0749: * @return Index of the currently active component.
0750: */
0751: public int getIndex() {
0752: return index;
0753: }
0754:
0755: /**
0756: * Returns the {@link WizardContainer} which is used by this {@link Wizard}
0757: * instance.
0758: *
0759: * @return {@link WizardContainer} which is used by this {@link Wizard}
0760: * instance.
0761: */
0762: public WizardContainer getContainer() {
0763: return container;
0764: }
0765:
0766: /**
0767: * Gets the value of the property with the given name. This method in turn calls
0768: * the {@link PropertyContainer#getProperty(String)} method on the property
0769: * container used by this wizard instance.
0770: *
0771: * @param name Name of the property whose value should be obtained.
0772: * @return Value of the specified property, or <code>null</code> is the property
0773: * with the given name does not exist.
0774: */
0775: public String getProperty(final String name) {
0776: return propertyContainer.getProperty(name);
0777: }
0778:
0779: /**
0780: * Sets the value of the property with the given name to the given value. This
0781: * method in turn calls the {@link PropertyContainer#setProperty(String,String)}
0782: * method on the property container used by this wizard instance.
0783: *
0784: * @param name Name of the property whose value should be set.
0785: * @param value New value for the property.
0786: */
0787: public void setProperty(final String name, final String value) {
0788: propertyContainer.setProperty(name, value);
0789: }
0790:
0791: /**
0792: * Returns the {@link Context} of this {@link Wizard} instance.
0793: *
0794: * @return {@link Context} of this {@link Wizard} instance.
0795: */
0796: public Context getContext() {
0797: return context;
0798: }
0799:
0800: /**
0801: * Returns the {@link ClassLoader} used by this {@link Wizard} instance.
0802: *
0803: * @return {@link ClassLoader} used by this {@link Wizard} instance.
0804: */
0805: public ClassLoader getClassLoader() {
0806: return classLoader;
0807: }
0808:
0809: /**
0810: * Returns the {@link FinishHandler} used by this {@link Wizard} instance.
0811: *
0812: * @return {@link FinishHandler} used by this {@link Wizard} instance.
0813: */
0814: public FinishHandler getFinishHandler() {
0815: return finishHandler;
0816: }
0817:
0818: /**
0819: * Sets the {@link FinishHandler} which should be used by this {@link Wizard}
0820: * instance.
0821: *
0822: *
0823: * @param finishHandler {@link FinishHandler} which should be used by this
0824: * {@link Wizard} instance.
0825: */
0826: public void setFinishHandler(final FinishHandler finishHandler) {
0827: this .finishHandler = finishHandler;
0828: }
0829:
0830: // factory methods for children /////////////////////////////////////////////////
0831: /**
0832: * Creates a new child (or sub-) wizard with the given list of
0833: * {@link WizardComponent}s and the initial index of the active component.
0834: *
0835: * @param components List of {@link WizardComponent}s over which the child
0836: * wizard should iterate.
0837: * @param index Initial index of the active component.
0838: * @return New child (sub-) wizard.
0839: */
0840: public Wizard createSubWizard(
0841: final List<WizardComponent> components, final int index) {
0842: return new Wizard(this , components, index);
0843: }
0844:
0845: /**
0846: * Creates a new child (or sub-) wizard with the given list of
0847: * {@link WizardComponent}s, initial index of the active component, property
0848: * container and class loader.
0849: *
0850: * @param components List of {@link WizardComponent}s over which the child
0851: * wizard should iterate.
0852: * @param index Initial index of the active component.
0853: * @param propertyContainer {@link PropertyContainer} which should be used by
0854: * the child wizard.
0855: * @param classLoader {@link ClassLoader} which should be used by the child
0856: * wizard.
0857: * @return New child (sub-) wizard.
0858: */
0859: public Wizard createSubWizard(
0860: final List<WizardComponent> components, final int index,
0861: final PropertyContainer propertyContainer,
0862: final ClassLoader classLoader) {
0863: return new Wizard(this , components, index, propertyContainer,
0864: classLoader);
0865: }
0866:
0867: // private //////////////////////////////////////////////////////////////////////
0868: /**
0869: * Returns the currently active {@link WizardComponent}.
0870: *
0871: * @return Currently active {@link WizardComponent}, or <code>null</code> if the
0872: * index of the active component is outside the list size.
0873: */
0874: private WizardComponent getCurrent() {
0875: if ((index > -1) && (index < components.size())) {
0876: return components.get(index);
0877: } else {
0878: return null;
0879: }
0880: }
0881:
0882: /**
0883: * Returns the previous component in the <b>current</b> wizard's components
0884: * list.
0885: *
0886: * @return Previous {@link WizardComponent} or <code>null</code> if there is no
0887: * previous component.
0888: */
0889: private WizardComponent getPrevious() {
0890: // if current component is a point of no return - we cannot move backwards,
0891: // i.e. there is no previous component
0892: if ((getCurrent() != null) && getCurrent().isPointOfNoReturn()) {
0893: return null;
0894: }
0895:
0896: for (int i = index - 1; i > -1; i--) {
0897: final WizardComponent component = components.get(i);
0898:
0899: // if the component can be executed backward it is the previous one
0900: if (component.canExecuteBackward()) {
0901: return component;
0902: }
0903:
0904: // if the currently examined component is a point of no return and it
0905: // cannot be executed (since we passed the previous statement) - we have
0906: // no previous component
0907: if (component.isPointOfNoReturn()) {
0908: return null;
0909: }
0910: }
0911:
0912: // if we reached the before-first index and yet could not find a previous
0913: // component, then there is no previous component
0914: return null;
0915: }
0916:
0917: /**
0918: * Returns the next component in the <b>current</b> wizard's components
0919: * list.
0920: *
0921: * @return Next {@link WizardComponent} or <code>null</code> if there is no
0922: * next component.
0923: */
0924: private WizardComponent getNext() {
0925: for (int i = index + 1; i < components.size(); i++) {
0926: final WizardComponent component = components.get(i);
0927:
0928: // if the component can be executed forward it is the next one
0929: if (component.canExecuteForward()) {
0930: return component;
0931: }
0932: }
0933:
0934: // if we reached the after-last index and yet could not find a next
0935: // component, then there is no next component
0936: return null;
0937: }
0938:
0939: /////////////////////////////////////////////////////////////////////////////////
0940: // Constants
0941: /**
0942: * Name of the system property which is expected to contain the URI which points
0943: * to the XML file with the list of components for the root wizard.
0944: */
0945: public static final String COMPONENTS_INSTANCE_URI_PROPERTY = "nbi.wizard.components.instance.uri"; // NOI18N
0946:
0947: /**
0948: * Default URI which points to the XML file with the list of components for the
0949: * root wizard.
0950: */
0951: public static final String DEFAULT_COMPONENTS_INSTANCE_URI = FileProxy.RESOURCE_SCHEME_PREFIX
0952: + "org/netbeans/installer/wizard/wizard-components.xml"; // NOI18N
0953:
0954: /**
0955: * Name of the system property which is expected to contain the URI which points
0956: * to the XML schema which defines the format for the serialized list of wizard
0957: * components.
0958: */
0959: public static final String COMPONENTS_SCHEMA_URI_PROPERTY = "nbi.wizard.components.schema.uri"; // NOI18N
0960:
0961: /**
0962: * Default URI which points to the XML schema which defines the format for the
0963: * serialized list of wizard components.
0964: */
0965: public static final String DEFAULT_COMPONENTS_SCHEMA_URI = FileProxy.RESOURCE_SCHEME_PREFIX
0966: + "org/netbeans/installer/wizard/wizard-components.xsd"; // NOI18N
0967:
0968: /**
0969: * Name of the XML tag which describes a list of components.
0970: */
0971: public static final String TAG_COMPONENTS = "components"; // NOI18N
0972:
0973: /**
0974: * Name of the XML tag which describes an individual component.
0975: */
0976: public static final String TAG_COMPONENT = "component"; // NOI18N
0977:
0978: /**
0979: * Name of the XMl tag which describes the properties of a component.
0980: */
0981: public static final String TAG_PROPERTIES = "properties"; // NOI18N
0982:
0983: /**
0984: * Name of the XML attribute which contains the classname of a component.
0985: */
0986: public static final String ATTRIBUTE_CLASS = "class"; // NOI18N
0987:
0988: /**
0989: * Name of the system property which will be set when a component executes. Its
0990: * value will be the fully qualified class name of the component.
0991: */
0992: public static final String CURRENT_COMPONENT_CLASSNAME_PROPERTY = "nbi.wizard.current.component.classname"; // NOI18N
0993:
0994: // private //////////////////////////////////////////////////////////////////////
0995: /**
0996: * Name of a resource bundle entry.
0997: */
0998: private static final String RESOURCE_FAILED_TO_CREATE_INSTANCE = "W.error.failed.to.create.instance"; // NOI18N
0999:
1000: /**
1001: * Name of a resource bundle entry.
1002: */
1003: private static final String RESOURCE_FAILED_TO_LOAD_COMPONENTS = "W.error.failed.to.load.components"; // NOI18N
1004:
1005: /**
1006: * Name of a resource bundle entry.
1007: */
1008: private static final String RESOURCE_FAILED_TO_LOAD_COMPONENT = "W.error.failed.to.load.component"; // NOI18N
1009:
1010: /**
1011: * Name of a resource bundle entry.
1012: */
1013: private static final String RESOURCE_FAILED_TO_ATTACH_ERROR_HANDLER = "W.error.failed.to.attach.error.handler"; // NOI18N
1014:
1015: /**
1016: * Name of a resource bundle entry.
1017: */
1018: private static final String RESOURCE_CANNOT_MOVE_BACKWARD_SILENT = "W.error.cannot.move.backward.silent"; // NOI18N
1019:
1020: /**
1021: * Name of a resource bundle entry.
1022: */
1023: private static final String RESOURCE_UNKNOWN_UI_MODE = "W.error.unknown.ui.mode"; // NOI18N
1024:
1025: /**
1026: * Name of a resource bundle entry.
1027: */
1028: private static final String RESOURCE_CANNOT_MOVE_BACKWARD_AT_FIRST = "W.error.cannot.move.backward.at.first"; // NOI18N
1029:
1030: /**
1031: * Name of a resource bundle entry.
1032: */
1033: private static final String RESOURCE_PARSER_UNSUPPORTS_SCHEMAS = "W.error.parser.unsupports.schemas";//NOI18N
1034:
1035: /**
1036: * Name of a resource bundle entry.
1037: */
1038: private static final String RESOURCE_INTERRUPTED = "W.error.interrupted";//NOI18N
1039: }
|