001/**
002 *
003 * Copyright 2005-2007 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.commands;
018
019import java.util.List;
020
021import org.jivesoftware.smack.SmackException.NoResponseException;
022import org.jivesoftware.smack.SmackException.NotConnectedException;
023import org.jivesoftware.smack.XMPPException.XMPPErrorException;
024import org.jivesoftware.smack.packet.XMPPError;
025
026import org.jivesoftware.smackx.commands.packet.AdHocCommandData;
027import org.jivesoftware.smackx.xdata.Form;
028
029import org.jxmpp.jid.Jid;
030
031/**
032 * An ad-hoc command is responsible for executing the provided service and
033 * storing the result of the execution. Each new request will create a new
034 * instance of the command, allowing information related to executions to be
035 * stored in it. For example suppose that a command that retrieves the list of
036 * users on a server is implemented. When the command is executed it gets that
037 * list and the result is stored as a form in the command instance, i.e. the
038 * <code>getForm</code> method retrieves a form with all the users.
039 * <p>
040 * Each command has a <tt>node</tt> that should be unique within a given JID.
041 * <p>
042 * Commands may have zero or more stages. Each stage is usually used for
043 * gathering information required for the command execution. Users are able to
044 * move forward or backward across the different stages. Commands may not be
045 * cancelled while they are being executed. However, users may request the
046 * "cancel" action when submitting a stage response indicating that the command
047 * execution should be aborted. Thus, releasing any collected information.
048 * Commands that require user interaction (i.e. have more than one stage) will
049 * have to provide the data forms the user must complete in each stage and the
050 * allowed actions the user might perform during each stage (e.g. go to the
051 * previous stage or go to the next stage).
052 * <p>
053 * All the actions may throw an XMPPException if there is a problem executing
054 * them. The <code>XMPPError</code> of that exception may have some specific
055 * information about the problem. The possible extensions are:
056 * 
057 * <li><i>malformed-action</i>. Extension of a <i>bad-request</i> error.</li>
058 * <li><i>bad-action</i>. Extension of a <i>bad-request</i> error.</li>
059 * <li><i>bad-locale</i>. Extension of a <i>bad-request</i> error.</li>
060 * <li><i>bad-payload</i>. Extension of a <i>bad-request</i> error.</li>
061 * <li><i>bad-sessionid</i>. Extension of a <i>bad-request</i> error.</li>
062 * <li><i>session-expired</i>. Extension of a <i>not-allowed</i> error.</li>
063 * <p>
064 * See the <code>SpecificErrorCondition</code> class for detailed description
065 * of each one.
066 * <p>
067 * Use the <code>getSpecificErrorConditionFrom</code> to obtain the specific
068 * information from an <code>XMPPError</code>.
069 * 
070 * @author Gabriel Guardincerri
071 * 
072 */
073public abstract class AdHocCommand {
074    // TODO: Analyze the redesign of command by having an ExecutionResponse as a
075    // TODO: result to the execution of every action. That result should have all the
076    // TODO: information related to the execution, e.g. the form to fill. Maybe this
077    // TODO: design is more intuitive and simpler than the current one that has all in
078    // TODO: one class.
079
080    private AdHocCommandData data;
081
082    public AdHocCommand() {
083        super();
084        data = new AdHocCommandData();
085    }
086
087    /**
088     * Returns the specific condition of the <code>error</code> or <tt>null</tt> if the
089     * error doesn't have any.
090     * 
091     * @param error the error the get the specific condition from.
092     * @return the specific condition of this error, or null if it doesn't have
093     *         any.
094     */
095    public static SpecificErrorCondition getSpecificErrorCondition(XMPPError error) {
096        // This method is implemented to provide an easy way of getting a packet
097        // extension of the XMPPError.
098        for (SpecificErrorCondition condition : SpecificErrorCondition.values()) {
099            if (error.getExtension(condition.toString(),
100                    AdHocCommandData.SpecificError.namespace) != null) {
101                return condition;
102            }
103        }
104        return null;
105    }
106
107    /**
108     * Set the the human readable name of the command, usually used for
109     * displaying in a UI.
110     * 
111     * @param name the name.
112     */
113    public void setName(String name) {
114        data.setName(name);
115    }
116
117    /**
118     * Returns the human readable name of the command.
119     * 
120     * @return the human readable name of the command
121     */
122    public String getName() {
123        return data.getName();
124    }
125
126    /**
127     * Sets the unique identifier of the command. This value must be unique for
128     * the <code>OwnerJID</code>.
129     * 
130     * @param node the unique identifier of the command.
131     */
132    public void setNode(String node) {
133        data.setNode(node);
134    }
135
136    /**
137     * Returns the unique identifier of the command. It is unique for the
138     * <code>OwnerJID</code>.
139     * 
140     * @return the unique identifier of the command.
141     */
142    public String getNode() {
143        return data.getNode();
144    }
145
146    /**
147     * Returns the full JID of the owner of this command. This JID is the "to" of a
148     * execution request.
149     * 
150     * @return the owner JID.
151     */
152    public abstract Jid getOwnerJID();
153
154    /**
155     * Returns the notes that the command has at the current stage.
156     * 
157     * @return a list of notes.
158     */
159    public List<AdHocCommandNote> getNotes() {
160        return data.getNotes();
161    }
162
163    /**
164     * Adds a note to the current stage. This should be used when setting a
165     * response to the execution of an action. All the notes added here are
166     * returned by the {@link #getNotes} method during the current stage.
167     * Once the stage changes all the notes are discarded.
168     * 
169     * @param note the note.
170     */
171    protected void addNote(AdHocCommandNote note) {
172        data.addNote(note);
173    }
174
175    public String getRaw() {
176        return data.getChildElementXML().toString();
177    }
178
179    /**
180     * Returns the form of the current stage. Usually it is the form that must
181     * be answered to execute the next action. If that is the case it should be
182     * used by the requester to fill all the information that the executor needs
183     * to continue to the next stage. It can also be the result of the
184     * execution.
185     * 
186     * @return the form of the current stage to fill out or the result of the
187     *         execution.
188     */
189    public Form getForm() {
190        if (data.getForm() == null) {
191            return null;
192        }
193        else {
194            return new Form(data.getForm());
195        }
196    }
197
198    /**
199     * Sets the form of the current stage. This should be used when setting a
200     * response. It could be a form to fill out the information needed to go to
201     * the next stage or the result of an execution.
202     * 
203     * @param form the form of the current stage to fill out or the result of the
204     *      execution.
205     */
206    protected void setForm(Form form) {
207        data.setForm(form.getDataFormToSend());
208    }
209
210    /**
211     * Executes the command. This is invoked only on the first stage of the
212     * command. It is invoked on every command. If there is a problem executing
213     * the command it throws an XMPPException.
214     * 
215     * @throws XMPPErrorException if there is an error executing the command.
216     * @throws NotConnectedException 
217     * @throws InterruptedException 
218     */
219    public abstract void execute() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
220
221    /**
222     * Executes the next action of the command with the information provided in
223     * the <code>response</code>. This form must be the answer form of the
224     * previous stage. This method will be only invoked for commands that have one
225     * or more stages. If there is a problem executing the command it throws an
226     * XMPPException.
227     * 
228     * @param response the form answer of the previous stage.
229     * @throws XMPPErrorException if there is a problem executing the command.
230     * @throws NotConnectedException 
231     * @throws InterruptedException 
232     */
233    public abstract void next(Form response) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
234
235    /**
236     * Completes the command execution with the information provided in the
237     * <code>response</code>. This form must be the answer form of the
238     * previous stage. This method will be only invoked for commands that have one
239     * or more stages. If there is a problem executing the command it throws an
240     * XMPPException.
241     * 
242     * @param response the form answer of the previous stage.
243     * @throws XMPPErrorException if there is a problem executing the command.
244     * @throws NotConnectedException 
245     * @throws InterruptedException 
246     */
247    public abstract void complete(Form response) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
248
249    /**
250     * Goes to the previous stage. The requester is asking to re-send the
251     * information of the previous stage. The command must change it state to
252     * the previous one. If there is a problem executing the command it throws
253     * an XMPPException.
254     * 
255     * @throws XMPPErrorException if there is a problem executing the command.
256     * @throws NotConnectedException 
257     * @throws InterruptedException 
258     */
259    public abstract void prev() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
260
261    /**
262     * Cancels the execution of the command. This can be invoked on any stage of
263     * the execution. If there is a problem executing the command it throws an
264     * XMPPException.
265     * 
266     * @throws XMPPErrorException if there is a problem executing the command.
267     * @throws NotConnectedException 
268     * @throws InterruptedException 
269     */
270    public abstract void cancel() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
271
272    /**
273     * Returns a collection with the allowed actions based on the current stage.
274     * Possible actions are: {@link Action#prev prev}, {@link Action#next next} and
275     * {@link Action#complete complete}. This method will be only invoked for commands that
276     * have one or more stages.
277     * 
278     * @return a collection with the allowed actions based on the current stage
279     *      as defined in the SessionData.
280     */
281    protected List<Action> getActions() {
282        return data.getActions();
283    }
284
285    /**
286     * Add an action to the current stage available actions. This should be used
287     * when creating a response.
288     * 
289     * @param action the action.
290     */
291    protected void addActionAvailable(Action action) {
292        data.addAction(action);
293    }
294
295    /**
296     * Returns the action available for the current stage which is
297     * considered the equivalent to "execute". When the requester sends his
298     * reply, if no action was defined in the command then the action will be
299     * assumed "execute" thus assuming the action returned by this method. This
300     * method will never be invoked for commands that have no stages.
301     * 
302     * @return the action available for the current stage which is considered
303     *      the equivalent to "execute".
304     */
305    protected Action getExecuteAction() {
306        return data.getExecuteAction();
307    }
308
309    /**
310     * Sets which of the actions available for the current stage is
311     * considered the equivalent to "execute". This should be used when setting
312     * a response. When the requester sends his reply, if no action was defined
313     * in the command then the action will be assumed "execute" thus assuming
314     * the action returned by this method.
315     * 
316     * @param action the action.
317     */
318    protected void setExecuteAction(Action action) {
319        data.setExecuteAction(action);
320    }
321
322    /**
323     * Returns the status of the current stage.
324     * 
325     * @return the current status.
326     */
327    public Status getStatus() {
328        return data.getStatus();
329    }
330
331    /**
332     * Check if this command has been completed successfully.
333     * 
334     * @return <code>true</code> if this command is completed.
335     * @since 4.2
336     */
337    public boolean isCompleted() {
338        return getStatus() == Status.completed;
339    }
340
341    /**
342     * Sets the data of the current stage. This should not used.
343     * 
344     * @param data the data.
345     */
346    void setData(AdHocCommandData data) {
347        this.data = data;
348    }
349
350    /**
351     * Gets the data of the current stage. This should not used.
352     *
353     * @return the data.
354     */
355    AdHocCommandData getData() {
356        return data;
357    }
358
359    /**
360     * Returns true if the <code>action</code> is available in the current stage.
361     * The {@link Action#cancel cancel} action is always allowed. To define the
362     * available actions use the <code>addActionAvailable</code> method.
363     * 
364     * @param action
365     *            The action to check if it is available.
366     * @return True if the action is available for the current stage.
367     */
368    protected boolean isValidAction(Action action) {
369        return getActions().contains(action) || Action.cancel.equals(action);
370    }
371
372    /**
373     * The status of the stage in the adhoc command.
374     */
375    public enum Status {
376
377        /**
378         * The command is being executed.
379         */
380        executing,
381
382        /**
383         * The command has completed. The command session has ended.
384         */
385        completed,
386
387        /**
388         * The command has been canceled. The command session has ended.
389         */
390        canceled
391    }
392
393    public enum Action {
394
395        /**
396         * The command should be executed or continue to be executed. This is
397         * the default value.
398         */
399        execute,
400
401        /**
402         * The command should be canceled.
403         */
404        cancel,
405
406        /**
407         * The command should be digress to the previous stage of execution.
408         */
409        prev,
410
411        /**
412         * The command should progress to the next stage of execution.
413         */
414        next,
415
416        /**
417         * The command should be completed (if possible).
418         */
419        complete,
420
421        /**
422         * The action is unknow. This is used when a recieved message has an
423         * unknown action. It must not be used to send an execution request.
424         */
425        unknown
426    }
427
428    public enum SpecificErrorCondition {
429
430        /**
431         * The responding JID cannot accept the specified action.
432         */
433        badAction("bad-action"),
434
435        /**
436         * The responding JID does not understand the specified action.
437         */
438        malformedAction("malformed-action"),
439
440        /**
441         * The responding JID cannot accept the specified language/locale.
442         */
443        badLocale("bad-locale"),
444
445        /**
446         * The responding JID cannot accept the specified payload (e.g. the data
447         * form did not provide one or more required fields).
448         */
449        badPayload("bad-payload"),
450
451        /**
452         * The responding JID cannot accept the specified sessionid.
453         */
454        badSessionid("bad-sessionid"),
455
456        /**
457         * The requesting JID specified a sessionid that is no longer active
458         * (either because it was completed, canceled, or timed out).
459         */
460        sessionExpired("session-expired");
461
462        private final String value;
463
464        SpecificErrorCondition(String value) {
465            this.value = value;
466        }
467
468        @Override
469        public String toString() {
470            return value;
471        }
472    }
473}