001    /*
002    
003      Copyright (C) 2001 Renaud Pawlak, Laurent Martelli
004      
005      This program is free software; you can redistribute it and/or modify
006      it under the terms of the GNU Lesser General Public License as
007      published by the Free Software Foundation; either version 2 of the
008      License, or (at your option) any later version.
009    
010      This program is distributed in the hope that it will be useful, but
011      WITHOUT ANY WARRANTY; without even the implied warranty of
012      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013      Lesser General Public License for more details.
014    
015      You should have received a copy of the GNU Lesser General Public
016      License along with this program; if not, write to the Free Software
017      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
018      USA */
019    
020    package org.objectweb.jac.aspects.gui;
021    
022    import org.objectweb.jac.core.Display;
023    import org.objectweb.jac.core.rtti.AbstractMethodItem;
024    import org.objectweb.jac.core.rtti.ClassRepository;
025    import org.objectweb.jac.util.Stack;
026    
027    /**
028     * This class allows a GUI programmer to create input sequences to ask
029     * the user to fill a set of parameters when invoking a method.
030     *
031     * <p>By default, when invoking a method through the GUI, an
032     * <code>InputWrapper</code> opens a dialog to fill the parameters
033     * values when needed. If an input sequence is attached to this method
034     * (see <code>GuiAC.setInputSequence</code>), then the input wrappers
035     * will ask for the parameters using several input dialogs, each one
036     * corresponding to a step of the input sequence.
037     *
038     * <p>Defining a new input sequence is done by concretizing this
039     * class. For instance, the following class defines a sequence with
040     * two steps that open input dialogs from some prototypes defined in
041     * the class. The first steps asks for a boolean value that will
042     * dinamically determines the second step input.
043     *
044     * <pre>
045     * public class MyInputSequence extends InputSequence {
046     *    public MyInputSequence( Display display, 
047     *                            AbstractMethodItem method, 
048     *                            Object[] parameters ) {
049     *       super(display,method,parameters);
050     *    }
051     *    public int getNbSteps() {
052     *       return 2;
053     *    }
054     *    public void init() {}
055     *    public void validate() {
056     *       Object[] values = getStepValues(2);
057     *       setParameterValue(0, values[0]);
058     *       Boolean firstStepResult = getStepValues(1)[0];
059     *       if ( firstStepResult.booleanValue() ) {
060     *          setParameterValue(1, values[1]);
061     *       } else {
062     *          setParameterValue(1, null);
063     *       }
064     *    }
065     *    public AbstractMethodItem getStepMethod( int step ) {
066     *       if( step == 1 ) {
067     *          return getLocalInputMethod( "myPrototype1" );
068     *       } else if ( step == 2 ) {
069     *          Object[] values = getStepValues(1);
070     *          Boolean firstStepResult = values[0];
071     *          if ( firstStepResult.booleanValue() ) {
072     *             return getLocalInputMethod( "myPrototype2" );
073     *          } else {
074     *             return getLocalInputMethod( "myPrototype3" );
075     *       } else {
076     *          return null; 
077     *       }
078     *    }
079     *    public void myPrototype1( boolean b ) {}
080     *    public void myPrototype2( String s ) {}
081     *    public void myPrototype3( String s1, String s2 ) {}
082     * }
083     * </pre>
084     *
085     * @see InputWrapper
086     *
087     * @author <a href="mailto:pawlak@cnam.fr">Renaud Pawlak</a> */
088    
089    public abstract class InputSequence {
090    
091       AbstractMethodItem method;
092       Object[] parameters;
093       Display display;
094    
095       Stack stepParameters = new Stack();
096       int currentStep = 0;
097    
098       /**
099        * Creates a user-defined input sequence.
100        *
101        * @param display the display to use to show the input boxes
102        * @param method the method that will be finally invoked at the end
103        * of the input sequence
104        * @param parameters the array that contains the parameters of the
105        * invoked method */
106    
107       public InputSequence( Display display, AbstractMethodItem method, 
108                             Object[] parameters ) {
109          this.display = display;
110          this.method = method;
111          this.parameters = parameters;
112       }
113    
114       /**
115        * This method is called when a new invocation is performed on the
116        * method.
117        *
118        * <p>Define it if some objects must be dynamically constructed to
119        * handle the sequence. */
120    
121       public abstract void init();
122    
123       /**
124        * This method is called when the input sequence is finished and
125        * when the user validates the last step input.
126        *
127        * <p>Define this method to fill the method parameters from the
128        * values found in all the performed steps.
129        *
130        * @return true is the input is valid (false cancels the
131        * invocation)
132        * @see #getStepValues(int)
133        * @see #setParameterValue(int,Object) */
134    
135       public abstract boolean validate();
136    
137       /**
138        * Define this method to return the number of steps (can
139        * dynamically change regarding the inputted vaules of the steps).
140        *
141        * @return the number of steps of the input sequence */
142    
143       public abstract int getNbSteps();
144    
145       /**
146        * Returns the current step (indexed from 1).
147        *
148        * @return the current step */
149    
150       public final int getCurrentStep() {
151          return currentStep;
152       }
153    
154       /**
155        * Tells if the sequence has a next step to perform after the
156        * current one.
157        *
158        * @return true if a next step to perform */
159    
160       public final boolean hasNextStep() {
161          return currentStep < getNbSteps();
162       }
163    
164       /**
165        * Returns the method that is used to define the input box for a
166        * given step.
167        *
168        * <p>This is the most important method since it defines the shape
169        * of an input box step. You should define a set of local method
170        * with the right prototype and get their correponding method item
171        * with the <code>getLocalInputMethod</code> method.
172        *
173        * <p>Note that the method is not actually called but is only used
174        * through the <code>Display.showIntput</code> method.
175        * 
176        * @param step the step (indexed from 1)
177        * @return the method that is used to create the input
178        * @see org.objectweb.jac.core.Display#showInput(Object,AbstractMethodItem,Object[])
179        * @see #getLocalInputMethod(String) */
180    
181       public abstract AbstractMethodItem getStepMethod( int step );      
182    
183       /**
184        * Call this method on a new input sequence to process the inputs.
185        *
186        * @return false if some error happened or if an input step was
187        * cancelled by the user */
188    
189       public final boolean proceedInputs() {
190          init();
191          while( hasNextStep() ) {
192             if ( ! nextStep() ) return false;
193          }
194          return validate();
195       }
196          
197       /**
198        * Process the next step.
199        * @return true if ok */
200    
201       public final boolean nextStep() {
202          currentStep++;
203          AbstractMethodItem stepMethod = getStepMethod( currentStep );
204          Object[] params = null;
205          stepParameters.push( params = new Object[stepMethod.getParameterTypes().length] );
206          return display.showInput( null, stepMethod, params );
207       }
208    
209       /**
210        * Process the previous step back.
211        * @return true if ok */
212    
213       public boolean previousStep() {
214          stepParameters.pop();
215          currentStep--;
216          AbstractMethodItem stepMethod = getStepMethod( currentStep );
217          return display.showInput( null, stepMethod, (Object[])stepParameters.peek() );
218       }
219    
220       /**
221        * Returns the method item that corresponds to a method defined in
222        * the user-defined input sequence.
223        *
224        * @param name the method name 
225        * @return the corresponding method item */
226    
227       protected AbstractMethodItem getLocalInputMethod( String name ) {
228          return ClassRepository.get().getClass( this.getClass() ).getMethod( name );
229       }
230    
231       /**
232        * Returns the values that were entered by the user for a given
233        * step.
234        *
235        * @param step the step number (indexed from 1) 
236        * @return the user-inputted values */
237    
238       protected Object[] getStepValues( int step ) {
239          if( currentStep < step ) {
240             throw new RuntimeException("Step "+step+" result is not available yet");
241          }
242          return (Object[])stepParameters.get(step-1);
243       }
244    
245       /**
246        * Sets the parameter value for the final call of the method that
247        * will be invoked at the end of the sequence.
248        *
249        * @param i the parameter index
250        * @param value the value */
251       
252       protected void setParameterValue( int i, Object value ) {
253          parameters[i] = value;
254       }
255    }
256    
257