001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU General
007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
008: * License("CDDL") (collectively, the "License"). You may not use this file except in
009: * compliance with the License. You can obtain a copy of the License at
010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
011: * License for the specific language governing permissions and limitations under the
012: * License. When distributing the software, include this License Header Notice in
013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
014: * designates this particular file as subject to the "Classpath" exception as
015: * provided by Sun in the GPL Version 2 section of the License file that
016: * accompanied this code. If applicable, add the following below the License Header,
017: * with the fields enclosed by brackets [] replaced by your own identifying
018: * information: "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Contributor(s):
021: *
022: * The Original Software is NetBeans. The Initial Developer of the Original Software
023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
024: * Rights Reserved.
025: *
026: * If you wish your version of this file to be governed by only the CDDL or only the
027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
029: * you do not indicate a single choice of license, a recipient has the option to
030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
031: * to extend the choice of license to its licensees as provided above. However, if
032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
033: * the option applies only if the new code is made subject to such option by the
034: * copyright holder.
035: */
036:
037: package org.netbeans.installer.wizard.components;
038:
039: import java.io.File;
040: import java.io.InputStream;
041: import java.util.ArrayList;
042: import java.util.List;
043: import javax.swing.JComponent;
044: import org.netbeans.installer.utils.ResourceUtils;
045: import org.netbeans.installer.utils.SystemUtils;
046: import org.netbeans.installer.utils.UiUtils;
047: import org.netbeans.installer.utils.helper.NbiProperties;
048: import org.netbeans.installer.utils.helper.swing.NbiButton;
049: import org.netbeans.installer.wizard.ui.SwingUi;
050: import org.netbeans.installer.wizard.containers.SwingContainer;
051: import org.netbeans.installer.wizard.Wizard;
052: import org.netbeans.installer.wizard.ui.WizardUi;
053:
054: /**
055: * This class represents a single unit of a {@link Wizard} sequence. It is a logical
056: * abstraction of a wizard step, should normally be accompanied by a
057: * {@link WizardUi} instance.
058: *
059: * <p>
060: * This class provides a bunch of methods which are used by the wizard in order to
061: * execute the component, devise whether is can be executed or can be skipped,
062: * whether the user is allowed to skip the component, etc.
063: *
064: * <p>
065: * The basic infrastructure used by the concrete implementations of the
066: * {@link WizardComponent} class is also defined here: support for child components
067: * and properties.
068: *
069: * @author Kirill Sorokin
070: * @since 1.0
071: */
072: public abstract class WizardComponent {
073: /////////////////////////////////////////////////////////////////////////////////
074: // Instance
075: /**
076: * {@link Wizard} which currently executes this component. This field is
077: * populated at the moment the component is executed, thus it is unsafe to
078: * assume that it holds a correct value unless it is known that the component
079: * is active.
080: */
081: private Wizard wizard;
082:
083: /**
084: * List of child components. It is not expected that all implementations will
085: * make use of this field, thus the presence of children may simply be ignored
086: * by some.
087: */
088: private List<WizardComponent> children;
089:
090: /**
091: * Component's properties. These should not be mixed up with the properties
092: * available through {@link Wizard#getProperty(String)} and
093: * {@link Wizard#setProperty(String,String)} - these are internal to the
094: * component, while the wizard's ones are expected to keep the user input.
095: */
096: private NbiProperties properties;
097:
098: // constructor //////////////////////////////////////////////////////////////////
099: /**
100: * Creates a new instance of {@link WizardComponent}. This is the default
101: * <code>protected</code> constructor which must be called by the concrete
102: * implementations. It initializes the fields above and sets some default
103: * properties.
104: */
105: protected WizardComponent() {
106: children = new ArrayList<WizardComponent>();
107: properties = new NbiProperties();
108:
109: setProperty(TITLE_PROPERTY, DEFAULT_TITLE);
110: setProperty(DESCRIPTION_PROPERTY, DEFAULT_DESCRIPTION);
111:
112: setProperty(HELP_BUTTON_TEXT_PROPERTY, DEFAULT_HELP_BUTTON_TEXT);
113: setProperty(BACK_BUTTON_TEXT_PROPERTY, DEFAULT_BACK_BUTTON_TEXT);
114: setProperty(NEXT_BUTTON_TEXT_PROPERTY, DEFAULT_NEXT_BUTTON_TEXT);
115: setProperty(CANCEL_BUTTON_TEXT_PROPERTY,
116: DEFAULT_CANCEL_BUTTON_TEXT);
117: setProperty(FINISH_BUTTON_TEXT_PROPERTY,
118: DEFAULT_FINISH_BUTTON_TEXT);
119: }
120:
121: // execution flow ///////////////////////////////////////////////////////////////
122: /**
123: * This method is called when the component was reached with the
124: * {@link Wizard#next()} method. It is meant to perform the operations required
125: * by the concrete component, such as perform a search of some sort, etc.
126: *
127: * <p>
128: * This method is called after {@link #setWizard(Wizard)}, {@link #initialize()}
129: * and {@link #getWizardUi()}, thus it is safe to assume that the wizard field
130: * has been correctly initialized and the UI (if it exists) was shown.
131: */
132: public abstract void executeForward();
133:
134: /**
135: * This method is called when the component was reached with the
136: * {@link Wizard#previous()} method. It is meant to perform the operations
137: * required by the concrete component, such as perform a search of some sort,
138: * etc.
139: *
140: * <p>
141: * This method is called after {@link #setWizard(Wizard)}, {@link #initialize()}
142: * and {@link #getWizardUi()}, thus it is safe to assume that the wizard field
143: * has been correctly initialized and the UI (if it exists) was shown.
144: */
145: public abstract void executeBackward();
146:
147: /**
148: * This method is called every time a wizard reaches this component. Unlike the
149: * {@link #executeForward()} and {@link #executeBackward()} methods this one is
150: * called <b>before</b> the UI is shown and thus is intended to be used to
151: * prepare the model or data for use by the UI.
152: *
153: * <p>
154: * This method is called after {@link #setWizard(Wizard)}, thus it is safe to
155: * assume that the wizard field has been correctly initialized.
156: */
157: public abstract void initialize();
158:
159: /**
160: * This method is called by the wizard in order to find out whether this
161: * component can be reached via {@link Wizard#next()}.
162: *
163: * <p>
164: * It's important to note that if this method returns <code>false</code>, the
165: * wizard will not assume that it cannot proceed, but will <b>skip</b> this
166: * component altogether.
167: *
168: * @return <code>true</code> if the component can be executed,
169: * <code>false</code> if the component should be skipped.
170: */
171: public boolean canExecuteForward() {
172: return true;
173: }
174:
175: /**
176: * This method is called by the wizard in order to find out whether this
177: * component can be reached via {@link Wizard#previous()}.
178: *
179: * <p>
180: * It's important to note that if this method returns <code>false</code>, the
181: * wizard will not assume that it cannot proceed, but will <b>skip</b> this
182: * component altogether.
183: *
184: * @return <code>true</code> if the component can be executed,
185: * <code>false</code> if the component should be skipped.
186: */
187: public boolean canExecuteBackward() {
188: return true;
189: }
190:
191: /**
192: * This method is called by the wizard in order to find out whether it is
193: * allowed to execute any components before this via the
194: * {@link Wizard#previous()} method. If this method returns <code>true</code> it
195: * will be illegal to call {@link Wizard#previous()} if the current component
196: * is active.
197: *
198: * @return <code>true</code> is the component is the point of no return,
199: * <code>false</code> otherwise.
200: */
201: public boolean isPointOfNoReturn() {
202: return false;
203: }
204:
205: // ui ///////////////////////////////////////////////////////////////////////////
206: /**
207: * Returns the {@link WizardUi} object for this component.
208: *
209: * @return {@link WizardUi} object for this component.
210: */
211: public abstract WizardUi getWizardUi();
212:
213: // wizard ///////////////////////////////////////////////////////////////////////
214: /**
215: * Returns the {@link Wizard} which currently executes this component.
216: *
217: * @return {@link Wizard} which currently executes this component.
218: */
219: public final Wizard getWizard() {
220: return wizard;
221: }
222:
223: /**
224: * Sets the {@link Wizard} which currently executes this component.
225: *
226: * @param wizard {@link Wizard} which currently executes this component.
227: */
228: public final void setWizard(final Wizard wizard) {
229: this .wizard = wizard;
230: }
231:
232: // children /////////////////////////////////////////////////////////////////////
233: /**
234: * Registers a new child for this component.
235: *
236: * @param component New child component which should be registered.
237: */
238: public final void addChild(final WizardComponent component) {
239: children.add(component);
240: }
241:
242: /**
243: * Unregisters a child component. If it was not previously registered, no action
244: * is taken.
245: *
246: * @param component Child component which should be unregistered.
247: */
248: public final void removeChild(final WizardComponent component) {
249: children.remove(component);
250: }
251:
252: /**
253: * Registers several children for this component.
254: *
255: * @param components {@link List} of child components which should be
256: * registered.
257: */
258: public final void addChildren(final List<WizardComponent> components) {
259: children.addAll(components);
260: }
261:
262: /**
263: * Returns the list of currently registered child components. It is not
264: * guaranteed that operating on this list directly will affect the actual
265: * children.
266: *
267: * @return {@link List} of the registered child components.
268: */
269: public final List<WizardComponent> getChildren() {
270: return children;
271: }
272:
273: // properties ///////////////////////////////////////////////////////////////////
274: /**
275: * Returns the value of the component's property with the specified name. This
276: * method attempts to parse the property value using the
277: * {@link SystemUtils#parseString(String,ClassLoader)} method and supplying
278: * {@link Wizard#getClassLoader()} as the class loader value.
279: *
280: * @param name Name of the property whose value should be returned.
281: * @return Value of the specified property, parsed via
282: * {@link SystemUtils#parseString(String,ClassLoader)}.
283: */
284: public final String getProperty(final String name) {
285: return getProperty(name, true);
286: }
287:
288: /**
289: * Returns the value of the specified property. Thsi method can either attempt
290: * to resolve the value, or return it as is.
291: *
292: * @param name Name of the property whose value needs to be returned.
293: * @param resolve Whether to resolve the property value or not.
294: * @return Value of the specified property, either parsed or not.
295: */
296: public final String getProperty(final String name,
297: final boolean resolve) {
298: final String value = properties.getProperty(name);
299:
300: if (resolve) {
301: return value != null ? resolveString(value) : null;
302: } else {
303: return value;
304: }
305: }
306:
307: /**
308: * Sets the specified property to the specified value. If such property does not
309: * exist - it is created.
310: *
311: * @param name Name of the property whose value needs to be set.
312: * @param value Value of the property.
313: */
314: public final void setProperty(final String name, final String value) {
315: properties.setProperty(name, value);
316: }
317:
318: /**
319: * Returns the properties of this component. The values of the properties will
320: * not be parsed. It is not guaranteed that operating on the return value of
321: * this method will affect the actual properties of the component.
322: *
323: * @return Component's properties.
324: */
325: public final NbiProperties getProperties() {
326: return properties;
327: }
328:
329: // helpers //////////////////////////////////////////////////////////////////////
330: /**
331: * A helper method - calls {@link SystemUtils#resolveString(String,ClassLoader)}
332: * supplying {@link Wizard#getClassLoader()} as the class loader value.
333: *
334: * @param string String to be resolved.
335: * @return Resolved string.
336: */
337: protected final String resolveString(final String string) {
338: return SystemUtils.resolveString(string, wizard
339: .getClassLoader());
340: }
341:
342: /**
343: * A helper method - calls {@link SystemUtils#resolvePath(String,ClassLoader)}
344: * supplying {@link Wizard#getClassLoader()} as the class loader value.
345: *
346: * @param path Path to be resolved as a {@link String}.
347: * @return Resolved path as a {@link File}.
348: */
349: protected final File resolvePath(final String path) {
350: return SystemUtils.resolvePath(path, wizard.getClassLoader());
351: }
352:
353: /**
354: * A helper method - calls
355: * {@link ResourceUtils#getString(String,String,ClassLoader)}, supplying
356: * {@link Wizard#getClassLoader()} as the class loader value.
357: *
358: * @param baseName Resource bundle base name.
359: * @param key Name of the key whose value needs to be obtained.
360: * @return Value of the specified key from the specified bundle.
361: */
362: protected final String getString(final String baseName,
363: final String key) {
364: return ResourceUtils.getString(baseName, key, wizard
365: .getClassLoader());
366: }
367:
368: /**
369: * A helper method - calls
370: * {@link ResourceUtils#getString(String,String,ClassLoader,Object[])},
371: * supplying {@link Wizard#getClassLoader()} as the class loader value.
372: *
373: * @param baseName Resource bundle base name.
374: * @param key Name of the key whose value needs to be obtained.
375: * @param arguments Objects which should be used to substitute wildcards in the
376: * key value.
377: * @return Value of the specified key from the specified bundle with its
378: * wildcards resolved using the supplied arguments.
379: */
380: protected final String getString(final String baseName,
381: final String key, final Object... arguments) {
382: return ResourceUtils.getString(baseName, key, wizard
383: .getClassLoader(), arguments);
384: }
385:
386: /**
387: * A helper method - calls {@link ResourceUtils#getResource(String,ClassLoader)}
388: * supplying {@link Wizard#getClassLoader()} as the class loader value.
389: *
390: * @param path Path to the resource which should be obtained.
391: * @return {@link InputStream} from the resource, or <code>null</code> if it was
392: * not found.
393: */
394: protected final InputStream getResource(final String path) {
395: return ResourceUtils.getResource(path, wizard.getClassLoader());
396: }
397:
398: /////////////////////////////////////////////////////////////////////////////////
399: // Inner Classes
400: /**
401: * Implementation of the {@link WizardUi} for {@link WizardComponent}.
402: *
403: * @author Kirill Sorokin
404: * @since 1.0
405: */
406: public static class WizardComponentUi implements WizardUi {
407: /**
408: * Current {@link WizardComponent} for this UI.
409: */
410: protected WizardComponent component;
411:
412: /**
413: * UI implementation for Swing environment. This is initialized lazily, i.e.
414: * the value of this field will be null, unless the accessor method has been
415: * called - {@link #getSwingUi(SwingContainer)}.
416: */
417: protected WizardComponentSwingUi swingUi;
418:
419: /**
420: * Creates a new instance of {@link WizardComponentUi}, initializing it with
421: * the specified instance of {@link WizardComponent}.
422: *
423: * @param component Instance of {@link WizardComponent} which should be used
424: * by this UI.
425: */
426: protected WizardComponentUi(final WizardComponent component) {
427: this .component = component;
428: }
429:
430: /**
431: * {@inheritDoc}
432: */
433: public SwingUi getSwingUi(final SwingContainer container) {
434: if (swingUi == null) {
435: swingUi = new WizardComponentSwingUi(component,
436: container);
437: }
438:
439: swingUi.initializeContainer();
440: swingUi.initialize();
441:
442: return swingUi;
443: }
444: }
445:
446: /**
447: * Implementation of {@link SwingUi} for {@link WizardComponent}.
448: *
449: * @author Kirill Sorokin
450: * @since 1.0
451: */
452: public static class WizardComponentSwingUi extends SwingUi {
453: /**
454: * Current {@link WizardComponent} for this UI.
455: */
456: protected WizardComponent component;
457:
458: /**
459: * Current {@link SwingContainer} for this UI.
460: */
461: protected SwingContainer container;
462:
463: /**
464: * Creates a new instance of {@link WizardComponentSwingUi}, initializing it
465: * with the specified instances of {@link WizardComponent} and
466: * {@link SwingContainer}.
467: *
468: * @param component Instance of {@link WizardComponent} which should be used
469: * by this UI.
470: * @param container Instance of {@link SwingContainer} which should be used
471: * by this UI.
472: */
473: protected WizardComponentSwingUi(
474: final WizardComponent component,
475: final SwingContainer container) {
476: this .component = component;
477: this .container = container;
478: }
479:
480: /**
481: * {@inheritDoc}
482: */
483: public String getTitle() {
484: return component.getProperty(TITLE_PROPERTY);
485: }
486:
487: /**
488: * {@inheritDoc}
489: */
490: public String getDescription() {
491: return component.getProperty(DESCRIPTION_PROPERTY);
492: }
493:
494: /**
495: * {@inheritDoc}
496: */
497: public void evaluateHelpButtonClick() {
498: // does nothing
499: }
500:
501: /**
502: * {@inheritDoc}
503: */
504: public void evaluateBackButtonClick() {
505: component.getWizard().previous();
506: }
507:
508: /**
509: * {@inheritDoc}
510: */
511: public void evaluateNextButtonClick() {
512: component.getWizard().next();
513: }
514:
515: /**
516: * {@inheritDoc}
517: */
518: public void evaluateCancelButtonClick() {
519: final String cancelDialogTitle = ResourceUtils
520: .getString(WizardComponent.class,
521: RESOURCE_CANCEL_DIALOG_TITLE);
522: final String canceldialogText = ResourceUtils.getString(
523: WizardComponent.class, RESOURCE_CANCEL_DIALOG_TEXT);
524:
525: if (UiUtils.showYesNoDialog(cancelDialogTitle,
526: canceldialogText)) {
527: component.getWizard().getFinishHandler().cancel();
528: }
529: }
530:
531: /**
532: * {@inheritDoc}
533: */
534: public NbiButton getDefaultEnterButton() {
535: return container.getNextButton();
536: }
537:
538: /**
539: * {@inheritDoc}
540: */
541: public NbiButton getDefaultEscapeButton() {
542: return container.getCancelButton();
543: }
544:
545: /**
546: * {@inheritDoc}
547: */
548: public JComponent getDefaultFocusOwner() {
549: if (getDefaultEnterButton() != null) {
550: return getDefaultEnterButton();
551: } else {
552: return null;
553: }
554: }
555:
556: // protected ////////////////////////////////////////////////////////////////
557: /**
558: * Initializes the container. This method sets the appropriate texts on the
559: * buttons, enables/disables them according to the current position in the
560: * wizard, etc.
561: *
562: * <p>
563: * This method is called right before the UI is shown.
564: */
565: protected void initializeContainer() {
566: // set up the help button
567: container.getHelpButton().setVisible(false);
568: container.getHelpButton().setEnabled(false);
569:
570: container.getHelpButton().setText(
571: component.getProperty(HELP_BUTTON_TEXT_PROPERTY));
572:
573: // set up the back button
574: container.getBackButton().setVisible(true);
575: if (component.getWizard().hasPrevious()) {
576: container.getBackButton().setEnabled(true);
577: } else {
578: container.getBackButton().setEnabled(false);
579: }
580:
581: container.getBackButton().setText(
582: component.getProperty(BACK_BUTTON_TEXT_PROPERTY));
583:
584: // set up the next (or finish) button
585: container.getNextButton().setVisible(true);
586: container.getNextButton().setEnabled(true);
587:
588: if (component.getWizard().hasNext()) {
589: container
590: .getNextButton()
591: .setText(
592: component
593: .getProperty(NEXT_BUTTON_TEXT_PROPERTY));
594: } else {
595: container
596: .getNextButton()
597: .setText(
598: component
599: .getProperty(FINISH_BUTTON_TEXT_PROPERTY));
600: }
601:
602: // set up the cancel button
603: container.getCancelButton().setVisible(true);
604: container.getCancelButton().setEnabled(true);
605:
606: container.getCancelButton().setText(
607: component.getProperty(CANCEL_BUTTON_TEXT_PROPERTY));
608: }
609:
610: /**
611: * Initializes the UI. This methods sets the correct texts for the labels,
612: * textfields, and initializes other controls.
613: *
614: * <p>
615: * This method is called right before the UI is shown.
616: */
617: protected void initialize() {
618: // does nothing
619: }
620: }
621:
622: /////////////////////////////////////////////////////////////////////////////////
623: // Constants
624: /**
625: * Name of the property, which contains the component's title.
626: */
627: public static final String TITLE_PROPERTY = "title"; // NOI18N
628:
629: /**
630: * Name of the property, which contains the component's description.
631: */
632: public static final String DESCRIPTION_PROPERTY = "description"; // NOI18N
633:
634: /**
635: * Name of the property, which contains the text for the standard 'Help'
636: * button.
637: */
638: public static final String HELP_BUTTON_TEXT_PROPERTY = "help.button.text"; // NOI18N
639:
640: /**
641: * Name of the property, which contains the text for the standard 'Back'
642: * button.
643: */
644: public static final String BACK_BUTTON_TEXT_PROPERTY = "back.button.text"; // NOI18N
645:
646: /**
647: * Name of the property, which contains the text for the standard 'Next'
648: * button.
649: */
650: public static final String NEXT_BUTTON_TEXT_PROPERTY = "next.button.text"; // NOI18N
651:
652: /**
653: * Name of the property, which contains the text for the standard 'Cancel'
654: * button.
655: */
656: public static final String CANCEL_BUTTON_TEXT_PROPERTY = "cancel.button.text"; // NOI18N
657:
658: /**
659: * Name of the property, which contains the text for the standard 'Finish'
660: * button.
661: */
662: public static final String FINISH_BUTTON_TEXT_PROPERTY = "finish.button.text"; // NOI18N
663:
664: // private //////////////////////////////////////////////////////////////////////
665: /**
666: * Default value for the component's title.
667: */
668: private static final String DEFAULT_TITLE = ResourceUtils
669: .getString(WizardComponent.class, "WC.title"); // NOI18N
670:
671: /**
672: * Default value for the component's description.
673: */
674: private static final String DEFAULT_DESCRIPTION = ResourceUtils
675: .getString(WizardComponent.class, "WC.description"); // NOI18N
676:
677: /**
678: * Default text for the standard 'Help' button.
679: */
680: private static final String DEFAULT_HELP_BUTTON_TEXT = ResourceUtils
681: .getString(WizardComponent.class, "WC.help.button.text"); // NOI18N
682:
683: /**
684: * Default text for the standard 'Back' button.
685: */
686: private static final String DEFAULT_BACK_BUTTON_TEXT = ResourceUtils
687: .getString(WizardComponent.class, "WC.back.button.text"); // NOI18N
688:
689: /**
690: * Default text for the standard 'Next' button.
691: */
692: private static final String DEFAULT_NEXT_BUTTON_TEXT = ResourceUtils
693: .getString(WizardComponent.class, "WC.next.button.text"); // NOI18N
694:
695: /**
696: * Default text for the standard 'Cancel' button.
697: */
698: private static final String DEFAULT_CANCEL_BUTTON_TEXT = ResourceUtils
699: .getString(WizardComponent.class, "WC.cancel.button.text"); // NOI18N
700:
701: /**
702: * Default text for the standard 'Finish' button.
703: */
704: private static final String DEFAULT_FINISH_BUTTON_TEXT = ResourceUtils
705: .getString(WizardComponent.class, "WC.finish.button.text"); // NOI18N
706:
707: /**
708: * Name of a resource bundle entry.
709: */
710: public static final String RESOURCE_CANCEL_DIALOG_TITLE = "WC.cancel.dialog.title"; // NOI18N
711:
712: /**
713: * Name of a resource bundle entry.
714: */
715: public static final String RESOURCE_CANCEL_DIALOG_TEXT = "WC.cancel.dialog.text"; // NOI18N
716: }
|